mirror of https://github.com/logseq/logseq
Merge branch 'master' into zotero/poc
commit
828e296f86
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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')
|
||||
},
|
||||
|
|
|
@ -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
|
@ -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.",
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")]]])]]))])))
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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?)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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 %)))
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
(defprotocol Fs
|
||||
(mkdir! [this dir])
|
||||
(mkdir-recur! [this dir])
|
||||
(readdir [this dir])
|
||||
(unlink! [this path opts])
|
||||
(rmdir! [this dir])
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))]
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
(ns frontend.version)
|
||||
|
||||
(defonce version "0.2.3-1")
|
||||
(defonce version "0.2.4")
|
||||
|
|
|
@ -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 []
|
||||
|
|
|
@ -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:"
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue