mirror of https://github.com/logseq/logseq
Merge remote-tracking branch 'upstream/master' into whiteboards
commit
d2c3b88865
|
@ -7,10 +7,14 @@ on:
|
|||
workflow_dispatch:
|
||||
inputs:
|
||||
build-target:
|
||||
description: 'Build Target ("nightly"/"beta"/"non-release")'
|
||||
type: string
|
||||
description: 'Build Target (Release Type)'
|
||||
type: choice
|
||||
required: true
|
||||
default: "beta"
|
||||
options:
|
||||
- beta
|
||||
- nightly
|
||||
- non-release
|
||||
default: "non-release"
|
||||
git-ref:
|
||||
description: "Build from Git Ref(master)"
|
||||
required: true
|
||||
|
|
|
@ -9,8 +9,15 @@ import android.os.Build;
|
|||
import android.os.Environment;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.MediaStore;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
// The following refs
|
||||
// https://stackoverflow.com/questions/29713587/how-to-get-the-real-path-with-action-open-document-tree-intent
|
||||
// https://gist.github.com/asifmujteba/d89ba9074bc941de1eaa#file-asfurihelper
|
||||
// with bug fixes and patches.
|
||||
public class FileUtil {
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
public static String getPath(final Context context, final Uri uri) {
|
||||
|
@ -24,24 +31,64 @@ public class FileUtil {
|
|||
final String docId = DocumentsContract.getDocumentId(uri);
|
||||
final String[] split = docId.split(":");
|
||||
final String type = split[0];
|
||||
|
||||
if ("primary".equalsIgnoreCase(type)) {
|
||||
return Environment.getExternalStorageDirectory() + "/" + split[1];
|
||||
// NOTE: It's not a good idea to use storage root as Graph root.
|
||||
String remain = "";
|
||||
if (split.length == 2) {
|
||||
remain = split[1];
|
||||
}
|
||||
|
||||
// TODO handle non-primary volumes
|
||||
}
|
||||
// DownloadsProvider
|
||||
else if (isDownloadsDocument(uri)) {
|
||||
if ("primary".equalsIgnoreCase(type)) {
|
||||
return Environment.getExternalStorageDirectory() + "/" + remain;
|
||||
} else if ("home".equalsIgnoreCase(type)) {
|
||||
return Environment.getExternalStorageDirectory() + "/Documents/" + remain;
|
||||
} else if ("downloads".equalsIgnoreCase(type)) {
|
||||
return Environment.getExternalStorageDirectory() + "/Download/" + remain; // No 's' here
|
||||
}
|
||||
|
||||
File dir = null;
|
||||
File[] mdirs = context.getExternalMediaDirs();
|
||||
for (File mdir : mdirs) {
|
||||
String extPath = mdir.getAbsolutePath();
|
||||
|
||||
if (extPath.contains("/" + type + "/")) {
|
||||
dir = new File(extPath.substring(0, extPath.indexOf("/Android")) + "/" + remain);
|
||||
if (dir.exists()) {
|
||||
return dir.getAbsolutePath();
|
||||
}
|
||||
}
|
||||
}
|
||||
// FIXME: The following attempt cannot handle same directory name on different devices!
|
||||
// attempt 1
|
||||
dir = new File("/storage/" + type + "/" + remain);
|
||||
if (dir.exists()) {
|
||||
return dir.getAbsolutePath();
|
||||
}
|
||||
// attempt 3
|
||||
dir = new File("/mnt/media_rw/" + type + "/" + remain);
|
||||
if (dir.exists()) {
|
||||
return dir.getAbsolutePath();
|
||||
}
|
||||
// attempt 3
|
||||
dir = new File("/mnt/" + type + "/" + remain);
|
||||
if (dir.exists()) {
|
||||
return dir.getAbsolutePath();
|
||||
}
|
||||
|
||||
// TODO: other cases
|
||||
} else if (isDownloadsDocument(uri)) {
|
||||
final String id = DocumentsContract.getDocumentId(uri);
|
||||
final Uri contentUri = ContentUris.withAppendedId(
|
||||
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
|
||||
|
||||
return getDataColumn(context, contentUri, null, null);
|
||||
}
|
||||
// MediaProvider
|
||||
else if (isMediaDocument(uri)) {
|
||||
if (!TextUtils.isEmpty(id)) {
|
||||
if (id.startsWith("raw:")) {
|
||||
return id.replaceFirst("raw:", "");
|
||||
}
|
||||
try {
|
||||
final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
|
||||
return getDataColumn(context, contentUri, null, null);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} else if (isMediaDocument(uri)) {
|
||||
final String docId = DocumentsContract.getDocumentId(uri);
|
||||
final String[] split = docId.split(":");
|
||||
final String type = split[0];
|
||||
|
@ -56,24 +103,49 @@ public class FileUtil {
|
|||
}
|
||||
|
||||
final String selection = "_id=?";
|
||||
final String[] selectionArgs = new String[] {
|
||||
final String[] selectionArgs = new String[]{
|
||||
split[1]
|
||||
};
|
||||
|
||||
return getDataColumn(context, contentUri, selection, selectionArgs);
|
||||
}
|
||||
}
|
||||
// MediaStore (and general)
|
||||
else if ("content".equalsIgnoreCase(uri.getScheme())) {
|
||||
} else if (isTermuxDocument(uri)) {
|
||||
String docId = DocumentsContract.getDocumentId(uri);
|
||||
|
||||
// Ref: https://github.com/termux/termux-app/blob/master/app/src/main/java/com/termux/app/TermuxInstaller.java
|
||||
if (docId.startsWith("/")) {
|
||||
if (docId.contains("/com.termux/files/home/storage/")) {
|
||||
String remain = docId.replaceFirst("^.*?com\\.termux/files/home/storage/[^/]+/", "");
|
||||
if (docId.contains("/storage/external-1")) { // TODO: Support external-2 or more
|
||||
File[] dirs = context.getExternalFilesDirs(remain);
|
||||
if (dirs != null && dirs.length >= 2) {
|
||||
docId = dirs[1].getAbsolutePath();
|
||||
}
|
||||
} else if (docId.contains("/storage/media-1")) {
|
||||
File[] dirs = context.getExternalMediaDirs();
|
||||
if (dirs != null && dirs.length >= 2) {
|
||||
docId = dirs[1].getAbsolutePath() + "/" + remain;
|
||||
}
|
||||
} else if (docId.contains("/storage/downloads")) {
|
||||
docId = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/" + remain;
|
||||
} else if (docId.contains("/storage/shared")) {
|
||||
docId = Environment.getExternalStorageDirectory() + "/" + remain;
|
||||
}
|
||||
}
|
||||
File dir = new File(docId);
|
||||
if (dir.exists()) {
|
||||
return dir.getAbsolutePath();
|
||||
}
|
||||
Log.e("Logseq/FileUtil", "Handle termux content url failed: " + docId);
|
||||
}
|
||||
// FIXME: Are there any other cases?
|
||||
}
|
||||
} else if ("content".equalsIgnoreCase(uri.getScheme())) {
|
||||
// Return the remote address
|
||||
if (isGooglePhotosUri(uri))
|
||||
return uri.getLastPathSegment();
|
||||
|
||||
return getDataColumn(context, uri, null, null);
|
||||
}
|
||||
// File
|
||||
else if ("file".equalsIgnoreCase(uri.getScheme())) {
|
||||
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
|
||||
return uri.getPath();
|
||||
}
|
||||
|
||||
|
@ -135,4 +207,8 @@ public class FileUtil {
|
|||
public static boolean isGooglePhotosUri(Uri uri) {
|
||||
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
public static boolean isTermuxDocument(Uri uri) {
|
||||
return "com.termux.documents".equals(uri.getAuthority());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.os.Environment;
|
|||
import android.os.Build;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.activity.result.ActivityResult;
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
|
@ -56,7 +57,14 @@ public class FolderPicker extends Plugin {
|
|||
Uri treeUri = result.getData().getData();
|
||||
Uri docUri = DocumentsContract.buildDocumentUriUsingTree(treeUri,
|
||||
DocumentsContract.getTreeDocumentId(treeUri));
|
||||
ret.put("path", FileUtil.getPath(context, docUri));
|
||||
call.resolve(ret);
|
||||
Log.i("Logseq/FolderPicker", "Got uri " + docUri);
|
||||
String path = FileUtil.getPath(context, docUri);
|
||||
Log.i("Logseq/FolderPicker", "Convert to path " + FileUtil.getPath(context, docUri));
|
||||
if (path == null || path.isEmpty()) {
|
||||
call.reject("Cannot support this directory type: " + docUri);
|
||||
} else {
|
||||
ret.put("path", path);
|
||||
call.resolve(ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1533,16 +1533,8 @@
|
|||
(util/stop e))
|
||||
(route-handler/redirect-to-page! uuid)))
|
||||
|
||||
(defn- toggle-block-children
|
||||
[_e children]
|
||||
(let [block-ids (map :block/uuid children)]
|
||||
(dorun
|
||||
(if (some editor-handler/collapsable? block-ids)
|
||||
(map editor-handler/collapse-block! block-ids)
|
||||
(map editor-handler/expand-block! block-ids)))))
|
||||
|
||||
(rum/defc block-children < rum/reactive
|
||||
[config children collapsed?]
|
||||
[config block children collapsed?]
|
||||
(let [ref? (:ref? config)
|
||||
query? (:custom-query? config)
|
||||
children (when (coll? children)
|
||||
|
@ -1552,7 +1544,9 @@
|
|||
(not collapsed?))
|
||||
(let [doc-mode? (state/sub :document/mode?)]
|
||||
[:div.block-children-container.flex {:style {:margin-left (if doc-mode? 18 29)}}
|
||||
[:div.block-children-left-border {:on-click (fn [event] (toggle-block-children event children))}]
|
||||
[:div.block-children-left-border
|
||||
{:on-click (fn [_]
|
||||
(editor-handler/toggle-open-block-children! (:block/uuid block)))}]
|
||||
[:div.block-children.w-full {:style {:display (if collapsed? "none" "")}}
|
||||
(for [child children]
|
||||
(when (map? child)
|
||||
|
@ -2469,7 +2463,7 @@
|
|||
*navigating-block (get state ::navigating-block)
|
||||
navigating-block (rum/react *navigating-block)
|
||||
navigated? (and (not= (:block/uuid block) navigating-block) navigating-block)
|
||||
block (if navigated?
|
||||
block (if (or navigated? custom-query?)
|
||||
(let [block (db/pull [:block/uuid navigating-block])
|
||||
blocks (db/get-paginated-blocks repo (:db/id block)
|
||||
{:scoped-block-id (:db/id block)})
|
||||
|
@ -2574,7 +2568,7 @@
|
|||
(when @*show-right-menu?
|
||||
(block-right-menu config block edit?))]
|
||||
|
||||
(block-children config children collapsed?)
|
||||
(block-children config block children collapsed?)
|
||||
|
||||
(dnd-separator-wrapper block block-id slide? false false)]))
|
||||
|
||||
|
@ -2590,7 +2584,7 @@
|
|||
|
||||
(or (:ref? config) (:custom-query? config))
|
||||
(state/set-collapsed-block! block-id
|
||||
(editor-handler/block-default-collapsed? block config))
|
||||
(boolean (editor-handler/block-default-collapsed? block config)))
|
||||
|
||||
:else
|
||||
nil)
|
||||
|
|
|
@ -261,7 +261,7 @@
|
|||
(ui/menu-link
|
||||
{:key "Collapse all"
|
||||
:on-click (fn [_e]
|
||||
(editor-handler/collapse-all! block-id))}
|
||||
(editor-handler/collapse-all! block-id {}))}
|
||||
"Collapse all")
|
||||
|
||||
(when (state/sub [:plugin/simple-commands])
|
||||
|
|
|
@ -307,9 +307,11 @@
|
|||
result)))
|
||||
(open-dir [_this _ok-handler]
|
||||
(p/let [_ (when (= (mobile-util/platform) "android") (check-permission-android))
|
||||
{:keys [path localDocumentsPath]} (p/chain
|
||||
(.pickFolder mobile-util/folder-picker)
|
||||
#(js->clj % :keywordize-keys true))
|
||||
{:keys [path localDocumentsPath]} (-> (.pickFolder mobile-util/folder-picker)
|
||||
(p/then #(js->clj % :keywordize-keys true))
|
||||
(p/catch (fn [e]
|
||||
(js/alert (str e))
|
||||
nil))) ;; NOTE: Can not pick folder, let it crash
|
||||
_ (when (and (mobile-util/native-ios?)
|
||||
(not (or (local-container-path? path localDocumentsPath)
|
||||
(mobile-util/iCloud-container-path? path))))
|
||||
|
|
|
@ -13,7 +13,9 @@
|
|||
[frontend.state :as state]
|
||||
[frontend.util :as util]
|
||||
[goog.dom :as gdom]
|
||||
[logseq.graph-parser.block :as gp-block]))
|
||||
[logseq.graph-parser.block :as gp-block]
|
||||
[frontend.modules.instrumentation.posthog :as posthog]
|
||||
[cljs-bean.core :as bean]))
|
||||
|
||||
;; Fns
|
||||
|
||||
|
@ -283,6 +285,13 @@
|
|||
|
||||
(defn get-filtered-ref-blocks
|
||||
[ref-blocks filters ref-pages]
|
||||
(let [ref-blocks' (mapcat second ref-blocks)
|
||||
filtered-blocks (filter-blocks ref-blocks' filters ref-pages)]
|
||||
(group-by :block/page filtered-blocks)))
|
||||
(try
|
||||
(let [ref-blocks' (doall (mapcat second ref-blocks))
|
||||
filtered-blocks (filter-blocks ref-blocks' filters ref-pages)]
|
||||
(group-by :block/page filtered-blocks))
|
||||
(catch :default e
|
||||
(js/console.error e)
|
||||
(posthog/capture :bad-ref-blocks (bean/->js
|
||||
{:ref-blocks ref-blocks
|
||||
:filters filters
|
||||
:ref-pages ref-pages})))))
|
||||
|
|
|
@ -3133,13 +3133,16 @@
|
|||
(defn shortcut-left-right [direction]
|
||||
(fn [e]
|
||||
(when-not (auto-complete?)
|
||||
(util/stop e)
|
||||
(cond
|
||||
(state/editing?)
|
||||
(keydown-arrow-handler direction)
|
||||
(do
|
||||
(util/stop e)
|
||||
(keydown-arrow-handler direction))
|
||||
|
||||
(state/selection?)
|
||||
(open-selected-block! direction e)
|
||||
(do
|
||||
(util/stop e)
|
||||
(open-selected-block! direction e))
|
||||
|
||||
:else
|
||||
nil))))
|
||||
|
@ -3198,14 +3201,12 @@
|
|||
:or {semantic? false}}]
|
||||
(when block-id
|
||||
(if-let [block (db-model/query-block-by-uuid block-id)]
|
||||
(and
|
||||
(not (util/collapsed? block))
|
||||
(or (db-model/has-children? block-id)
|
||||
(and
|
||||
(:outliner/block-title-collapse-enabled? (state/get-config))
|
||||
(block-with-title? (:block/format block)
|
||||
(:block/content block)
|
||||
semantic?))))
|
||||
(or (db-model/has-children? block-id)
|
||||
(and
|
||||
(:outliner/block-title-collapse-enabled? (state/get-config))
|
||||
(block-with-title? (:block/format block)
|
||||
(:block/content block)
|
||||
semantic?)))
|
||||
false))))
|
||||
|
||||
(defn all-blocks-with-level
|
||||
|
@ -3284,14 +3285,16 @@
|
|||
value (boolean value)]
|
||||
(when repo
|
||||
(outliner-tx/transact!
|
||||
{:outliner-op :collapse-expand-blocks}
|
||||
(doseq [block-id block-ids]
|
||||
(when-let [block (db/entity [:block/uuid block-id])]
|
||||
(let [current-value (:block/collapsed? block)]
|
||||
(when-not (= current-value value)
|
||||
(let [block {:block/uuid block-id
|
||||
:block/collapsed? value}]
|
||||
(outliner-core/save-block! block)))))))
|
||||
{:outliner-op :collapse-expand-blocks}
|
||||
(doseq [block-id block-ids]
|
||||
(when-let [block (db/entity [:block/uuid block-id])]
|
||||
(let [current-value (:block/collapsed? block)]
|
||||
(when-not (= current-value value)
|
||||
(let [block {:block/uuid block-id
|
||||
:block/collapsed? value}]
|
||||
(outliner-core/save-block! block)))))))
|
||||
(doseq [block-id block-ids]
|
||||
(state/set-collapsed-block! block-id value))
|
||||
(let [block-id (first block-ids)
|
||||
input-pos (or (state/get-edit-pos) :max)]
|
||||
;; update editing input content
|
||||
|
@ -3304,13 +3307,11 @@
|
|||
(defn collapse-block! [block-id]
|
||||
(when (collapsable? block-id)
|
||||
(when-not (skip-collapsing-in-db?)
|
||||
(set-blocks-collapsed! [block-id] true)))
|
||||
(state/set-collapsed-block! block-id true))
|
||||
(set-blocks-collapsed! [block-id] true))))
|
||||
|
||||
(defn expand-block! [block-id]
|
||||
(when-not (skip-collapsing-in-db?)
|
||||
(set-blocks-collapsed! [block-id] false)
|
||||
(state/set-collapsed-block! block-id false)))
|
||||
(set-blocks-collapsed! [block-id] false)))
|
||||
|
||||
(defn expand!
|
||||
([e] (expand! e false))
|
||||
|
@ -3383,12 +3384,15 @@
|
|||
|
||||
(defn collapse-all!
|
||||
([]
|
||||
(collapse-all! nil))
|
||||
([block-id]
|
||||
(collapse-all! nil {}))
|
||||
([block-id {:keys [collapse-self?]
|
||||
:or {collapse-self? true}}]
|
||||
(let [blocks (all-blocks-with-level {:incremental? false
|
||||
:expanded? true
|
||||
:root-block block-id})
|
||||
block-ids (map :block/uuid blocks)]
|
||||
block-ids (cond->> (mapv :block/uuid blocks)
|
||||
(not collapse-self?)
|
||||
(remove #{block-id}))]
|
||||
(set-blocks-collapsed! block-ids true))))
|
||||
|
||||
(defn expand-all!
|
||||
|
@ -3408,6 +3412,14 @@
|
|||
(collapse-all!)
|
||||
(expand-all!))))
|
||||
|
||||
(defn toggle-open-block-children! [block-id]
|
||||
(let [all-expanded? (empty? (all-blocks-with-level {:incremental? false
|
||||
:collapse? true
|
||||
:root-block block-id}))]
|
||||
(if all-expanded?
|
||||
(collapse-all! block-id {:collapse-self? false})
|
||||
(expand-all! block-id))))
|
||||
|
||||
(defn select-all-blocks!
|
||||
[]
|
||||
(if-let [current-input-id (state/get-edit-input-id)]
|
||||
|
@ -3494,8 +3506,7 @@
|
|||
(or
|
||||
(and
|
||||
(or (:ref? config) (:custom-query? config))
|
||||
(>= (inc (:block/level block))
|
||||
(state/get-ref-open-blocks-level))
|
||||
(>= (:block/level block) (state/get-ref-open-blocks-level))
|
||||
;; has children
|
||||
(first (:block/_parent (db/entity (:db/id block)))))
|
||||
(util/collapsed? block)))
|
||||
|
|
|
@ -68,14 +68,15 @@
|
|||
|
||||
(defn- tree [flat-nodes root-id]
|
||||
(let [children (group-by :block/parent flat-nodes)
|
||||
nodes (fn nodes [parent-id]
|
||||
(map (fn [b]
|
||||
(let [children (nodes (:db/id b))]
|
||||
(if (seq children)
|
||||
(assoc b :block/children children)
|
||||
b)))
|
||||
(children {:db/id parent-id})))]
|
||||
(nodes root-id)))
|
||||
nodes (fn nodes [parent-id level]
|
||||
(mapv (fn [b]
|
||||
(let [b' (assoc b :block/level (inc level))
|
||||
children (nodes (:db/id b) (inc level))]
|
||||
(if (seq children)
|
||||
(assoc b' :block/children children)
|
||||
b')))
|
||||
(get children {:db/id parent-id})))]
|
||||
(nodes root-id 1)))
|
||||
|
||||
(defn non-consecutive-blocks->vec-tree
|
||||
"`blocks` need to be in the same page."
|
||||
|
@ -88,12 +89,15 @@
|
|||
id->parent (zipmap (map :db/id blocks)
|
||||
(map (comp :db/id :block/parent) blocks))
|
||||
top-level-ids (set (remove #(id->parent (id->parent %)) (map :db/id blocks)))
|
||||
;; Separate blocks into parent and children groups [parent-children, parent-children]
|
||||
blocks' (loop [blocks blocks
|
||||
result []]
|
||||
(if-let [block (first blocks)]
|
||||
(if (top-level-ids (:db/id block))
|
||||
(recur (rest blocks) (conj result [block]))
|
||||
(recur (rest blocks) (conj (vec (butlast result)) (conj (last result) block))))
|
||||
(let [block' (assoc block :block/level 1)]
|
||||
(recur (rest blocks) (conj result [block'])))
|
||||
(recur (rest blocks) (conj (vec (butlast result))
|
||||
(conj (last result) block))))
|
||||
result))]
|
||||
(map (fn [[parent & children]]
|
||||
(if (seq children)
|
||||
|
|
|
@ -622,6 +622,67 @@
|
|||
:use-cache? false})))
|
||||
total))))))
|
||||
|
||||
(deftest test-non-consecutive-blocks->vec-tree
|
||||
(let [blocks [{:block/page #:db{:id 2313},
|
||||
:block/uuid #uuid "62f49b4c-f9f0-4739-9985-8bd55e4c68d4",
|
||||
:block/parent #:db{:id 2313},
|
||||
:db/id 2315}
|
||||
{:block/page #:db{:id 2313},
|
||||
:block/uuid #uuid "62f49b4c-aa84-416e-9554-b486b4e59b1b",
|
||||
:block/parent #:db{:id 2315},
|
||||
:db/id 2316}
|
||||
{:block/page #:db{:id 2313},
|
||||
:block/uuid #uuid "62f49b4c-f80c-49b4-ae83-f78c4520c071",
|
||||
:block/parent #:db{:id 2316},
|
||||
:db/id 2317}
|
||||
{:block/page #:db{:id 2313},
|
||||
:block/uuid #uuid "62f49b4c-8f5b-4a04-b749-68d34b28bcf2",
|
||||
:block/parent #:db{:id 2317},
|
||||
:db/id 2318}
|
||||
{:block/page #:db{:id 2313},
|
||||
:block/uuid #uuid "62f4b8c1-a99b-434f-84c3-011d6afc48ba",
|
||||
:block/parent #:db{:id 2315},
|
||||
:db/id 2333}
|
||||
{:block/page #:db{:id 2313},
|
||||
:block/uuid #uuid "62f4b8c6-072e-4133-90e2-0591021a7fea",
|
||||
:block/parent #:db{:id 2333},
|
||||
:db/id 2334}]]
|
||||
(= (tree/non-consecutive-blocks->vec-tree blocks)
|
||||
'({:db/id 2315,
|
||||
:block/uuid #uuid "62f49b4c-f9f0-4739-9985-8bd55e4c68d4",
|
||||
:block/parent #:db{:id 2313},
|
||||
:block/page #:db{:id 2313},
|
||||
:block/level 1,
|
||||
:block/children
|
||||
[{:db/id 2316,
|
||||
:block/uuid #uuid "62f49b4c-aa84-416e-9554-b486b4e59b1b",
|
||||
:block/parent #:db{:id 2315},
|
||||
:block/page #:db{:id 2313},
|
||||
:block/level 2,
|
||||
:block/children
|
||||
[{:db/id 2317,
|
||||
:block/uuid #uuid "62f49b4c-f80c-49b4-ae83-f78c4520c071",
|
||||
:block/parent #:db{:id 2316},
|
||||
:block/page #:db{:id 2313},
|
||||
:block/level 3,
|
||||
:block/children
|
||||
[{:db/id 2318,
|
||||
:block/uuid #uuid "62f49b4c-8f5b-4a04-b749-68d34b28bcf2",
|
||||
:block/parent #:db{:id 2317},
|
||||
:block/page #:db{:id 2313},
|
||||
:block/level 4}]}]}
|
||||
{:db/id 2333,
|
||||
:block/uuid #uuid "62f4b8c1-a99b-434f-84c3-011d6afc48ba",
|
||||
:block/parent #:db{:id 2315},
|
||||
:block/page #:db{:id 2313},
|
||||
:block/level 2,
|
||||
:block/children
|
||||
[{:db/id 2334,
|
||||
:block/uuid #uuid "62f4b8c6-072e-4133-90e2-0591021a7fea",
|
||||
:block/parent #:db{:id 2333},
|
||||
:block/page #:db{:id 2313},
|
||||
:block/level 3}]}]}))))
|
||||
|
||||
(comment
|
||||
(dotimes [i 5]
|
||||
(do
|
||||
|
|
Loading…
Reference in New Issue