Merge branch 'master' into zotero/poc

pull/2456/head
Weihua Lu 2021-07-14 15:22:56 +08:00
commit 828e296f86
43 changed files with 891 additions and 359 deletions

View File

@ -1,6 +1,6 @@
{
"name": "@logseq/libs",
"version": "0.0.1-alpha.24",
"version": "0.0.1-alpha.25",
"description": "Logseq SDK libraries",
"main": "dist/lsplugin.user.js",
"typings": "index.d.ts",

View File

@ -220,7 +220,11 @@ class LSPluginCaller extends EventEmitter {
this._call = async (...args: any) => {
// parent all will get message before handshaked
await refChild.call(LSPMSGFn(pl.id), { type: args[0], payload: args[1] || {} })
await refChild.call(LSPMSGFn(pl.id), {
type: args[0], payload: Object.assign(args[1] || {}, {
$$pid: pl.id
})
})
}
this._callUserModel = async (type, payload: any) => {
@ -263,7 +267,9 @@ class LSPluginCaller extends EventEmitter {
// TODO: support sync call
// @ts-ignore Call in same thread
this._pluginLocal?.emit(type, payload)
this._pluginLocal?.emit(type, Object.assign(payload, {
$$pid: pl.id
}))
return actor?.promise
}

View File

@ -7,7 +7,7 @@ import {
setupInjectedUI,
deferred,
invokeHostExportedApi,
isObject, withFileProtocol
isObject, withFileProtocol, IS_DEV, getSDKPathRoot
} from './helpers'
import * as pluginHelpers from './helpers'
import Debug from 'debug'
@ -477,7 +477,7 @@ class PluginLocal
if (!entry.endsWith('.js')) return
let sdkPath = await invokeHostExportedApi('_callApplication', 'getAppPath')
let sdkPathRoot = await getSDKPathRoot()
let entryPath = await invokeHostExportedApi(
'write_user_tmp_file',
`${this._id}_index.html`,
@ -486,7 +486,7 @@ class PluginLocal
<head>
<meta charset="UTF-8">
<title>logseq plugin entry</title>
<script src="${sdkPath}/js/lsplugin.user.js"></script>
<script src="${sdkPathRoot}/lsplugin.user.js"></script>
</head>
<body>
<div id="app"></div>

View File

@ -1,6 +1,7 @@
import EventEmitter from 'eventemitter3'
import * as CSS from 'csstype'
import { LSPluginCaller } from './LSPlugin.caller'
import { LSPluginFileStorage } from './modules/LSPlugin.Storage'
export type PluginLocalIdentity = string
@ -494,4 +495,6 @@ export interface ILSPluginUser extends EventEmitter<LSPluginUserEvents> {
App: IAppProxy & Record<string, any>
Editor: IEditorProxy & Record<string, any>
DB: IDBProxy
FileStorage: LSPluginFileStorage
}

View File

@ -16,6 +16,7 @@ import Debug from 'debug'
import * as CSS from 'csstype'
import { snakeCase } from 'snake-case'
import EventEmitter from 'eventemitter3'
import { LSPluginFileStorage } from './modules/LSPlugin.Storage'
declare global {
interface Window {
@ -154,7 +155,7 @@ const editor: Partial<IEditorProxy> = {
return false
}
const key = + '_' + this.baseInfo.id
const key = tag + '_' + this.baseInfo.id
const label = tag
const type = 'block-context-menu-item'
@ -192,6 +193,8 @@ export class LSPluginUser extends EventEmitter<LSPluginUserEvents> implements IL
*/
private _ui = new Map<number, uiState>()
private _fileStorage: LSPluginFileStorage
/**
* handler of before unload plugin
* @private
@ -226,6 +229,9 @@ export class LSPluginUser extends EventEmitter<LSPluginUserEvents> implements IL
actor?.reject(e)
}
})
// modules
this._fileStorage = new LSPluginFileStorage(this)
}
async ready (
@ -400,6 +406,10 @@ export class LSPluginUser extends EventEmitter<LSPluginUserEvents> implements IL
get DB (): IDBProxy {
return this._makeUserProxy(db)
}
get FileStorage (): LSPluginFileStorage {
return this._fileStorage
}
}
export * from './LSPlugin'

View File

@ -1,6 +1,7 @@
import { StyleString, UIOptions } from './LSPlugin'
import { PluginLocal } from './LSPlugin.core'
import { snakeCase } from 'snake-case'
import * as path from 'path'
interface IObject {
[key: string]: any;
@ -13,6 +14,31 @@ declare global {
}
}
export const IS_DEV = process.env.NODE_ENV === 'development'
let _appPathRoot
export async function getAppPathRoot (): Promise<string> {
if (_appPathRoot) {
return _appPathRoot
}
return (_appPathRoot =
await invokeHostExportedApi('_callApplication', 'getAppPath')
)
}
export async function getSDKPathRoot (): Promise<string> {
if (IS_DEV) {
// TODO: cache in preference file
return localStorage.getItem('LSP_DEV_SDK_ROOT') || 'http://localhost:8080'
}
const appPathRoot = await getAppPathRoot()
return path.join(appPathRoot, 'js')
}
export function isObject (item: any) {
return (item === Object(item) && !Array.isArray(item))
}

View File

@ -0,0 +1,87 @@
import { LSPluginUser } from '../LSPlugin.user'
export interface IAsyncStorage {
getItem (key: string): Promise<string | undefined>
setItem (key: string, value: string): Promise<void>
removeItem (key: string): Promise<void>
hasItem (key: string): Promise<boolean>
clear (): Promise<void>
}
/**
* A storage based on local files under specific context
*/
class LSPluginFileStorage implements IAsyncStorage {
/**
* @param ctx
*/
constructor (
private ctx: LSPluginUser
) {}
/**
* plugin id
*/
get ctxId () {
return this.ctx.baseInfo.id
}
/**
* @param key A string as file name that support nested directory
* @param value Storage value
*/
setItem (key: string, value: string): Promise<void> {
return this.ctx.caller.callAsync(`api:call`, {
method: 'write-plugin-storage-file',
args: [this.ctxId, key, value]
})
}
/**
* @param key
*/
getItem (key: string): Promise<string | undefined> {
return this.ctx.caller.callAsync(`api:call`, {
method: 'read-plugin-storage-file',
args: [this.ctxId, key]
})
}
/**
* @param key
*/
removeItem (key: string): Promise<void> {
return this.ctx.caller.call(`api:call`, {
method: 'unlink-plugin-storage-file',
args: [this.ctxId, key]
})
}
/**
* Clears the storage
*/
clear (): Promise<void> {
return this.ctx.caller.call(`api:call`, {
method: 'clear-plugin-storage-files',
args: [this.ctxId]
})
}
/**
* @param key
*/
hasItem (key: string): Promise<boolean> {
return this.ctx.caller.callAsync(`api:call`, {
method: 'exist-plugin-storage-file',
args: [this.ctxId, key]
})
}
}
export {
LSPluginFileStorage
}

View File

@ -1,4 +1,5 @@
const path = require('path')
const webpack = require('webpack')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports = {
@ -16,11 +17,14 @@ module.exports = {
extensions: ['.tsx', '.ts', '.js'],
},
plugins: [
new webpack.ProvidePlugin({
process: 'process/browser',
}),
// new BundleAnalyzerPlugin()
],
output: {
library: "LSPluginEntry",
libraryTarget: "umd",
library: 'LSPluginEntry',
libraryTarget: 'umd',
filename: 'lsplugin.user.js',
path: path.resolve(__dirname, 'dist')
},

View File

@ -90,11 +90,11 @@ html[data-theme='light'] {
--ls-secondary-background-color: #f7f7f7;
--ls-tertiary-background-color: #f1eee8;
--ls-quaternary-background-color: #e8e5de;
--ls-table-tr-even-background-color: #f4f5f7;
--ls-table-tr-even-background-color: #f7f7f7;
--ls-active-primary-color: rgb(4, 85, 145);
--ls-active-secondary-color: #003761;
--ls-block-properties-background-color: #f7f6f4;
--ls-page-properties-background-color: #eae7e1;
--ls-block-properties-background-color: #f7f7f7;
--ls-page-properties-background-color: #f7f7f7;
--ls-block-ref-link-text-color: #d8e1e8;
--ls-search-background-color: var(--ls-primary-background-color);
--ls-border-color: #ccc;
@ -120,7 +120,7 @@ html[data-theme='light'] {
--ls-page-blockquote-color: var(--ls-primary-text-color);
--ls-page-blockquote-bg-color: #fbfaf8;
--ls-page-blockquote-border-color: #799bbc;
--ls-page-inline-code-bg-color: #f7f6f4;
--ls-page-inline-code-bg-color: #f7f7f7;
--ls-page-inline-code-color: var(--ls-primary-text-color);
--ls-scrollbar-foreground-color: rgba(0, 0, 0, 0.1);
--ls-scrollbar-background-color: rgba(0, 0, 0, 0.05);
@ -128,7 +128,7 @@ html[data-theme='light'] {
--ls-head-text-color: var(--ls-link-text-color);
--ls-icon-color: #908e8b;
--ls-search-icon-color: var(--ls-icon-color);
--ls-a-chosen-bg: #f4f5f7;
--ls-a-chosen-bg: #f7f7f7;
--ls-right-sidebar-code-bg-color: var(--ls-secondary-background-color);
--color-level-1: var(--ls-secondary-background-color);
--color-level-2: var(--ls-tertiary-background-color);
@ -724,14 +724,14 @@ li p:last-child,
overflow-y: scroll;
}
.loader {
-webkit-animation: spin 2s infinite linear;
}
.loader-reverse {
-webkit-animation: spin 2s infinite linear reverse;
}
.loader {
-webkit-animation: spin 2s infinite linear;
}
.canceled,
.done {
text-decoration: line-through;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{
"name": "Logseq",
"version": "0.2.3",
"version": "0.2.4",
"main": "electron.js",
"author": "Logseq",
"description": "A privacy-first, open-source platform for knowledge management and collaboration.",

View File

@ -18,6 +18,12 @@
(defmethod handle :mkdir [_window [_ dir]]
(fs/mkdirSync dir))
(defmethod handle :mkdir-recur [_window [_ dir]]
(fs/mkdirSync dir #js {:recursive true}))
(defmethod handle :rmdir-recur [_window [_ dir]]
(fs/rmdirSync dir #js {:recursive true}))
;; {encoding: 'utf8', withFileTypes: true}
(defn- readdir
[dir]

View File

@ -8,6 +8,7 @@
[promesa.core :as p]
[electron.ipc :as ipc]
[frontend.handler.notification :as notification]
[frontend.handler.metadata :as metadata-handler]
[frontend.ui :as ui]))
(defn listen-to-open-dir!
@ -41,6 +42,8 @@
(notification/show!
(ui/loading "Logseq is saving the graphs to your local file system, please wait for several seconds.")
:warning)
(doseq [repo repos]
(metadata-handler/set-pages-metadata! repo))
(js/setTimeout
(fn []
(-> (p/all (map db/persist! repos))

View File

@ -330,49 +330,52 @@
(rum/defc page-inner
[config page-name href redirect-page-name page-entity contents-page? children html-export? label]
[:a.page-ref
{:data-ref page-name
:href href
:on-click (fn [e]
(util/stop e)
(let [create-first-block! (fn []
(when-not (editor-handler/add-default-title-property-if-needed! redirect-page-name)
(editor-handler/insert-first-page-block-if-not-exists! redirect-page-name)))]
(if (gobj/get e "shiftKey")
(do
(js/setTimeout create-first-block! 310)
(when-let [page-entity (db/entity [:block/name redirect-page-name])]
(state/sidebar-add-block!
(state/get-current-repo)
(:db/id page-entity)
:page
{:page page-entity})))
(do
(create-first-block!)
(route-handler/redirect! {:to :page
:path-params {:name redirect-page-name}}))))
(when (and contents-page?
(state/get-left-sidebar-open?))
(ui-handler/close-left-sidebar!)))}
(let [tag? (:tag? config)]
[:a
{:class (if tag? "tag" "page-ref")
:data-ref page-name
:href href
:on-click (fn [e]
(util/stop e)
(let [create-first-block! (fn []
(when-not (editor-handler/add-default-title-property-if-needed! redirect-page-name)
(editor-handler/insert-first-page-block-if-not-exists! redirect-page-name)))]
(if (gobj/get e "shiftKey")
(do
(js/setTimeout create-first-block! 310)
(when-let [page-entity (db/entity [:block/name redirect-page-name])]
(state/sidebar-add-block!
(state/get-current-repo)
(:db/id page-entity)
:page
{:page page-entity})))
(do
(create-first-block!)
(route-handler/redirect! {:to :page
:path-params {:name redirect-page-name}}))))
(when (and contents-page?
(state/get-left-sidebar-open?))
(ui-handler/close-left-sidebar!)))}
(if (and (coll? children) (seq children))
(for [child children]
(if (= (first child) "Label")
(last child)
(let [{:keys [content children]} (last child)
page-name (subs content 2 (- (count content) 2))]
(rum/with-key (page-reference html-export? page-name (assoc config :children children) nil) page-name))))
(cond
(and label
(string? label)
(not (string/blank? label))) ; alias
label
(if (and (coll? children) (seq children))
(for [child children]
(if (= (first child) "Label")
(last child)
(let [{:keys [content children]} (last child)
page-name (subs content 2 (- (count content) 2))]
(rum/with-key (page-reference html-export? page-name (assoc config :children children) nil) page-name))))
(cond
(and label
(string? label)
(not (string/blank? label))) ; alias
label
(coll? label)
(->elem :span (map-inline config label))
(coll? label)
(->elem :span (map-inline config label))
:else
(get page-entity :block/original-name page-name)))])
:else
(let [s (get page-entity :block/original-name page-name)]
(if tag? (str "#" s) s))))]))
(defn- use-delayed-open [open? page-name]
"A react hook to debounce open? value.
@ -737,19 +740,7 @@
["Tag" s]
(when-let [s (block/get-tag item)]
(let [s (text/page-ref-un-brackets! s)]
[:a.tag {:data-ref s
:href (rfe/href :page {:name s})
:on-click (fn [e]
(let [repo (state/get-current-repo)
page (db/pull repo '[*] [:block/name (string/lower-case (util/url-decode s))])]
(when (gobj/get e "shiftKey")
(state/sidebar-add-block!
repo
(:db/id page)
:page
{:page page})
(.preventDefault e))))}
(str "#" s)]))
(page-cp (assoc config :tag? true) {:block/name s})))
["Emphasis" [[kind] data]]
(let [elem (case kind
@ -800,10 +791,15 @@
(block-reference (assoc config :reference? true) id label*))
["Page_ref" page]
(let [label* (if (seq (mldoc/plain->text label)) label nil)]
(if (and (string? page) (string/blank? page))
[:span (util/format "[[%s]]" page)]
(page-reference (:html-export? config) page config label*)))
(let [format (get-in config [:block :block/format])]
(if (and (= format :org)
(show-link? config nil page page)
(not (contains? #{"pdf" "mp4" "ogg" "webm"} (util/get-file-ext page))))
(image-link config url page nil nil page)
(let [label* (if (seq (mldoc/plain->text label)) label nil)]
(if (and (string? page) (string/blank? page))
[:span (util/format "[[%s]]" page)]
(page-reference (:html-export? config) page config label*)))))
["Search" s]
(cond
@ -1215,12 +1211,14 @@
(rum/defcs block-control < rum/reactive
[state config block uuid block-id body children collapsed? *ref-collapsed? *control-show?]
(let [has-child? (and
(let [has-children-blocks? (and (coll? children) (seq children))
has-child? (and
(not (:pre-block? block))
(or (and (coll? children) (seq children))
(seq body)))
(or has-children-blocks? (seq body)))
control-show? (and
(seq (:block/title block))
(or (and (seq (:block/title block))
(seq body))
has-children-blocks?)
(util/react *control-show?))
ref-collapsed? (util/react *ref-collapsed?)
dark? (= "dark" (state/sub :ui/theme))
@ -1460,10 +1458,7 @@
(rum/with-key elem (str (random-uuid)))))
:else
(let [page-name (string/lower-case (str v))]
(if (db/entity [:block/name page-name])
(page-cp config {:block/name page-name})
(inline-text (:block/format block) (str v)))))]))
(inline-text (:block/format block) (str v)))]))
(rum/defc properties-cp
[config block]
@ -2116,11 +2111,13 @@
;; exclude the current one, otherwise it'll loop forever
remove-blocks (if current-block-uuid [current-block-uuid] nil)
query-result (and query-atom (rum/react query-atom))
not-grouped-by-page? (and (string? query) (string/includes? query "(by-page false)"))
result (when query-result
(db/custom-query-result-transform query-result remove-blocks q))
(db/custom-query-result-transform query-result remove-blocks q not-grouped-by-page?))
view-f (and view (sci/eval-string (pr-str view)))
only-blocks? (:block/uuid (first result))
blocks-grouped-by-page? (and (seq result)
(not not-grouped-by-page?)
(coll? (first result))
(:block/name (ffirst result))
(:block/uuid (first (second (first result))))
@ -2161,8 +2158,7 @@
:path-params {:name name}})))}
(or original-name name)]])]
(and (seq result)
(or only-blocks? blocks-grouped-by-page?))
(and (seq result) (or only-blocks? blocks-grouped-by-page?))
(->hiccup result (cond-> (assoc config
:custom-query? true
;; :breadcrumb-show? true

View File

@ -295,14 +295,14 @@
[:div.flex.flex-row.space-between
[:div.flex-1.flex-row
[:a.page-title {:on-click (fn [e]
(.preventDefault e)
(when (gobj/get e "shiftKey")
(when-let [page (db/pull repo '[*] [:block/name page-name])]
(state/sidebar-add-block!
repo
(:db/id page)
:page
{:page page}))))}
(.preventDefault e)
(when (gobj/get e "shiftKey")
(when-let [page (db/pull repo '[*] [:block/name page-name])]
(state/sidebar-add-block!
repo
(:db/id page)
:page
{:page page}))))}
[:h1.title {:style {:margin-left -2}}
(if page-original-name
(if (and (string/includes? page-original-name "[[")
@ -316,66 +316,66 @@
(when (not config/publishing?)
(let [contents? (= (string/lower-case (str page-name)) "contents")
links (fn [] (->>
[(when-not contents?
{:title (t :page/add-to-favorites)
:options {:on-click (fn [] (page-handler/handle-add-page-to-contents! page-original-name))}})
[(when-not contents?
{:title (t :page/add-to-favorites)
:options {:on-click (fn [] (page-handler/handle-add-page-to-contents! page-original-name))}})
{:title "Go to presentation mode"
:options {:on-click (fn []
(state/sidebar-add-block!
repo
(:db/id page)
:page-presentation
{:page page}))}}
(when (and (not contents?)
(not journal?))
{:title (t :page/rename)
:options {:on-click #(state/set-modal! (rename-page-dialog title page-name))}})
{:title "Go to presentation mode"
:options {:on-click (fn []
(state/sidebar-add-block!
repo
(:db/id page)
:page-presentation
{:page page}))}}
(when (and (not contents?)
(not journal?))
{:title (t :page/rename)
:options {:on-click #(state/set-modal! (rename-page-dialog title page-name))}})
(when-let [file-path (and (util/electron?) (page-handler/get-page-file-path))]
[{:title (t :page/open-in-finder)
:options {:on-click #(js/window.apis.showItemInFolder file-path)}}
{:title (t :page/open-with-default-app)
:options {:on-click #(js/window.apis.openPath file-path)}}])
(when-let [file-path (and (util/electron?) (page-handler/get-page-file-path))]
[{:title (t :page/open-in-finder)
:options {:on-click #(js/window.apis.showItemInFolder file-path)}}
{:title (t :page/open-with-default-app)
:options {:on-click #(js/window.apis.openPath file-path)}}])
(when-not contents?
{:title (t :page/delete)
:options {:on-click #(state/set-modal! (delete-page-dialog page-name))}})
(when-not contents?
{:title (t :page/delete)
:options {:on-click #(state/set-modal! (delete-page-dialog page-name))}})
(when (state/get-current-page)
{:title (t :export)
:options {:on-click #(state/set-modal! export/export-page)}})
(when (state/get-current-page)
{:title (t :export)
:options {:on-click #(state/set-modal! export/export-page)}})
(when (util/electron?)
{:title (t (if public? :page/make-private :page/make-public))
:options {:on-click
(fn []
(page-handler/update-public-attribute!
page-name
(if public? false true))
(state/close-modal!))}})
(when (util/electron?)
{:title (t (if public? :page/make-private :page/make-public))
:options {:on-click
(fn []
(page-handler/update-public-attribute!
page-name
(if public? false true))
(state/close-modal!))}})
(when plugin-handler/lsp-enabled?
(for [[_ {:keys [key label] :as cmd} action pid] (state/get-plugins-commands-with-type :page-menu-item)]
{:title label
:options {:on-click #(commands/exec-plugin-simple-command!
(when plugin-handler/lsp-enabled?
(for [[_ {:keys [key label] :as cmd} action pid] (state/get-plugins-commands-with-type :page-menu-item)]
{:title label
:options {:on-click #(commands/exec-plugin-simple-command!
pid (assoc cmd :page (state/get-current-page)) action)}}))
(when developer-mode?
{:title "(Dev) Show page data"
:options {:on-click (fn []
(let [page-data (with-out-str (pprint/pprint (db/pull (:db/id page))))]
(println page-data)
(notification/show!
[:div
[:pre.code page-data]
[:br]
(ui/button "Copy to clipboard"
:on-click #(.writeText js/navigator.clipboard page-data))]
:success
false)))}})]
(flatten)
(remove nil?)))]
(when developer-mode?
{:title "(Dev) Show page data"
:options {:on-click (fn []
(let [page-data (with-out-str (pprint/pprint (db/pull (:db/id page))))]
(println page-data)
(notification/show!
[:div
[:pre.code page-data]
[:br]
(ui/button "Copy to clipboard"
:on-click #(.writeText js/navigator.clipboard page-data))]
:success
false)))}})]
(flatten)
(remove nil?)))]
[:div.flex.flex-row
(when plugin-handler/lsp-enabled?
@ -497,63 +497,63 @@
(util/format "%d page%s" c1 s1)
)]
[:div.p-6
;; [:div.flex.items-center.justify-between.mb-2
;; [:span "Layout"]
;; (ui/select
;; (mapv
;; (fn [item]
;; (if (= (:label item) layout)
;; (assoc item :selected "selected")
;; item))
;; [{:label "gForce"}
;; {:label "dagre"}])
;; (fn [value]
;; (set-setting! :layout value))
;; "graph-layout")]
[:div.flex.items-center.justify-between.mb-2
[:span "Journals"]
;; FIXME: why it's not aligned well?
[:div.mt-1
(ui/toggle journal?
(fn []
(let [value (not journal?)]
(reset! *journal? value)
(set-setting! :journal? value)))
true)]]
[:div.flex.items-center.justify-between.mb-2
[:span "Orphan pages"]
[:div.mt-1
(ui/toggle orphan-pages?
(fn []
(let [value (not orphan-pages?)]
(reset! *orphan-pages? value)
(set-setting! :orphan-pages? value)))
true)]]
[:div.flex.items-center.justify-between.mb-2
[:span "Built-in pages"]
[:div.mt-1
(ui/toggle builtin-pages?
(fn []
(let [value (not builtin-pages?)]
(reset! *builtin-pages? value)
(set-setting! :builtin-pages? value)))
true)]]
(when (seq focus-nodes)
[:div.flex.flex-col.mb-2
[:p {:title "N hops from selected nodes"}
"N hops from selected nodes"]
(ui/tippy {:html [:div.pr-3 n-hops]}
(ui/slider (or n-hops 10)
{:min 1
:max 10
:on-change #(reset! *n-hops (int %))}))])
;; [:div.flex.items-center.justify-between.mb-2
;; [:span "Layout"]
;; (ui/select
;; (mapv
;; (fn [item]
;; (if (= (:label item) layout)
;; (assoc item :selected "selected")
;; item))
;; [{:label "gForce"}
;; {:label "dagre"}])
;; (fn [value]
;; (set-setting! :layout value))
;; "graph-layout")]
[:div.flex.items-center.justify-between.mb-2
[:span "Journals"]
;; FIXME: why it's not aligned well?
[:div.mt-1
(ui/toggle journal?
(fn []
(let [value (not journal?)]
(reset! *journal? value)
(set-setting! :journal? value)))
true)]]
[:div.flex.items-center.justify-between.mb-2
[:span "Orphan pages"]
[:div.mt-1
(ui/toggle orphan-pages?
(fn []
(let [value (not orphan-pages?)]
(reset! *orphan-pages? value)
(set-setting! :orphan-pages? value)))
true)]]
[:div.flex.items-center.justify-between.mb-2
[:span "Built-in pages"]
[:div.mt-1
(ui/toggle builtin-pages?
(fn []
(let [value (not builtin-pages?)]
(reset! *builtin-pages? value)
(set-setting! :builtin-pages? value)))
true)]]
(when (seq focus-nodes)
[:div.flex.flex-col.mb-2
[:p {:title "N hops from selected nodes"}
"N hops from selected nodes"]
(ui/tippy {:html [:div.pr-3 n-hops]}
(ui/slider (or n-hops 10)
{:min 1
:max 10
:on-change #(reset! *n-hops (int %))}))])
[:a.opacity-70.opacity-100 {:on-click (fn []
(swap! *graph-reset? not)
(reset! *focus-nodes [])
(reset! *n-hops nil)
(state/clear-search-filters!))}
"Reset Graph"]]])))
[:a.opacity-70.opacity-100 {:on-click (fn []
(swap! *graph-reset? not)
(reset! *focus-nodes [])
(reset! *n-hops nil)
(state/clear-search-filters!))}
"Reset Graph"]]])))
(graph-filter-section
[:span.font-medium "Search"]
(fn [open?]
@ -674,36 +674,68 @@
(when (seq (:nodes graph))
(page-graph-inner graph dark?))))
(rum/defc all-pages < rum/reactive
(defn- sort-pages-by
[by-item desc? pages]
(let [comp (if desc? > <)
by-item (if (= by-item :block/name)
(fn [x] (string/lower-case (:block/name x)))
by-item)]
(sort-by by-item comp pages)))
(rum/defc sortable-title
[title key by-item desc?]
[:th
[:a {:on-click (fn []
(reset! by-item key)
(swap! desc? not))}
[:div.flex.items-center
[:span.mr-1 title]
(when (= @by-item key)
[:span
(if @desc? (svg/caret-down) (svg/caret-up))])]]])
(rum/defcs all-pages < rum/reactive
(rum/local :block/updated-at ::sort-by-item)
(rum/local true ::desc?)
;; {:did-mount (fn [state]
;; (let [current-repo (state/sub :git/current-repo)]
;; (js/setTimeout #(db/remove-orphaned-pages! current-repo) 0))
;; state)}
[]
(let [current-repo (state/sub :git/current-repo)]
[state]
(let [current-repo (state/sub :git/current-repo)
*sort-by-item (get state ::sort-by-item)
*desc? (get state ::desc?)]
(rum/with-context [[t] i18n/*tongue-context*]
[:div.flex-1
[:h1.title (t :all-pages)]
(when current-repo
(let [pages (page-handler/get-pages-with-modified-at current-repo)]
(let [pages (->> (page-handler/get-all-pages current-repo)
(map (fn [page] (assoc page :block/backlinks (count (:block/_refs (db/entity (:db/id page)))))))
(sort-pages-by @*sort-by-item @*desc?))]
[:table.table-auto
[:thead
[:tr
[:th (t :block/name)]
[:th (t :file/last-modified-at)]]]
(sortable-title (t :block/name) :block/name *sort-by-item *desc?)
(sortable-title (t :page/backlinks) :block/backlinks *sort-by-item *desc?)
(sortable-title (t :page/created-at) :block/created-at *sort-by-item *desc?)
(sortable-title (t :page/updated-at) :block/updated-at *sort-by-item *desc?)]]
[:tbody
(for [page pages]
[:tr {:key page}
(for [{:block/keys [name created-at updated-at backlinks] :as page} pages]
[:tr {:key name}
[:td [:a {:on-click (fn [e]
(let [repo (state/get-current-repo)
page (db/pull repo '[*] [:block/name (string/lower-case page)])]
(let [repo (state/get-current-repo)]
(when (gobj/get e "shiftKey")
(state/sidebar-add-block!
repo
(:db/id page)
:page
{:page page}))))
:href (rfe/href :page {:name page})}
page]]
[:td [:span.text-gray-500.text-sm
(t :file/no-data)]]])]]))])))
{:page (:block/name page)}))))
:href (rfe/href :page {:name (:block/name page)})}
(block/page-cp {} page)]]
[:td [:span.text-gray-500.text-sm backlinks]]
[:td [:span.text-gray-500.text-sm (if created-at
(date/int->local-time created-at)
"Unknown")]]
[:td [:span.text-gray-500.text-sm (if updated-at
(date/int->local-time updated-at)
"Unknown")]]])]]))])))

View File

@ -68,7 +68,7 @@
/* Change to another cursor style if Shift key is active */
[data-active-keystroke*="Shift" i]
:is(.journal-title, .page-title,
.block-ref, .page-ref, a.tag,
.block-ref, .page-ref, a.tag,
.bullet-container.cursor) {
cursor: e-resize;
}

View File

@ -342,6 +342,14 @@
(let [value (not enable-all-pages-public?)]
(config-handler/set-config! :publishing/all-pages-public? value)))))
(defn enable-block-timestamps-row [t enable-block-timestamps?]
(toggle "block timestamps"
(t :settings-page/enable-block-time)
enable-block-timestamps?
(fn []
(let [value (not enable-block-timestamps?)]
(config-handler/set-config! :feature/enable-block-timestamps? value)))))
(defn encryption-row [t enable-encryption?]
(toggle "enable_encryption"
(t :settings-page/enable-encryption)
@ -430,7 +438,7 @@
logical-outdenting? (state/logical-outdenting?)
enable-tooltip? (state/enable-tooltip?)
enable-git-auto-push? (state/enable-git-auto-push? current-repo)
enable-block-time? (state/enable-block-time?)
enable-block-timestamps? (state/enable-block-timestamps?)
show-brackets? (state/show-brackets?)
github-token (state/sub [:me :access-token])
cors-proxy (state/sub [:me :cors_proxy])
@ -457,6 +465,7 @@
(file-format-row t preferred-format)
(date-format-row t preferred-date-format)
(workflow-row t preferred-workflow)
(enable-block-timestamps-row t enable-block-timestamps?)
(show-brackets-row t show-brackets?)
(outdenting-row t logical-outdenting?)
(tooltip-row t enable-tooltip?)

View File

@ -310,6 +310,17 @@
c37.027-10.806,61.375,4.323,61.375,4.323C218.946,192.781,201.513,216.553,201.513,216.553z"}]])
(rum/defc caret-up
[]
[:svg.h-4.w-4
{:aria-hidden "true"
:version "1.1"
:view-box "0 0 320 512"
:fill "currentColor"
:display "inline-block"}
[:path {:d "M288.662 352H31.338c-17.818 0-26.741-21.543-14.142-34.142l128.662-128.662c7.81-7.81 20.474-7.81 28.284 0l128.662 128.662c12.6 12.599 3.676 34.142-14.142 34.142z"}]])
(rum/defc caret-down
[]
[:svg.h-4.w-4

View File

@ -292,6 +292,7 @@
(def config-file "config.edn")
(def custom-css-file "custom.css")
(def metadata-file "metadata.edn")
(def pages-metadata-file "pages-metadata.edn")
(def config-default-content (rc/inline "config.edn"))
@ -369,6 +370,13 @@
(when repo
(get-file-path repo (str app-name "/" metadata-file)))))
(defn get-pages-metadata-path
([]
(get-pages-metadata-path (state/get-current-repo)))
([repo]
(when repo
(get-file-path repo (str app-name "/" pages-metadata-file)))))
(defn get-custom-css-path
([]
(get-custom-css-path (state/get-current-repo)))

View File

@ -191,6 +191,12 @@
(when day
(format (tf/parse (tf/formatter "yyyyMMdd") (str day)))))
(defn journal-day->ts
[day]
(when day
(-> (tf/parse (tf/formatter "yyyyMMdd") (str day))
(tc/to-long))))
(defn journal-title->long
[journal-title]
(journal-title-> journal-title #(tc/to-long %)))

View File

@ -299,16 +299,23 @@
(keyword (string/lower-case (name order)))
:desc)
k (-> (string/lower-case (name k))
(string/replace "-" "_"))]
(when (contains? #{"created_at" "last_modified_at"} k)
(let [comp (if (= order :desc) >= <=)]
(reset! sort-by
(fn [result]
(->> result
flatten
(clojure.core/sort-by #(get-in % [:block/properties k])
comp))))
nil)))
(string/replace "_" "-"))]
(let [get-value (cond
(= k "created-at")
:block/created-at
(= k "updated-at")
:block/updated-at
:else
#(get-in % [:block/properties k]))
comp (if (= order :desc) >= <=)]
(reset! sort-by
(fn [result]
(->> result
flatten
(clojure.core/sort-by get-value comp))))
nil))
(= 'page fe)
(let [page-name (string/lower-case (first (rest e)))
@ -442,9 +449,12 @@
result)
(when-let [query (query-wrapper query blocks?)]
(react/react-query repo
{:query query}
(if sort-by
{:transform-fn sort-by})))))))))
{:query query
:query-string query-string}
(cond->
{:use-cache? false}
sort-by
(assoc :transform-fn sort-by))))))))))
(defn custom-query
[repo query-m query-opts]

View File

@ -50,7 +50,7 @@
input))
(defn custom-query-result-transform
[query-result remove-blocks q]
[query-result remove-blocks q not-grouped-by-page?]
(try
(let [repo (state/get-current-repo)
result (db-utils/seq-flatten query-result)
@ -75,7 +75,7 @@
(log/error :sci/call-error e)
result))
result)
(if block?
(if (and block? (not not-grouped-by-page?))
(db-utils/group-by-page result)
result))))
(catch js/Error e

View File

@ -178,6 +178,9 @@
:file/last-modified-at "Last modified at"
:file/no-data "No data"
:file/format-not-supported "Format .{1} is not supported."
:page/created-at "Created At"
:page/updated-at "Updated At"
:page/backlinks "Back Links"
:editor/block-search "Search for a block"
:editor/image-uploading "Uploading"
:draw/invalid-file "Could not load this invalid excalidraw file"

View File

@ -63,8 +63,7 @@
(let [page-name (string/lower-case node)]
(.unhoverNode ^js graph node)
(route-handler/redirect! {:to :page
:path-params {:name page-name}})
))))
:path-params {:name page-name}})))))
(defn reset-graph!
[^js graph]

View File

@ -555,7 +555,14 @@
(with-page-refs with-id?)
with-block-refs
block-tags->pages)
last-pos' (get-in block [:meta :start-pos])]
last-pos' (get-in block [:meta :start-pos])
{:keys [created-at updated-at]} (:properties properties)
block (cond-> block
(and created-at (integer? created-at))
(assoc :block/created-at created-at)
(and updated-at (integer? updated-at))
(assoc :block/updated-at updated-at))]
(recur (conj headings block) [] (rest blocks) {} {} last-pos' (:level block) children []))
:else

View File

@ -43,6 +43,10 @@
[dir]
(protocol/mkdir! (get-fs dir) dir))
(defn mkdir-recur!
[dir]
(protocol/mkdir-recur! (get-fs dir) dir))
(defn readdir
[dir]
(protocol/readdir (get-fs dir) dir))
@ -61,20 +65,20 @@
[repo dir path content opts]
(when content
(let [fs-record (get-fs dir)]
(p/let [metadata-or-css? (or (string/ends-with? path config/metadata-file)
(string/ends-with? path config/custom-css-file))
content (if metadata-or-css? content (encrypt/encrypt content))]
(->
(p/let [_ (protocol/write-file! (get-fs dir) repo dir path content opts)]
(when (= bfs-record fs-record)
(db/set-file-last-modified-at! repo (config/get-file-path repo path) (js/Date.))))
(p/catch (fn [error]
(log/error :file/write-failed {:dir dir
:path path
:error error})
;; Disable this temporarily
;; (js/alert "Current file can't be saved! Please copy its content to your local file system and click the refresh button.")
)))))))
(p/let [metadata-or-css? (or (string/ends-with? path (str "/" config/metadata-file))
(string/ends-with? path (str "/" config/custom-css-file)))
content (if metadata-or-css? content (encrypt/encrypt content))]
(->
(p/let [_ (protocol/write-file! (get-fs dir) repo dir path content opts)]
(when (= bfs-record fs-record)
(db/set-file-last-modified-at! repo (config/get-file-path repo path) (js/Date.))))
(p/catch (fn [error]
(log/error :file/write-failed {:dir dir
:path path
:error error})
;; Disable this temporarily
;; (js/alert "Current file can't be saved! Please copy its content to your local file system and click the refresh button.")
)))))))
(defn read-file
([dir path]
@ -90,7 +94,7 @@
(defn rename!
[repo old-path new-path]
(cond
; See https://github.com/isomorphic-git/lightning-fs/issues/41
; See https://github.com/isomorphic-git/lightning-fs/issues/41
(= old-path new-path)
(p/resolved nil)
@ -146,9 +150,9 @@
(p/let [stat (stat dir path)]
true)
(p/catch
(fn [_error]
(p/let [_ (write-file! repo dir path initial-content nil)]
false)))))))
(fn [_error]
(p/let [_ (write-file! repo dir path initial-content nil)]
false)))))))
(defn file-exists?
[dir path]

View File

@ -65,12 +65,14 @@
protocol/Fs
(mkdir! [this dir]
(ipc/ipc "mkdir" dir))
(mkdir-recur! [this dir]
(ipc/ipc "mkdir-recur" dir))
(readdir [this dir] ; recursive
(ipc/ipc "readdir" dir))
(unlink! [this path _opts]
(ipc/ipc "unlink" path))
(rmdir! [this dir]
nil)
(ipc/ipc "rmdir-recur" dir))
(read-file [this dir path _options]
(let [path (concat-path dir path)]
(ipc/ipc "readFile" path)))

View File

@ -2,6 +2,7 @@
(defprotocol Fs
(mkdir! [this dir])
(mkdir-recur! [this dir])
(readdir [this dir])
(unlink! [this path opts])
(rmdir! [this dir])

View File

@ -193,3 +193,23 @@
{:title page-name
;; :date (date/get-date-time-string)
})
(defn fix-pages-timestamps
[pages]
(map (fn [{:block/keys [name created-at updated-at journal-day] :as p}]
(cond->
p
(nil? created-at)
(assoc :block/created-at
(if journal-day
(date/journal-day->ts journal-day)
(util/time-ms)))
(nil? updated-at)
(assoc :block/updated-at
;; Not exact true
(if journal-day
(date/journal-day->ts journal-day)
(util/time-ms)))))
pages))

View File

@ -42,6 +42,9 @@
(cond
alt-key?
(do
(editor-handler/set-block-property! (:block/uuid current-block)
:id
(str (:block/uuid current-block)))
(editor-handler/api-insert-new-block!
(util/format "((%s))" (str (:block/uuid current-block)))
{:block-uuid (:block/uuid target-block)

View File

@ -635,13 +635,17 @@
last-block-id (:db/id (last blocks))]
(when last-block-id
(db/pull last-block-id))))
format (or
(:block/format block)
(db/get-page-format (:db/id block))
:markdown)
content (if (seq properties)
(property/insert-properties format content properties)
content)
new-block (-> (select-keys block [:block/page :block/file :block/journal?
:block/journal-day])
(assoc :block/content content
:block/format (or
(:block/format block)
(db/get-page-format (:db/id block))
:markdown))
:block/format format)
(wrap-parse-block)
(assoc :block/uuid (db/new-block-id)))
new-block (if (:block/page new-block)
@ -649,9 +653,6 @@
(assoc new-block :block/page (:db/id block)))
new-block (if-let [db-id (:db/id (:block/file block))]
(assoc new-block :block/file db-id)
new-block)
new-block (if (and (map? properties) (seq properties))
(update new-block :block/properties (fn [m] (merge m properties)))
new-block)]
(let [[block-m sibling?] (cond
before?
@ -895,29 +896,31 @@
(property/insert-property format content key value))
block (outliner-core/block {:block/uuid block-id
:block/properties properties
:block/content content})]
:block/content content})
input-pos (or (state/get-edit-pos) :max)]
(outliner-core/save-node block)
(db/refresh! (state/get-current-repo)
{:key :block/change
:data [(db/pull [:block/uuid block-id])]})
;; update editing input content
(when-let [editing-block (state/get-edit-block)]
(and (= (:block/uuid editing-block) block-id)
(state/set-edit-content! (state/get-edit-input-id) content))))))))
(when (= (:block/uuid editing-block) block-id)
(edit-block! editing-block
input-pos
format
(state/get-edit-input-id)))))))))
(defn remove-block-property!
[block-id key]
(let [key (keyword key)]
(block-property-aux! block-id key nil))
(db/refresh! (state/get-current-repo)
{:key :block/change
:data [(db/pull [:block/uuid block-id])]}))
(block-property-aux! block-id key nil)))
(defn set-block-property!
[block-id key value]
(let [key (keyword key)]
(block-property-aux! block-id key value))
(db/refresh! (state/get-current-repo)
{:key :block/change
:data [(db/pull [:block/uuid block-id])]}))
(block-property-aux! block-id key value)))
(defn set-block-timestamp!
[block-id key value]
@ -1051,7 +1054,7 @@
(defn copy-selection-blocks
[]
(when-let [blocks (seq (get-selected-blocks-with-children))]
(let [repo (dom/attr (first blocks) "repo")
(let [repo (state/get-current-repo)
ids (->> (distinct (map #(when-let [id (dom/attr % "blockid")]
(uuid id)) blocks))
(remove nil?))
@ -1068,7 +1071,7 @@
;; remove embeds and references
(let [blocks (remove (fn [block] (= "true" (dom/attr block "data-transclude"))) blocks)]
(when (seq blocks)
(let [repo (dom/attr (first blocks) "repo")
(let [repo (state/get-current-repo)
ids (distinct (map #(uuid (dom/attr % "blockid")) blocks))]
(delete-blocks! repo ids))))))
@ -2937,7 +2940,8 @@
(remove-block-property! block-id :collapsed))
(defn expand!
[]
[e]
(util/stop e)
(cond
(state/editing?)
(when-let [block-id (:block/uuid (state/get-edit-block))]
@ -2970,7 +2974,8 @@
(expand-block! uuid)))))))))
(defn collapse!
[]
[e]
(util/stop e)
(cond
(state/editing?)
(when-let [block-id (:block/uuid (state/get-edit-block))]

View File

@ -300,6 +300,17 @@
(when-not file-exists?
(reset-file! repo-url path default-content)))))
(defn create-pages-metadata-file
[repo-url]
(let [repo-dir (config/get-repo-dir repo-url)
path (str config/app-name "/" config/pages-metadata-file)
file-path (str "/" path)
default-content "{}"]
(p/let [_ (fs/mkdir-if-not-exists (str repo-dir "/" config/app-name))
file-exists? (fs/create-if-not-exists repo-url repo-dir file-path default-content)]
(when-not file-exists?
(reset-file! repo-url path default-content)))))
(defn edn-file-set-key-value
[path k v]
(when-let [repo (state/get-current-repo)]

View File

@ -4,9 +4,13 @@
[cljs.reader :as reader]
[frontend.config :as config]
[frontend.db :as db]
[frontend.fs :as fs]
[datascript.db :as ddb]
[clojure.string :as string]
[promesa.core :as p]))
[promesa.core :as p]
[frontend.util :as util]
[frontend.date :as date]
[frontend.handler.common :as common-handler]))
(def default-metadata-str "{}")
@ -33,10 +37,25 @@
new-metadata (if encrypted?
(assoc new-metadata :db/encrypted? true)
new-metadata)
_ (prn "New metadata:\n" new-metadata)
new-content (pr-str new-metadata)]
(file-handler/set-file-content! repo path new-content))))))
(defn set-pages-metadata!
[repo]
(let [path (config/get-pages-metadata-path)
all-pages (->> (db/get-all-pages repo)
(common-handler/fix-pages-timestamps)
(map #(select-keys % [:block/name :block/created-at :block/updated-at]))
(vec))]
(-> (file-handler/create-pages-metadata-file repo)
(p/finally (fn []
(let [new-content (pr-str all-pages)]
(fs/write-file! repo
(config/get-repo-dir repo)
path
new-content
{})))))))
(defn set-db-encrypted-secret!
[encrypted-secret]
(when-not (string/blank? encrypted-secret)

View File

@ -450,14 +450,15 @@
(init-commands!)
(shortcut/refresh!))))
;; TODO: add use :file/last-modified-at
(defn get-pages-with-modified-at
(defn get-all-pages
[repo]
(->> (db/get-modified-pages repo)
(remove util/file-page?)
(remove util/uuid-string?)
(->> (db/get-all-pages)
(remove (fn [p]
(db/built-in-pages-names (string/upper-case p))))))
(let [name (:block/name p)]
(or (util/file-page? name)
(util/uuid-string? name)
(db/built-in-pages-names (string/upper-case name))))))
(common-handler/fix-pages-timestamps)))
(defn get-filters
[page-name]

View File

@ -184,6 +184,31 @@
(remove-non-exists-refs!))]
(db/transact! repo-url all-data)))
(defn- load-pages-metadata!
[repo file-paths files]
(try
(let [file (config/get-pages-metadata-path)]
(when (contains? (set file-paths) file)
(when-let [content (some #(when (= (:file/path %) file) (:file/content %)) files)]
(let [metadata (common-handler/safe-read-string content "Parsing pages metadata file failed: ")
pages (db/get-all-pages repo)
pages (zipmap (map :block/name pages) pages)
metadata (->>
(filter (fn [{:block/keys [name created-at updated-at]}]
(when-let [page (get pages name)]
(and
(or
(nil? (:block/created-at page))
(>= created-at (:block/created-at page)))
(or
(nil? (:block/updated-at page))
(>= updated-at (:block/created-at page)))))) metadata)
(remove nil?))]
(when (seq metadata)
(db/transact! repo metadata))))))
(catch js/Error e
(log/error :exception e))))
(defn- parse-files-and-create-default-files-inner!
[repo-url files delete-files delete-blocks file-paths first-clone? db-encrypted? re-render? re-render-opts metadata]
(let [parsed-files (filter
@ -200,6 +225,7 @@
(:file/content %)) files)]
(file-handler/restore-config! repo-url content true))))
(reset-contents-and-blocks! repo-url files blocks-pages delete-files delete-blocks)
(load-pages-metadata! repo-url file-paths files)
(when first-clone?
(if (and (not db-encrypted?) (state/enable-encryption? repo-url))
(state/pub-event! [:modal/encryption-setup-dialog repo-url

View File

@ -10,7 +10,8 @@
[clojure.set :as set]
[clojure.zip :as zip]
[frontend.modules.outliner.datascript :as ds]
[frontend.util :as util]))
[frontend.util :as util]
[frontend.util.property :as property]))
(defrecord Block [data])
@ -59,29 +60,6 @@
(outliner-state/get-by-parent-id repo [:block/uuid id])
(mapv block))))
;; TODO: we might need to store created-at and updated-at as datom attributes
;; instead of being attributes of properties.
;; which might improve the db performance, we can improve it later
(defn- with-timestamp
[m]
(let [updated-at (util/time-ms)
properties (assoc (:block/properties m)
:id (:block/uuid m)
:updated-at updated-at)
properties (if-let [created-at (get properties :created-at)]
properties
(assoc properties :created-at updated-at))
m (assoc m :block/properties properties)
page-id (or (get-in m [:block/page :db/id])
(:db/id (:block/page (db/entity (:db/id m)))))
page (db/entity page-id)
page-properties (:block/properties page)
page-tx {:db/id page-id
:block/properties (assoc page-properties
:updated-at updated-at
:created-at (get page-properties :created-at updated-at))}]
[m page-tx]))
(defn- update-block-unordered
[block]
(let [parent (:block/parent block)
@ -91,6 +69,19 @@
(assoc block :block/unordered false)
(assoc block :block/unordered true))))
(defn- block-with-timestamps
[block]
(let [updated-at (util/time-ms)
block (cond->
(assoc block :block/updated-at updated-at)
(nil? (:block/created-at block))
(assoc :block/created-at updated-at))
content (property/insert-properties (:block/format block)
(or (:block/content block) "")
{:created-at (:block/created-at block)
:updated-at (:block/updated-at block)})]
(assoc block :block/content content)))
;; -get-id, -get-parent-id, -get-left-id return block-id
;; the :block/parent, :block/left should be datascript lookup ref
@ -143,26 +134,32 @@
m (-> (:data this)
(dissoc :block/children :block/meta)
(util/remove-nils))
other-tx (:db/other-tx m)]
m (if (state/enable-block-timestamps?) (block-with-timestamps m) m)
other-tx (:db/other-tx m)
id (:db/id (:data this))]
(when (seq other-tx)
(swap! txs-state (fn [txs]
(vec (concat txs other-tx)))))
(when-let [id (:db/id (:data this))]
(when id
(swap! txs-state (fn [txs]
(vec
(concat txs
(map (fn [attribute]
[:db/retract id attribute])
db-schema/retract-attributes))))))
db-schema/retract-attributes)))))
(when-let [e (:block/page (db/entity id))]
(let [m {:db/id (:db/id e)
:block/updated-at (util/time-ms)}
m (if (:block/created-at e)
m
(assoc m :block/created-at (util/time-ms)))]
(swap! txs-state conj m))))
(swap! txs-state conj (dissoc m :db/other-tx))
this
;; TODO: enable for the database-only version
;; (let [[m page-tx] (with-timestamp (:data this))]
;; (swap! txs-state conj m page-tx)
;; m)
)
)
this))
(-del [this txs-state children?]
(assert (ds/outliner-txs-state? txs-state)

View File

@ -260,13 +260,10 @@
(not (false? (:git-auto-push
(get (sub-config) repo)))))
(defn enable-block-time?
(defn enable-block-timestamps?
[]
;; (true? (:feature/enable-block-time?
;; (get (sub-config) (get-current-repo))))
;; Disable block timestamps for now, because it doesn't work with undo/redo
false)
(true? (:feature/enable-block-timestamps?
(get (sub-config) (get-current-repo)))))
(defn sub-graph-config
[]
@ -551,7 +548,8 @@
(defn get-edit-pos
[]
(.-selectionStart (get-input)))
(when-let [input (get-input)]
(.-selectionStart input)))
(defn set-selection-start-block!
[start-block]

View File

@ -14,7 +14,7 @@
(def built-in-properties
(set/union
#{:id :custom-id :background-color :heading :collapsed :created-at :last-modified-at :created_at :last_modified_at}
#{:id :custom-id :background-color :heading :collapsed :created-at :updated-at :last-modified-at :created_at :last_modified_at}
(set (map keyword config/markers))))
(defn properties-built-in?
@ -226,7 +226,21 @@
(defn insert-properties
[format content kvs]
(reduce (fn [content [k v]] (insert-property format content k v)) content kvs))
(reduce
(fn [content [k v]]
(let [k (if (string? k)
(keyword (-> (string/lower-case k)
(string/replace " " "-")))
k)
v (if (coll? v)
(some->>
(seq v)
(distinct)
(map (fn [item] (util/format "[[%s]]" (text/page-ref-un-brackets! item))))
(string/join ", "))
v)]
(insert-property format content k v)))
content kvs))
(defn remove-property
([format key content]
@ -271,7 +285,9 @@
(let [[k v] (util/split-first ":" (subs text 1))]
(if (and k v)
(let [k (string/replace k "_" "-")
k (if (contains? #{:id :custom_id :custom-id} (string/lower-case k)) "id" k)]
compare-k (keyword (string/lower-case k))
k (if (contains? #{:id :custom_id :custom-id} compare-k) "id" k)
k (if (contains? #{:last-modified-at} compare-k) "updated-at" k)]
(str k ":: " (string/trim v)))
text)))))
after (subvec lines (inc end-idx))

View File

@ -1,3 +1,3 @@
(ns frontend.version)
(defonce version "0.2.3-1")
(defonce version "0.2.4")

View File

@ -93,16 +93,84 @@
path (util/node-path.join path "package.json")]
(fs/write-file! repo "" path (js/JSON.stringify data nil 2) {:skip-mtime? true}))))
(defn ^:private write_dotdir_file!
[file content sub-root]
(p/let [repo ""
path (plugin-handler/get-ls-dotdir-root)
path (util/node-path.join path sub-root)
exist? (fs/file-exists? path "")
_ (when-not exist? (fs/mkdir-recur! path))
user-path (util/node-path.join path file)
sub-dir? (string/starts-with? user-path path)
_ (if-not sub-dir? (do (log/info :debug user-path) (throw "write file denied")))
user-path-root (util/node-path.dirname user-path)
exist? (fs/file-exists? user-path-root "")
_ (when-not exist? (fs/mkdir-recur! user-path-root))
_ (fs/write-file! repo "" user-path content {:skip-mtime? true})]
user-path))
(defn ^:private read_dotdir_file
[file sub-root]
(p/let [repo ""
path (plugin-handler/get-ls-dotdir-root)
path (util/node-path.join path sub-root)
user-path (util/node-path.join path file)
sub-dir? (string/starts-with? user-path path)
_ (if-not sub-dir? (do (log/info :debug user-path) (throw "read file denied")))
exist? (fs/file-exists? "" user-path)
_ (when-not exist? (do (log/info :debug user-path) (throw "file not existed")))
content (fs/read-file "" user-path)]
content))
(defn ^:private unlink_dotdir_file!
[file sub-root]
(p/let [repo ""
path (plugin-handler/get-ls-dotdir-root)
path (util/node-path.join path sub-root)
user-path (util/node-path.join path file)
sub-dir? (string/starts-with? user-path path)
_ (if-not sub-dir? (do (log/info :debug user-path) (throw "access file denied")))
exist? (fs/file-exists? "" user-path)
_ (when-not exist? (do (log/info :debug user-path) (throw "file not existed")))
_ (fs/unlink! user-path {})]))
(def ^:export write_user_tmp_file
(fn [file content]
(p/let [repo ""
path (plugin-handler/get-ls-dotdir-root)
path (util/node-path.join path "tmp")
exist? (fs/file-exists? path "")
_ (when-not exist? (fs/mkdir! path))
path (util/node-path.join path file)
_ (fs/write-file! repo "" path content {:skip-mtime? true})]
path)))
(write_dotdir_file! file content "tmp")))
(def ^:export write_plugin_storage_file
(fn [plugin-id file content]
(write_dotdir_file!
file content
(let [plugin-id (util/node-path.basename plugin-id)]
(util/node-path.join "storages" plugin-id)))))
(def ^:export read_plugin_storage_file
(fn [plugin-id file]
(let [plugin-id (util/node-path.basename plugin-id)]
(read_dotdir_file
file (util/node-path.join "storages" plugin-id)))))
(def ^:export unlink_plugin_storage_file
(fn [plugin-id file]
(let [plugin-id (util/node-path.basename plugin-id)]
(unlink_dotdir_file!
file (util/node-path.join "storages" plugin-id)))))
(def ^:export exist_plugin_storage_file
(fn [plugin-id file]
(p/let [root (plugin-handler/get-ls-dotdir-root)
plugin-id (util/node-path.basename plugin-id)
exist? (fs/file-exists?
(util/node-path.join root "storages" plugin-id)
file)]
exist?)))
(def ^:export clear_plugin_storage_files
(fn [plugin-id]
(p/let [root (plugin-handler/get-ls-dotdir-root)
plugin-id (util/node-path.basename plugin-id)]
(fs/rmdir! (util/node-path.join root "storages" plugin-id)))))
(def ^:export load_user_preferences
(fn []

View File

@ -77,6 +77,29 @@
#+END_QUOTE" "c" "d")
"c:: d\n#+BEGIN_QUOTE\n hello world\n #+END_QUOTE"))
(deftest test-insert-properties
(are [x y] (= x y)
(property/insert-properties :markdown "" {:foo "bar"})
"foo:: bar"
(property/insert-properties :markdown "" {"foo" "bar"})
"foo:: bar"
(property/insert-properties :markdown "" {"foo space" "bar"})
"foo-space:: bar"
(property/insert-properties :markdown "" {:foo #{"bar" "baz"}})
"foo:: [[bar]], [[baz]]"
(property/insert-properties :markdown "" {:foo ["bar" "bar" "baz"]})
"foo:: [[bar]], [[baz]]"
(property/insert-properties :markdown "a\nb\n" {:foo ["bar" "bar" "baz"]})
"a\nfoo:: [[bar]], [[baz]]\nb"
(property/insert-properties :markdown "" {:foo "\"bar, baz\""})
"foo:: \"bar, baz\""))
(deftest test->new-properties
(are [x y] (= (property/->new-properties x) y)
":PROPERTIES:\n:foo: bar\n:END:"

View File

@ -9,13 +9,6 @@
;; or ":todo" for TODO/DOING style.
:preferred-workflow :now
;; Git is only available in web app
;; Git settings
:git-pull-secs 60
:git-push-secs 10
;; Whether to enable git auto push
:git-auto-push true
;; The app will ignore those directories or files.
;; E.g. "/archived" "/test.md"
:hidden []
@ -30,6 +23,8 @@
;; Default is true, you can also toggle this via setting page
:ui/enable-tooltip? true
:feature/enable-block-timestamps? false
;; Specify a custom CSS import
;; This option take precedence over your local `logseq/custom.css` file
;; You may find a list of awesome logseq themes here: