Merge branch 'master' into feat/outliner-core

pull/1656/head
Tienson Qin 2021-03-24 21:28:47 +08:00
commit 8f05fb66b7
18 changed files with 232 additions and 64 deletions

9
.clj-kondo/config.edn Normal file
View File

@ -0,0 +1,9 @@
{:linters {:unresolved-symbol {:exclude [goog.DEBUG]}}
:hooks {:analyze-call {rum.core/defc hooks.rum/defc
rum.core/defcs hooks.rum/defcs}}
:lint-as {promesa.core/let clojure.core/let
garden.def/defstyles clojure.core/def
garden.def/defkeyframes clojure.core/def
rum.core/defcc rum.core/defc
clojure.test.check.clojure-test/defspec clojure.core/def
clojure.test.check.properties/for-all clojure.core/for}}

68
.clj-kondo/hooks/rum.clj Normal file
View File

@ -0,0 +1,68 @@
(ns hooks.rum
(:require [clj-kondo.hooks-api :as api]))
(defn fn-body? [x]
(and (seq? x)
(vector? (first x))))
(defn rewrite-body [mixins body defcs?]
(if defcs?
(let [[binding-vec & body] (:children body)
[state-arg & rest-args] (:children binding-vec)
;; the original vector without the state argument
fn-args (assoc binding-vec :children rest-args)
body (api/list-node
(list* (api/token-node 'let*)
(api/vector-node [state-arg (api/token-node nil)])
state-arg
(concat mixins body)))
body (api/list-node [fn-args body])]
body)
(let [[binding-vec & body] (:children body)]
(api/list-node (cons binding-vec (concat mixins body))))))
(defn rewrite
([node] (rewrite node false))
([node defcs?]
(let [args (rest (:children node))
component-name (first args)
?docstring (when (string? (api/sexpr (second args)))
(second args))
args (if ?docstring
(nnext args)
(next args))
bodies
(loop [args* (seq args)
mixins []
bodies []]
(if args*
(let [a (first args*)
a-sexpr (api/sexpr a)]
(cond (vector? a-sexpr) ;; a-sexpr is a binding vec and the rest is the body of the function
[(rewrite-body mixins (api/list-node args*) defcs?)]
(fn-body? a-sexpr)
(recur (next args*)
mixins
(conj bodies (rewrite-body mixins a defcs?)))
;; assume mixin
:else (recur (next args*)
(conj mixins a)
bodies)))
bodies))
new-node (with-meta
(api/list-node
(list* (api/token-node 'defn)
component-name
(if ?docstring
(cons ?docstring bodies)
bodies)))
(meta node))]
new-node)))
(defn defc [{:keys [:node]}]
(let [new-node (rewrite node)]
{:node new-node}))
(defn defcs [{:keys [:node]}]
(let [new-node (rewrite node true)]
{:node new-node}))

2
.gitignore vendored
View File

@ -29,5 +29,5 @@ strings.csv
.calva
resources/electron.js
.clj-kondo/
.clj-kondo/.cache
.lsp/

View File

@ -7,12 +7,12 @@
;; persistent-sorted-set {:mvn/version "0.1.2"}
;; FIXME: doesn't work on my archlinux laptop (tienson)
;; The required namespace "datascript.core" is not available, it was required by "frontend/db.cljs".
datascript/datascript {:git/url "https://github.com/tiensonqin/datascript",
:sha "efde8d389e6703b6f60ca3538f484a579b0d6de0"}
datascript/datascript {:git/url "https://github.com/logseq/datascript",
:sha "5c1983cdfbdaa4ba6f8410b54853ea3a78a0cd8c"}
;; datascript {:mvn/version "1.0.1"}
datascript-transit/datascript-transit
{:mvn/version "0.3.0"
:exclusions [datascript]}
:exclusions [datascript/datascript]}
borkdude/rewrite-edn {:git/url "https://github.com/borkdude/rewrite-edn"
:sha "edd87dc7f045f28d7afcbfc44bc0f0a2683dde62"}
funcool/promesa {:mvn/version "4.0.2"}

View File

@ -14,7 +14,10 @@ const resourceFilePath = path.join(resourcesPath, '**')
const css = {
watchCSS () {
return exec(`yarn css:watch`, {})
return cp.spawn(`yarn css:watch`, {
shell: true,
stdio: 'inherit'
})
},
buildCSS (...params) {

View File

@ -71,8 +71,9 @@
"fuse.js": "^6.4.6",
"gulp-cached": "^1.1.1",
"ignore": "^5.1.8",
"is-svg": "4.2.2",
"jszip": "^3.5.0",
"mldoc": "0.5.5",
"mldoc": "0.5.8",
"mousetrap": "^1.6.5",
"path": "^0.12.7",
"react": "^17.0.1",

View File

@ -20,7 +20,8 @@
"node-fetch": "^2.6.1",
"open": "^7.3.1",
"chokidar": "^3.5.1",
"fs-extra": "^9.1.0"
"fs-extra": "^9.1.0",
"electron-window-state": "^5.0.3"
},
"devDependencies": {
"@electron-forge/cli": "^6.0.0-beta.54",

View File

@ -7,6 +7,7 @@
["fs-extra" :as fs]
["path" :as path]
["electron" :refer [BrowserWindow app protocol ipcMain dialog] :as electron]
["electron-window-state" :as windowStateKeeper]
[clojure.core.async :as async]
[electron.state :as state]))
@ -22,8 +23,9 @@
(defn create-main-window
"Creates main app window"
[]
(let [win-opts {:width 980
:height 700
(let [win-state (windowStateKeeper (clj->js {:defaultWidth 980 :defaultHeight 700}))
win-opts {:width (.-width win-state)
:height (.-height win-state)
:frame (not mac?)
:autoHideMenuBar (not mac?)
:titleBarStyle (if mac? "hidden" nil)
@ -35,6 +37,7 @@
:preload (path/join js/__dirname "js/preload.js")}}
url MAIN_WINDOW_ENTRY
win (BrowserWindow. (clj->js win-opts))]
(.manage win-state win)
(.loadURL win url)
(when dev? (.. win -webContents (openDevTools)))
win))

View File

@ -147,6 +147,11 @@
["Embed Vimeo Video" [[:editor/input "{{vimeo }}" {:last-pattern slash
:backward-pos 2}]]]
(when (state/markdown?)
["Underline" [[:editor/input "<ins></ins>"
{:last-pattern slash
:backward-pos 6}]]])
["Html Inline " (->inline "html")]
;; TODO:

View File

@ -326,7 +326,7 @@
(declare page-reference)
(defn page-cp
(rum/defc page-cp
[{:keys [html-export? label children contents-page?] :as config} page]
(when-let [page-name (:block/name page)]
(let [source-page (model/get-alias-source-page (state/get-current-repo)
@ -572,22 +572,22 @@
["Subscript" l]
(->elem :sub (map-inline config l))
["Tag" s]
(if (and s (util/tag-valid? 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)]
[:span.warning.mr-1 {:title "Invalid tag, tags only accept alphanumeric characters, \"-\", \"_\", \"@\" and \"%\"."}
(str "#" s)])
(when s
(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)]))
["Emphasis" [[kind] data]]
(let [elem (case kind
"Bold" :b
@ -736,6 +736,12 @@
(-> (safe-read-string s)
(security/remove-javascript-links-in-href)))
["Inline_Html" s]
(when (not html-export?)
;; TODO: how to remove span and only export the content of `s`?
[:span {:dangerouslySetInnerHTML
{:__html s}}])
["Break_Line"]
[:br]
["Hard_Break_Line"]
@ -1257,26 +1263,36 @@
[:div [:h1 (:page-name config)]]
block-cp))))
(rum/defc span-comma
[]
[:span ", "])
(rum/defc property-cp
[config block k v]
[:div.my-1
[:b k]
[:span.mr-1 ":"]
(if (coll? v)
(let [v (->> (remove string/blank? v)
(filter string?))
vals (for [v-item v]
(page-cp config {:block/name v-item}))
elems (interpose (span-comma) vals)]
(for [elem elems]
(rum/with-key elem (str (random-uuid)))))
(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)))))])
(rum/defc properties-cp
[config block]
(let [properties (apply dissoc (:block/properties block) text/hidden-properties)]
(when (seq properties)
[:div.blocks-properties.text-sm.opacity-80.my-1.p-2
(for [[k v] properties]
^{:key (str (:block/uuid block) "-" k)}
[:div.my-1
[:b k]
[:span.mr-1 ":"]
(if (coll? v)
(let [v (->> (remove string/blank? v)
(filter string?))
vals (for [v-item v]
(page-cp config {:block/name v-item}))]
(interpose [:span ", "] vals))
(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)))))])])))
(rum/with-key (property-cp config block k v)
(str (:block/uuid block) "-" k)))])))
(rum/defcs timestamp-cp < rum/reactive
(rum/local false ::show?)

View File

@ -51,7 +51,7 @@
[:div.py-1.rounded-md.bg-base-3.shadow-xs
(ui/menu-link
{:key "cut"
:on-click editor-handler/cut-selection-blocks}
:on-click #(editor-handler/cut-selection-blocks true)}
"Cut")
(ui/menu-link
{:key "copy"
@ -254,8 +254,8 @@
(defn- cut-blocks-and-clear-selections!
[_]
(editor-handler/cut-selection-blocks)
[copy?]
(editor-handler/cut-selection-blocks copy?)
(editor-handler/clear-selection! nil))
(rum/defc hidden-selection < rum/reactive
@ -264,11 +264,11 @@
(editor-handler/copy-selection-blocks)
(editor-handler/clear-selection! nil)))
(mixins/keyboard-mixin (util/->system-modifier "ctrl+x")
cut-blocks-and-clear-selections!)
(fn [] (cut-blocks-and-clear-selections! true)))
(mixins/keyboard-mixin "backspace"
cut-blocks-and-clear-selections!)
(fn [] (cut-blocks-and-clear-selections! false)))
(mixins/keyboard-mixin "delete"
cut-blocks-and-clear-selections!)
(fn [] (cut-blocks-and-clear-selections! false)))
[]
[:div#selection.hidden])

View File

@ -28,3 +28,17 @@
[:a#download-as-html.hidden]
[:a#download-as-zip.hidden]
[:a#export-as-markdown.hidden]])))
(rum/defc export-page
[]
(when-let [current-repo (state/get-current-repo)]
(when-let [page (state/get-current-page)]
(rum/with-context [[t] i18n/*tongue-context*]
[:div.export.w-96
[:h1.title "Export"]
[:ul.mr-1
[:li.mb-4
[:a.font-medium {:on-click #(export/export-page-as-markdown! page)}
(t :export-markdown)]]]
[:a#export-page-as-markdown.hidden]]))))

View File

@ -16,6 +16,7 @@
[frontend.components.editor :as editor]
[frontend.components.reference :as reference]
[frontend.components.svg :as svg]
[frontend.components.export :as export]
[frontend.extensions.graph-2d :as graph-2d]
[frontend.ui :as ui]
[frontend.components.content :as content]
@ -313,6 +314,10 @@
{: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 (util/electron?)
{:title (t (if public? :page/make-private :page/make-public))
:options {:on-click

View File

@ -271,6 +271,7 @@
@conn)
(into {}))))
(defn get-files-full
[repo]
(when-let [conn (conn/get-files-conn repo)]
@ -705,7 +706,8 @@
(defn get-page-file
[page-name]
(some-> (db-utils/entity [:block/name page-name])
(some-> (or (db-utils/entity [:block/name page-name])
(db-utils/entity [:block/original-name page-name]))
:block/file))
(defn get-block-file

View File

@ -1201,8 +1201,8 @@
(common-handler/copy-to-clipboard-without-id-property! content)))))
(defn cut-selection-blocks
[]
(copy-selection-blocks)
[copy?]
(when copy? (copy-selection-blocks))
(when-let [blocks (seq (get-selected-blocks-with-children))]
(let [repo (dom/attr (first blocks) "repo")
ids (distinct (map #(uuid (dom/attr % "blockid")) blocks))]

View File

@ -178,11 +178,17 @@
(db/pull-many repo '[*] block-ids)
pages-name-and-content
(->> page-ids
(d/q '[:find ?n (pull ?p [:file/path])
(d/q '[:find ?n ?n2 (pull ?p [:file/path])
:in $ [?e ...]
:where
[?e :block/file ?p]
[?e :block/name ?n]] (db/get-conn repo))
[?e :block/name ?n]
[?e :block/original-name ?n2]] (db/get-conn repo))
(mapv (fn [[name origin-name file-path]]
(if (= name origin-name)
[[name file-path]]
[[name file-path] [origin-name file-path]])))
(apply concat)
(mapv (fn [[page-name file-path]] [page-name (:file/path file-path)]))
(d/q '[:find ?n ?c
:in $ [[?n ?p] ...]
@ -199,23 +205,46 @@
{:embed_blocks embed-blocks
:embed_pages pages-name-and-content}))
(defn- export-files-as-markdown
[repo files heading-to-list?]
(->> files
(mapv (fn [{:keys [path content names format]}]
(when (first names)
[path (fp/exportMarkdown f/mldoc-record content
(f/get-default-config format heading-to-list?)
(js/JSON.stringify
(clj->js (get-embed-and-refs-blocks-pages repo (first names)))))])))
(remove nil?)))
(defn export-repo-as-markdown!
[repo]
(when-let [repo (state/get-current-repo)]
(when-let [files (get-file-contents-with-suffix repo)]
(let [heading-to-list? (state/export-heading-to-list?)
files
(->> files
(mapv (fn [{:keys [path content names format]}]
(when (first names)
[path (fp/exportMarkdown f/mldoc-record content
(f/get-default-config format heading-to-list?)
(js/JSON.stringify
(clj->js (get-embed-and-refs-blocks-pages repo (first names)))))])))
(remove nil?))
(export-files-as-markdown repo files heading-to-list?)
zip-file-name (str repo "_markdown_" (quot (util/time-ms) 1000))]
(p/let [zipfile (zip/make-zip zip-file-name files)]
(when-let [anchor (gdom/getElement "export-as-markdown")]
(.setAttribute anchor "href" (js/window.URL.createObjectURL zipfile))
(.setAttribute anchor "download" (.-name zipfile))
(.click anchor)))))))
(defn export-page-as-markdown!
[page-name]
(when-let [repo (state/get-current-repo)]
(when-let [file (db/get-page-file page-name)]
(when-let [path (:file/path file)]
(when-let [content (db/get-file path)]
(let [names [page-name]
format (f/get-format path)
files [{:path path :content content :names names :format format}]]
(let [files
(export-files-as-markdown repo files (state/export-heading-to-list?))]
(let [data (js/Blob. [(second (first files))]
(clj->js {:type "text/plain;charset=utf-8,"}))]
(let [anchor (gdom/getElement "export-page-as-markdown")
url (js/window.URL.createObjectURL data)]
(.setAttribute anchor "href" url)
(.setAttribute anchor "download" path)
(.click anchor))))))))))

View File

@ -250,6 +250,11 @@
(get-in @state [:me :preferred_format] "markdown")))))
(defn markdown?
[]
(= (keyword (get-preferred-format))
:markdown))
(defn get-pages-directory
[]
(or

View File

@ -2794,7 +2794,7 @@ hsla-regex@^1.0.0:
resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38"
integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg=
html-comment-regex@^1.1.0:
html-comment-regex@^1.1.0, html-comment-regex@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7"
integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==
@ -3221,6 +3221,13 @@ is-stream@^1.1.0:
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
is-svg@4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-4.2.2.tgz#a4ea0f3f78dada7085db88f1e85b6f845626cfae"
integrity sha512-JlA7Mc7mfWjdxxTkJ094oUK9amGD7gQaj5xA/NCY0vlVvZ1stmj4VX+bRuwOMN93IHRZ2ctpPH/0FO6DqvQ5Rw==
dependencies:
html-comment-regex "^1.1.2"
is-svg@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75"
@ -3846,10 +3853,10 @@ mkdirp@^0.5.4, mkdirp@~0.5.1:
dependencies:
minimist "^1.2.5"
mldoc@0.5.5:
version "0.5.5"
resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-0.5.5.tgz#cb7eea471adc94e1c7858d4ae772ddabe0a75753"
integrity sha512-acseZvvwzLNlvp6/fZzqP5rqS80keWOK1XCreem5eXJNxLtJm+xIqzEJVcsmeyFS1Leya1gPT7dMcCUOhEr26g==
mldoc@0.5.6:
version "0.5.6"
resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-0.5.6.tgz#aa4351791e11b9c8a8df359a9d87619d7ff02e5d"
integrity sha512-iRTuTmLUdR8OKiRrrM4dl+51jxAmOJ92+B3rsYuKZERaYcU0B9jiJBT9S2nDZldARNEwjhj8DKITdnJZMxArGQ==
dependencies:
yargs "^12.0.2"