瀏覽代碼

Refactor to use deps.edn

Tienson Qin 5 年之前
父節點
當前提交
db73251f82
共有 53 個文件被更改,包括 21 次插入3467 次删除
  1. 4 2
      .gitignore
  2. 7 8
      .projectile
  3. 4 0
      bin/build
  4. 0 89
      dev/user.clj
  5. 3 4
      package.json
  6. 1 1
      procfile
  7. 2 22
      shadow-cljs.edn
  8. 0 23
      web/.gitignore
  9. 0 16
      web/deps.edn
  10. 0 31
      web/dev/shadow/hooks.clj
  11. 0 7
      web/dev/shadow/user.clj
  12. 二進制
      web/images/screenshot.png
  13. 0 100
      web/public/css/highlight.css
  14. 0 0
      web/public/css/org.css
  15. 0 278
      web/public/css/style.css
  16. 0 0
      web/public/css/tailwind.min.css
  17. 二進制
      web/public/favicon.ico
  18. 0 3
      web/public/img/angled-background.svg
  19. 二進制
      web/public/img/hero-pattern-lg.png
  20. 二進制
      web/public/img/logo.png
  21. 0 1
      web/public/js/highlight.pack.js
  22. 0 29
      web/src/frontend/blob.cljs
  23. 0 104
      web/src/frontend/components/agenda.cljs
  24. 0 27
      web/src/frontend/components/content.cljs
  25. 0 86
      web/src/frontend/components/file.cljs
  26. 0 81
      web/src/frontend/components/home.cljs
  27. 0 105
      web/src/frontend/components/journal.cljs
  28. 0 42
      web/src/frontend/components/repo.cljs
  29. 0 51
      web/src/frontend/components/settings.cljs
  30. 0 157
      web/src/frontend/components/sidebar.cljs
  31. 0 14
      web/src/frontend/config.cljs
  32. 0 40
      web/src/frontend/core.cljs
  33. 0 483
      web/src/frontend/db.cljs
  34. 0 54
      web/src/frontend/exif.js
  35. 0 18
      web/src/frontend/format.cljs
  36. 0 13
      web/src/frontend/format/markdown.cljs
  37. 0 99
      web/src/frontend/format/org/block.cljs
  38. 0 48
      web/src/frontend/format/org_mode.cljs
  39. 0 4
      web/src/frontend/format/protocol.cljs
  40. 0 44
      web/src/frontend/fs.cljs
  41. 0 133
      web/src/frontend/git.cljs
  42. 0 529
      web/src/frontend/handler.cljs
  43. 0 103
      web/src/frontend/image.cljs
  44. 0 117
      web/src/frontend/mixins.cljs
  45. 0 16
      web/src/frontend/page.cljs
  46. 0 37
      web/src/frontend/routes.cljs
  47. 0 59
      web/src/frontend/rum.cljs
  48. 0 12
      web/src/frontend/state.cljs
  49. 0 20
      web/src/frontend/storage.cljs
  50. 0 142
      web/src/frontend/ui.cljs
  51. 0 31
      web/src/frontend/utf8.cljs
  52. 0 184
      web/src/frontend/util.cljs
  53. 0 0
      yarn.lock

+ 4 - 2
.gitignore

@@ -11,8 +11,10 @@ pom.xml.asc
 .hgignore
 .hg/
 
-web/node_modules/
-web/public/js/main.js
+node_modules/
+resources/public/js/main.js
+resources/public/js/cljs-runtime
+resources/public/js/manifest.edn
 
 /.cpcache
 /target

+ 7 - 8
.projectile

@@ -1,9 +1,8 @@
 -/.git
--/api/.cpcache
--/api/.shadow-cljs/
--/api/node_modules/
--/web/.cpcache
--/web/.shadow-cljs/
--/web/node_modules/
--/web/public/js/cljs-runtime/
--/web/public/js/main.js
+-/.cpcache
+-/.shadow-cljs/
+-/node_modules/
+-/.cpcache
+-/.shadow-cljs/
+-/resources/public/js/cljs-runtime/
+-/resources/public/js/main.js

+ 4 - 0
bin/build

@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+yarn install
+yarn release
+clj -A:uberdeps

+ 0 - 89
dev/user.clj

@@ -1,89 +0,0 @@
-(ns user
-  (:require [com.stuartsierra.component :as component]
-            [clojure.tools.namespace.repl :as namespace]
-            [backend.config :as config]
-            [backend.db-migrate :as migrate]
-            [io.pedestal.service-tools.dev :as dev]
-            [clj-time
-             [coerce :as tc]
-             [core :as t]]
-            [clojure.java.io :as io]
-            [clojure.string :as string]))
-
-(namespace/disable-reload!)
-(namespace/set-refresh-dirs "src" "dev")
-(defonce *system (atom nil))
-(defonce *db (atom nil))
-
-(defn migrate []
-  (migrate/migrate @*db))
-
-(defn rollback []
-  (migrate/rollback @*db))
-
-(defn stop []
-  (some-> @*system (component/stop))
-  (reset! *system nil))
-
-(defn refresh []
-  (let [res (namespace/refresh)]
-    (when (not= res :ok)
-      (throw res))
-    :ok))
-
-(defn go
-  []
-  (require 'backend.core)
-  (dev/watch)
-  (when-some [f (resolve 'backend.system/new-system)]
-    (when-some [system (f config/config)]
-      (when-some [system' (component/start system)]
-        (reset! *system system')
-        (reset! *db {:datasource (get-in @*system [:hikari :datasource])}))))
-  (migrate))
-
-(defn reset []
-  (stop)
-  (refresh)
-  (go))
-
-(defn get-unix-timestamp []
-  (tc/to-long (t/now)))
-
-(def date-format
-  "Format for DateTime"
-  "yyyyMMddHHmmss")
-(def migrations-dir
-  "Default migrations directory"
-  "resources/migrations/")
-(def ragtime-format-edn
-  "EDN template for SQL migrations"
-  "{:up [\"\"]\n :down [\"\"]}")
-
-(defn migrations-dir-exist?
-  "Checks if 'resources/migrations' directory exists"
-  []
-  (.isDirectory (io/file migrations-dir)))
-
-(defn now
-  "Gets the current DateTime"  []
-  (.format (java.text.SimpleDateFormat. date-format) (new java.util.Date)))
-
-(defn migration-file-path
-  "Complete migration file path"
-  [name]
-  (str migrations-dir (now) "_" (string/replace name #"\s+|-+|_+" "_") ".edn"))
-
-(defn create-migration
-  "Creates a migration file with the current DateTime"
-  [name]
-  (let [migration-file (migration-file-path name)]
-    (if-not (migrations-dir-exist?)
-      (io/make-parents migration-file))
-    (spit migration-file ragtime-format-edn)))
-
-(defn reset-db
-  []
-  (dotimes [i 100]
-    (rollback))
-  (migrate))

+ 3 - 4
web/package.json → package.json

@@ -6,10 +6,9 @@
     "shadow-cljs": "2.8.81"
   },
   "scripts": {
-    "watch": "npx shadow-cljs watch app",
-    "release": "npx shadow-cljs release app",
-    "server": "npx shadow-cljs server;",
-    "clean": "rm -rf target; rm -rf public/js/compiled; rm -rf public/js/cljs-runtime"
+    "watch": "clj -A:cljs watch app",
+    "release": "clj -A:cljs release app",
+    "clean": "rm -rf target; rm -rf resources/public/js/compiled; rm -rf resources/public/js/cljs-runtime"
   },
   "dependencies": {
     "browserfs": "^1.4.3",

+ 1 - 1
procfile

@@ -1 +1 @@
-web: java -Dclojure.main.report=stderr -cp target/uberjar/logseq.jar clojure.main -m backend.core
+web: java -Dclojure.main.report=stderr -cp target/logseq.jar clojure.main -m app.core

+ 2 - 22
web/shadow-cljs.edn → shadow-cljs.edn

@@ -1,18 +1,5 @@
 ;; shadow-cljs configuration
 {:deps     true
-
- ;; :dependencies
- ;; [[binaryage/devtools "0.9.10"]
- ;;  [cider/cider-nrepl "0.23.0-SNAPSHOT"]
-
- ;;  [rum "0.11.4"]
- ;;  [datascript "0.18.9"]
- ;;  [funcool/promesa "4.0.2"]
- ;;  [medley "1.2.0"]
- ;;  [metosin/reitit "0.3.10"]
- ;;  [metosin/reitit-spec "0.3.10"]
- ;;  [metosin/reitit-frontend "0.3.10"]]
-
  :nrepl        {:port 8701}
 
  :builds
@@ -20,7 +7,7 @@
   {:target :browser
    :modules {:main {:init-fn frontend.core/init}}
 
-   :output-dir "public/js"
+   :output-dir "resources/public/js"
    :asset-path "/js"
 
    :compiler-options {:infer-externs :auto
@@ -35,11 +22,4 @@
    {:before-load frontend.core/stop
     ;; after live-reloading finishes call this function
     :after-load frontend.core/start
-    ;; serve the public directory over http at port 8700
-                                        ;:http-root   "public"
-                                        ;:http-port   8700
-    ;; :http-root    "public"
-    ;; :http-port    8080
-    :preloads     [devtools.preload]}
-
-   }}}
+    :preloads     [devtools.preload]}}}}

+ 0 - 23
web/.gitignore

@@ -1,23 +0,0 @@
-node_modules/
-public/js/cljs-runtime
-public/js/main.js
-public/js/manifest.edn
-
-/.cpcache
-/target
-/checkouts
-/src/gen
-
-pom.xml
-pom.xml.asc
-*.iml
-*.jar
-*.log
-.shadow-cljs
-.idea
-.lein-*
-.nrepl-*
-.DS_Store
-
-.hgignore
-.hg/

+ 0 - 16
web/deps.edn

@@ -1,16 +0,0 @@
-{:deps
- {;; dev
-  thheller/shadow-cljs        {:mvn/version "RELEASE"}
-  cider/cider-nrepl           {:mvn/version "0.23.0-SNAPSHOT"}
-  binaryage/devtools          {:mvn/version "0.9.10"}
-
-  rum                         {:mvn/version "0.11.4"}
-  datascript-transit          {:mvn/version "0.3.0"}
-  funcool/promesa             {:mvn/version "4.0.2"}
-  medley                      {:mvn/version "1.2.0"}
-  metosin/reitit-frontend     {:mvn/version "0.3.10"}
-  cljs-bean                   {:mvn/version "1.5.0"}}
-
- :paths
- ["src"
-  "dev"]}

+ 0 - 31
web/dev/shadow/hooks.clj

@@ -1,31 +0,0 @@
-(ns shadow.hooks
-  (:require [clojure.java.shell :refer [sh]]
-            [clojure.string :as str]))
-
-;; copied from https://gist.github.com/mhuebert/ba885b5e4f07923e21d1dc4642e2f182
-(defn exec [& cmd]
-  (let [cmd (str/split (str/join " " (flatten cmd)) #"\s+")
-        _ (println (str/join " " cmd))
-        {:keys [exit out err]} (apply sh cmd)]
-    (if (zero? exit)
-      (when-not (str/blank? out)
-        (println out))
-      (println err))))
-
-(defn purge-css
-  {:shadow.build/stage :flush}
-  [state {:keys [css-source
-                 js-globs
-                 public-dir]}]
-  (case (:shadow.build/mode state)
-    :release
-    (exec "purgecss --css " css-source
-          (for [content (if (string? js-globs) [js-globs] js-globs)]
-            (str "--content " content))
-          "-o" public-dir)
-
-    :dev
-    (do
-      (exec "mkdir -p" public-dir)
-      (exec "cp" css-source (str public-dir "/" (last (str/split css-source #"/"))))))
-  state)

+ 0 - 7
web/dev/shadow/user.clj

@@ -1,7 +0,0 @@
-(ns shadow.user
-  (:require [shadow.cljs.devtools.api :as api]))
-
-(defn cljs-repl
-  []
-  (api/watch :app)
-  (api/repl :app))

二進制
web/images/screenshot.png


+ 0 - 100
web/public/css/highlight.css

@@ -1,100 +0,0 @@
-/*
-
-Original highlight.js style (c) Ivan Sagalaev <[email protected]>
-
-*/
-
-.hljs {
-  display: block;
-  overflow-x: auto;
-  padding: 0.5em;
-  background: #F0F0F0;
-}
-
-
-/* Base color: saturation 0; */
-
-.hljs,
-.hljs-subst {
-  color: #444;
-}
-
-.hljs-comment {
-  color: #888888;
-}
-
-.hljs-keyword,
-.hljs-attribute,
-.hljs-selector-tag,
-.hljs-meta-keyword,
-.hljs-doctag,
-.hljs-name {
-  font-weight: bold;
-}
-
-
-/* User color: hue: 0 */
-
-.hljs-type,
-.hljs-string,
-.hljs-number,
-.hljs-selector-id,
-.hljs-selector-class,
-.hljs-quote,
-.hljs-template-tag,
-.hljs-deletion {
-  color: #880000;
-}
-
-.hljs-title,
-.hljs-section {
-  color: #880000;
-  font-weight: bold;
-}
-
-.hljs-regexp,
-.hljs-symbol,
-.hljs-variable,
-.hljs-template-variable,
-.hljs-link,
-.hljs-selector-attr,
-.hljs-selector-pseudo {
-  color: #BC6060;
-}
-
-
-/* Language color: hue: 90; */
-
-.hljs-literal {
-  color: #78A960;
-}
-
-.hljs-builtin-name,
-.hljs-built_in,
-.hljs-bullet,
-.hljs-code,
-.hljs-addition {
-  color: #397300;
-}
-
-
-/* Meta color: hue: 200 */
-
-.hljs-meta {
-  color: #1f7199;
-}
-
-.hljs-meta-string {
-  color: #4d99bf;
-}
-
-
-/* Misc effects */
-
-.hljs-emphasis {
-  font-style: italic;
-}
-
-.hljs-strong {
-  font-weight: bold;
-}

文件差異過大導致無法顯示
+ 0 - 0
web/public/css/org.css


+ 0 - 278
web/public/css/style.css

@@ -1,278 +0,0 @@
-.row {
-    display: flex;
-    flex-direction: row;
-    flex: 1;
-    word-break: break-word;
-}
-
-.space-between {
-    display: flex;
-    flex-direction: row;
-    justify-content: space-between;
-    word-break: break-word;
-}
-
-.column {
-    display: flex;
-    flex-direction: column;
-    flex: 1;
-    word-break: break-word;
-}
-
-.grow {
-    flex-grow: 1;
-}
-
-/* copied from emacs org html exporter */
-.title  { text-align: center;
-          margin-bottom: .2em; }
-.subtitle { text-align: center;
-            font-size: medium;
-            font-weight: bold;
-            margin-top:0; }
-.timestamp { color: #bebebe; margin-left: 6px; }
-.timestamp-kwd { color: #5f9ea0; margin-left: 6px; }
-.org-right  { margin-left: auto; margin-right: 0px;  text-align: right; }
-.org-left   { margin-left: 0px;  margin-right: auto; text-align: left; }
-.org-center { margin-left: auto; margin-right: auto; text-align: center; }
-.underline { text-decoration: underline; }
-#postamble p, #preamble p { font-size: 90%; margin: .2em; }
-p.verse { margin-left: 3%; }
-pre {
-    border: 1px solid #ccc;
-    box-shadow: 3px 3px 3px #eee;
-    padding: 8px;
-    font-family: monospace;
-    overflow: auto;
-    margin: 1.2em 0;
-}
-pre.src {
-    position: relative;
-    overflow: visible;
-    padding-top: 1.2em;
-}
-pre.src:before {
-    display: none;
-    position: absolute;
-    background-color: white;
-    top: -10px;
-    right: 10px;
-    padding: 3px;
-    border: 1px solid black;
-}
-pre.src:hover:before { display: inline;}
-/* Languages per Org manual */
-pre.src-asymptote:before { content: 'Asymptote'; }
-pre.src-awk:before { content: 'Awk'; }
-pre.src-C:before { content: 'C'; }
-/* pre.src-C++ doesn't work in CSS */
-pre.src-clojure:before { content: 'Clojure'; }
-pre.src-css:before { content: 'CSS'; }
-pre.src-D:before { content: 'D'; }
-pre.src-ditaa:before { content: 'ditaa'; }
-pre.src-dot:before { content: 'Graphviz'; }
-pre.src-calc:before { content: 'Emacs Calc'; }
-pre.src-emacs-lisp:before { content: 'Emacs Lisp'; }
-pre.src-fortran:before { content: 'Fortran'; }
-pre.src-gnuplot:before { content: 'gnuplot'; }
-pre.src-haskell:before { content: 'Haskell'; }
-pre.src-hledger:before { content: 'hledger'; }
-pre.src-java:before { content: 'Java'; }
-pre.src-js:before { content: 'Javascript'; }
-pre.src-latex:before { content: 'LaTeX'; }
-pre.src-ledger:before { content: 'Ledger'; }
-pre.src-lisp:before { content: 'Lisp'; }
-pre.src-lilypond:before { content: 'Lilypond'; }
-pre.src-lua:before { content: 'Lua'; }
-pre.src-matlab:before { content: 'MATLAB'; }
-pre.src-mscgen:before { content: 'Mscgen'; }
-pre.src-ocaml:before { content: 'Objective Caml'; }
-pre.src-octave:before { content: 'Octave'; }
-pre.src-org:before { content: 'Org mode'; }
-pre.src-oz:before { content: 'OZ'; }
-pre.src-plantuml:before { content: 'Plantuml'; }
-pre.src-processing:before { content: 'Processing.js'; }
-pre.src-python:before { content: 'Python'; }
-pre.src-R:before { content: 'R'; }
-pre.src-ruby:before { content: 'Ruby'; }
-pre.src-sass:before { content: 'Sass'; }
-pre.src-scheme:before { content: 'Scheme'; }
-pre.src-screen:before { content: 'Gnu Screen'; }
-pre.src-sed:before { content: 'Sed'; }
-pre.src-sh:before { content: 'shell'; }
-pre.src-sql:before { content: 'SQL'; }
-pre.src-sqlite:before { content: 'SQLite'; }
-/* additional languages in org.el's org-babel-load-languages alist */
-pre.src-forth:before { content: 'Forth'; }
-pre.src-io:before { content: 'IO'; }
-pre.src-J:before { content: 'J'; }
-pre.src-makefile:before { content: 'Makefile'; }
-pre.src-maxima:before { content: 'Maxima'; }
-pre.src-perl:before { content: 'Perl'; }
-pre.src-picolisp:before { content: 'Pico Lisp'; }
-pre.src-scala:before { content: 'Scala'; }
-pre.src-shell:before { content: 'Shell Script'; }
-pre.src-ebnf2ps:before { content: 'ebfn2ps'; }
-/* additional language identifiers per "defun org-babel-execute"
-       in ob-*.el */
-pre.src-cpp:before  { content: 'C++'; }
-pre.src-abc:before  { content: 'ABC'; }
-pre.src-coq:before  { content: 'Coq'; }
-pre.src-groovy:before  { content: 'Groovy'; }
-/* additional language identifiers from org-babel-shell-names in
-     ob-shell.el: ob-shell is the only babel language using a lambda to put
-     the execution function name together. */
-pre.src-bash:before  { content: 'bash'; }
-pre.src-csh:before  { content: 'csh'; }
-pre.src-ash:before  { content: 'ash'; }
-pre.src-dash:before  { content: 'dash'; }
-pre.src-ksh:before  { content: 'ksh'; }
-pre.src-mksh:before  { content: 'mksh'; }
-pre.src-posh:before  { content: 'posh'; }
-/* Additional Emacs modes also supported by the LaTeX listings package */
-pre.src-ada:before { content: 'Ada'; }
-pre.src-asm:before { content: 'Assembler'; }
-pre.src-caml:before { content: 'Caml'; }
-pre.src-delphi:before { content: 'Delphi'; }
-pre.src-html:before { content: 'HTML'; }
-pre.src-idl:before { content: 'IDL'; }
-pre.src-mercury:before { content: 'Mercury'; }
-pre.src-metapost:before { content: 'MetaPost'; }
-pre.src-modula-2:before { content: 'Modula-2'; }
-pre.src-pascal:before { content: 'Pascal'; }
-pre.src-ps:before { content: 'PostScript'; }
-pre.src-prolog:before { content: 'Prolog'; }
-pre.src-simula:before { content: 'Simula'; }
-pre.src-tcl:before { content: 'tcl'; }
-pre.src-tex:before { content: 'TeX'; }
-pre.src-plain-tex:before { content: 'Plain TeX'; }
-pre.src-verilog:before { content: 'Verilog'; }
-pre.src-vhdl:before { content: 'VHDL'; }
-pre.src-xml:before { content: 'XML'; }
-pre.src-nxml:before { content: 'XML'; }
-/* add a generic configuration mode; LaTeX export needs an additional
-     (add-to-list 'org-latex-listings-langs '(conf " ")) in .emacs */
-pre.src-conf:before { content: 'Configuration File'; }
-
-table { border-collapse:collapse; }
-caption.t-above { caption-side: top; }
-caption.t-bottom { caption-side: bottom; }
-td, th { vertical-align:top;  }
-th.org-right  { text-align: center;  }
-th.org-left   { text-align: center;   }
-th.org-center { text-align: center; }
-td.org-right  { text-align: right;  }
-td.org-left   { text-align: left;   }
-td.org-center { text-align: center; }
-dt { font-weight: bold; }
-.footpara { display: inline; }
-.footdef  { margin-bottom: 1em; }
-.figure { padding: 1em; }
-.figure p { text-align: center; }
-.inlinetask {
-    padding: 10px;
-    border: 2px solid gray;
-    margin: 10px;
-    background: #ffffcc;
-}
-#org-div-home-and-up
-{ text-align: right; font-size: 70%; white-space: nowrap; }
-.linenr { font-size: smaller }
-.code-highlighted { background-color: #ffff00; }
-.org-info-js_info-navigation { border-style: none; }
-#org-info-js_console-label
-{ font-size: 10px; font-weight: bold; white-space: nowrap; }
-.org-info-js_search-highlight
-{ background-color: #ffff00; color: #000000; font-weight: bold; }
-.org-svg { width: 90%; }
-
-.-mr-14 {
-    margin-right: -3.5rem;
-}
-
-/* loader */
-.loader {
-    border-top-color: #3498db;
-    -webkit-animation: spinner 1.5s linear infinite;
-    animation: spinner 1.5s linear infinite;
-}
-
-@-webkit-keyframes spinner {
-    0% { -webkit-transform: rotate(0deg); }
-    100% { -webkit-transform: rotate(360deg); }
-}
-
-@keyframes spinner {
-    0% { transform: rotate(0deg); }
-    100% { transform: rotate(360deg); }
-}
-
-/* scroll-bg */
-.scroll-background {
-    height: 400%; width: 400%; top: -25%; left: -100%; background-size: 800px auto; background-image: url('/img/hero-pattern-lg.png');
-}
-
-.angled-background {
-    background-image: url('/img/angled-background.svg'); background-size: 100% auto; background-position: -5px -5px;
-}
-
-.scroll-background-2 {
-    height: 800%; width: 400%; top: -100%; left: -100%; background-size: 400px auto; background-image: url('/img/hero-pattern-lg.png');
-}
-
-@-webkit-keyframes scrollSmall {0%{transform:rotate(-13deg) translateY(0)}to{transform:rotate(-13deg) translateY(-639px)}}
-@keyframes scrollSmall{0%{transform:rotate(-13deg) translateY(0)}to{transform:rotate(-13deg) translateY(-639px)}}
-@-webkit-keyframes scrollLarge{0%{transform:rotate(-13deg) translateY(0)}to{transform:rotate(-13deg) translateY(-1278px)}}
-@keyframes scrollLarge{0%{transform:rotate(-13deg) translateY(0)}to{transform:rotate(-13deg) translateY(-1278px)}}
-@-webkit-keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}
-@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}
-@-webkit-keyframes pulse{0%{opacity:1}50%{opacity:.5}to{opacity:1}}
-@keyframes pulse{0%{opacity:1}50%{opacity:.5}to{opacity:1}}
-.spin{-webkit-animation:spin .5s linear infinite;animation:spin .5s linear infinite}
-.pulse{-webkit-animation:pulse 2s ease infinite;animation:pulse 2s ease infinite}
-.scroll-bg{-webkit-animation-name:scrollSmall;animation-name:scrollSmall;-webkit-animation-duration:15s;animation-duration:15s;-webkit-animation-timing-function:linear;animation-timing-function:linear;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}
-@media (min-width:1024px){.scroll-bg{-webkit-animation-name:scrollLarge;animation-name:scrollLarge;-webkit-animation-duration:35s;animation-duration:35s;-webkit-animation-timing-function:linear;animation-timing-function:linear;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}}
-
-h1, h2, h3, h4, h5, h6 {
-    font-weight: bold;
-}
-
-textarea {
-    overflow: hidden;
-    padding: 8px;
-    border: 1px solid rgba(39,41,43,.15);
-    border-radius: 4px;
-    font-size: 1rem;
-    line-height: 1.5;
-    width: 100%;
-    resize: none;
-    outline: none;
-}
-
-.content ol, .content ui {
-    list-style: disc;
-    margin-left: 1em;
-}
-
-.content p, .content div {
-    word-break: break-word;
-}
-
-#journals .journal:first-child {
-    border-top: none;
-    min-height: 500px;
-}
-
-#journals .journal {
-    border-top: 1px solid #738694;
-    padding: 48px 0;
-    margin: 24px 0 48px 0;
-}
-
-#journals {
-    margin-bottom: 300px;
-}
-
-p {
-    line-height: 1.5;
-}

文件差異過大導致無法顯示
+ 0 - 0
web/public/css/tailwind.min.css


二進制
web/public/favicon.ico


+ 0 - 3
web/public/img/angled-background.svg

@@ -1,3 +0,0 @@
-<svg viewBox="0 0 100 1200" fill="#161e2e" xmlns="http://www.w3.org/2000/svg">
-  <polygon points="0,0 100,0 0,1200"/>
-</svg>

二進制
web/public/img/hero-pattern-lg.png


二進制
web/public/img/logo.png


文件差異過大導致無法顯示
+ 0 - 1
web/public/js/highlight.pack.js


+ 0 - 29
web/src/frontend/blob.cljs

@@ -1,29 +0,0 @@
-(ns frontend.blob)
-
-(defn- decode
-  "Decodes the data portion of a data url from base64"
-  [[media-type data]]
-  [media-type (js/atob data)])
-
-(defn- uint8
-  "Converts a base64 decoded data string to a Uint8Array"
-  [[media-type data]]
-  (->> (map #(.charCodeAt %1) data)
-       js/Uint8Array.
-       (vector media-type)))
-
-(defn- make-blob
-  "Creates a JS Blob object from a media type and a Uint8Array"
-  [[media-type uint8]]
-  (js/Blob. (array uint8) (js-obj "type" media-type)))
-
-(defn blob
-  "Converts a data-url into a JS Blob. This is useful for uploading
-   image data from JavaScript."
-  [data-url]
-  {:pre [(string? data-url)]}
-  (-> (re-find #"^data:([^;]+);base64,(.*)$" data-url)
-      rest
-      decode
-      uint8
-      make-blob))

+ 0 - 104
web/src/frontend/components/agenda.cljs

@@ -1,104 +0,0 @@
-(ns frontend.components.agenda
-  (:require [rum.core :as rum]
-            [frontend.util :as util]
-            [frontend.handler :as handler]
-            [frontend.format.org.block :as block]
-            [frontend.state :as state]
-            [clojure.string :as string]
-            [frontend.format.org-mode :as org]
-            [frontend.components.sidebar :as sidebar]
-            [frontend.db :as db]
-            [frontend.ui :as ui]))
-
-(rum/defc timestamps-cp
-  [timestamps]
-  [:ul
-   (for [[type {:keys [date time]}] timestamps]
-     (let [{:keys [year month day]} date
-           {:keys [hour min]} time]
-       [:li {:key type}
-        [:span {:style {:margin-right 6}} type]
-        [:span (if time
-                 (str year "-" month "-" day " " hour ":" min)
-                 (str year "-" month "-" day))]]))])
-
-(rum/defc title-cp
-  [title]
-  (let [title-json (js/JSON.stringify (clj->js title))
-        html (org/inline-list->html title-json)]
-    (util/raw-html html)))
-
-(rum/defc children-cp
-  [children]
-  (let [children-json (js/JSON.stringify (clj->js children))
-        html (org/json->html children-json)]
-    (util/raw-html html)))
-
-(rum/defc marker-cp
-  [marker]
-  (if marker
-    [:span {:class (str "marker-" (string/lower-case marker))
-            :style {:margin-left 8}}
-     (if (contains? #{"DOING" "IN-PROGRESS"} marker)
-       (str " (" marker ")"))]))
-
-(rum/defc tags-cp
-  [tags]
-  [:span
-   (for [{:keys [tag/name]} tags]
-     [:span.tag {:key name}
-      [:span
-       name]])])
-
-(rum/defc agenda
-  []
-  (let [tasks (db/get-agenda)]
-    (sidebar/sidebar
-     [:div#agenda
-      [:h2.mb-3 "Agenda"]
-      (if (seq tasks)
-        [:div.ml-1
-         (let [tasks (block/sort-tasks tasks)]
-           (for [{:heading/keys [uuid marker title priority level tags children timestamps meta repo file] :as task} tasks]
-             [:div.mb-2
-              {:key (str "task-" uuid)
-               :style {:padding-left 8
-                       :padding-right 8}}
-              [:div.column
-               [:div.row {:style {:align-items "center"}}
-                (case marker
-                  (list "DOING" "IN-PROGRESS" "TODO")
-                  (ui/checkbox {:on-change (fn [_]
-                                             ;; FIXME: Log timestamp
-                                             (handler/check task))})
-
-                  "WAIT"
-                  [:span {:style {:font-weight "bold"}}
-                   "WAIT"]
-
-                  "DONE"
-                  (ui/checkbox {:checked true
-                                :on-change (fn [_]
-                                             ;; FIXME: Log timestamp
-                                             (handler/uncheck task)
-                                             )})
-
-                  nil)
-                [:div.row.ml-2
-                 (if priority
-                   [:span.priority.mr-1
-                    (str "#[" priority "]")])
-                 (title-cp title)
-                 (marker-cp marker)
-                 (when (seq tags)
-                   (tags-cp tags))]]
-               (when (seq timestamps)
-                 (timestamps-cp timestamps))
-
-               ;; FIXME: parse error
-               ;; (when (seq children)
-               ;;   (children-cp children))
-
-               ]]
-             ))]
-        "Empty")])))

+ 0 - 27
web/src/frontend/components/content.cljs

@@ -1,27 +0,0 @@
-(ns frontend.components.content
-  (:require [rum.core :as rum]
-            [frontend.format :as format]
-            [frontend.format.org-mode :as org]
-            [frontend.handler :as handler]
-            [frontend.util :as util]))
-
-(defn- highlight!
-  []
-  (doseq [block (-> (js/document.querySelectorAll "pre code")
-                    (array-seq))]
-    (js/hljs.highlightBlock block)))
-
-(rum/defc html <
-  {:did-mount (fn [state]
-                (highlight!)
-                (handler/render-local-images!)
-                state)
-   :did-update (fn [state]
-                 (highlight!)
-                 state)}
-  [content format config]
-  (case format
-    (list :png :jpg :jpeg)
-    content
-    (util/raw-html (format/to-html content format
-                                   config))))

+ 0 - 86
web/src/frontend/components/file.cljs

@@ -1,86 +0,0 @@
-(ns frontend.components.file
-  (:require [rum.core :as rum]
-            [frontend.util :as util]
-            [frontend.handler :as handler]
-            [clojure.string :as string]
-            [frontend.db :as db]
-            [frontend.components.sidebar :as sidebar]
-            [frontend.ui :as ui]
-            [frontend.format :as format]
-            [frontend.format.org-mode :as org]
-            [frontend.components.content :as content]
-            [goog.crypt.base64 :as b64]))
-
-(defn- get-path
-  [state]
-  (let [route-match (first (:rum/args state))
-        encoded-path (get-in route-match [:parameters :path :path])
-        decoded-path (b64/decodeString encoded-path)]
-    [encoded-path decoded-path]))
-
-(rum/defcs file <
-  [state]
-  (let [[encoded-path path] (get-path state)
-        suffix (keyword (string/lower-case (last (string/split path #"\."))))]
-    (sidebar/sidebar
-     (cond
-       (and suffix (contains? #{:md :markdown :org} suffix))
-       [:div.content
-        [:a {:href (str "/file/" encoded-path "/edit")}
-         "edit"]
-        (let [content (db/get-file (last (get-path state)))]
-          (cond
-            (string/blank? content)
-            [:span]
-
-            content
-            (content/html content suffix org/default-config)
-
-            :else
-            "Loading ..."))]
-
-       ;; image type
-       (and suffix (contains? #{:png :jpg :jpeg} suffix))
-       (content/html [:img {:src path}] suffix org/default-config)
-
-       :else
-       [:div "Format ." (name suffix) " is not supported."]))))
-
-(defn- count-newlines
-  [s]
-  (count (re-seq #"\n" (or s ""))))
-
-(rum/defcs edit <
-  (rum/local nil ::content)
-  (rum/local "" ::commit-message)
-  {:will-mount (fn [state]
-                 (assoc state ::initial-content (db/get-file (last (get-path state)))))}
-  [state]
-  (let [initial-content (get state ::initial-content)
-        initial-rows (+ 3 (count-newlines initial-content))
-        content (get state ::content)
-        commit-message (get state ::commit-message)
-        rows (if (nil? @content) initial-rows (+ 3 (count-newlines @content)))
-        [_encoded-path path] (get-path state)]
-    (prn {:rows rows})
-    (sidebar/sidebar
-     [:div.content
-      [:h3.mb-2 (str "Update " path)]
-      [:textarea
-       {:rows rows
-        :default-value initial-content
-        :on-change #(reset! content (.. % -target -value))
-        :auto-focus true}]
-      [:div.mt-1.mb-1.relative.rounded-md.shadow-sm
-       [:input.form-input.block.w-full.sm:text-sm.sm:leading-5
-        {:placeholder "Commit message"
-         :on-change (fn [e]
-                      (reset! commit-message (util/evalue e)))}]]
-      (ui/button "Save" (fn []
-                          (when (and (not (string/blank? @content))
-                                     (not (= initial-content
-                                             @content)))
-                            (let [commit-message (if (string/blank? @commit-message)
-                                                   (str "Update " path)
-                                                   @commit-message)]
-                              (handler/alter-file path commit-message @content)))))])))

+ 0 - 81
web/src/frontend/components/home.cljs

@@ -1,81 +0,0 @@
-(ns frontend.components.home
-  (:require [frontend.state :as state]
-            [frontend.util :as util]
-            [frontend.handler :as handler]
-            [frontend.ui :as ui]
-            [frontend.mixins :as mixins]
-            [frontend.config :as config]
-            [rum.core :as rum]
-            [frontend.format :as format]
-            [clojure.string :as string]
-            [frontend.db :as db]
-            [frontend.components.sidebar :as sidebar]))
-
-(rum/defc front-page
-  []
-  [:div.relative.min-h-screen.overflow-hidden.bg-gray-900.lg:bg-gray-300
-   [:div.hidden.lg:block.absolute.scroll-bg.scroll-background]
-   [:div.angled-background
-    {:class (util/hiccup->class ".relative.min-h-screen.lg:min-w-3xl.xl:min-w-4xl.lg:flex.lg:items-center.lg:justify-center.lg:w-3/5.lg:py-20.lg:pl-8.lg:pr-8.bg-no-repeat")}
-    [:div
-     [:div.px-6.pt-8.pb-12.md:max-w-3xl.md:mx-auto.lg:mx-0.lg:max-w-none.lg:pt-0.lg:pb-16
-      [:div.flex.items-center.justify-between
-       [:div
-        [:img.h-6.lg:h-8.xl:h-9
-         {:alt "Logseq",
-          :src "/img/logo.png"}]]
-       [:div
-        [:a.text-sm.font-semibold.text-white.focus:outline-none.focus:underline
-         {:href "/login/github"}
-         "Login →"]]]]
-     [:div.px-6.md:max-w-3xl.md:mx-auto.lg:mx-0.lg:max-w-none
-      [:p.text-sm.font-semibold.text-gray-300.uppercase.tracking-wider
-       "\n              Now in early access\n            "]
-      [:h1.mt-3.text-3xl.leading-9.font-semibold.font-display.text-white.sm:mt-6.sm:text-4xl.sm:leading-10.xl:text-5xl.xl:leading-none
-       "\n              Beautiful UI components, crafted\n              "
-       [:br.hidden.sm:inline]
-       [:span.text-teal-400
-        "\n                by the creators of Tailwind CSS.\n              "]]
-      [:p.mt-2.text-lg.leading-7.text-gray-300.sm:mt-3.sm:text-xl.sm:max-w-xl.xl:mt-4.xl:text-2xl.xl:max-w-2xl
-       "\n              Fully responsive HTML components, designed and developed by Adam Wathan and Steve Schoger.\n            "]
-      [:div.mt-6.sm:flex.sm:mt-8.xl:mt-12
-       [:a.w-full.sm:w-auto.inline-flex.items-center.justify-center.px-6.py-3.border.border-transparent.text-base.leading-6.font-semibold.rounded-md.text-gray-900.bg-white.shadow-sm.hover:text-gray-600.focus:outline-none.focus:text-gray-600.transition.ease-in-out.duration-150.xl:text-lg.xl:py-4
-        {:href (str config/api "login/github")}
-        "Login with Github"]
-       [:a.mt-4.sm:ml-4.sm:mt-0.w-full.sm:w-auto.inline-flex.items-center.justify-center.px-6.py-3.border.border-transparent.text-base.leading-6.font-semibold.rounded-md.text-white.bg-gray-800.shadow-sm.hover:bg-gray-700.focus:outline-none.focus:bg-gray-700.transition.ease-in-out.duration-150.xl:text-lg.xl:py-4
-        {:href "/demo"}
-        "Live Demo"]]]
-     [:div.mt-8.sm:mt-12.relative.h-64.overflow-hidden.bg-gray-300.lg:hidden
-      [:div.absolute.scroll-bg.scroll-background-2]]
-     [:div.px-6.py-8.sm:pt-12.md:max-w-3xl.md:mx-auto.lg:mx-0.lg:max-w-full.lg:py-0.lg:pt-24
-      [:p.text-sm.font-semibold.text-gray-300.uppercase.tracking-wider
-       "Designed and developed by"]
-      [:div.mt-4.sm:flex
-       [:a.flex.items-center.no-underline
-        {:href "https://twitter.com/adamwathan"}]
-       [:div.flex-shrink-0
-        [:img.h-12.w-12.rounded-full.border-2.border-white
-         {:alt "", :src "/img/adam.jpg"}]]
-       [:div.ml-3
-        [:p.font-semibold.text-white.leading-tight "Adam Wathan"]
-        [:p.text-sm.text-gray-500.leading-tight
-         "Creator of Tailwind CSS"]]
-       [:a.mt-6.sm:mt-0.sm:ml-12.flex.items-center.no-underline
-        {:href "https://twitter.com/steveschoger"}]
-       [:div.flex-shrink-0
-        [:img.h-12.w-12.rounded-full.border-2.border-white
-         {:alt "", :src "/img/steve.jpg"}]]
-       [:div.ml-3
-        [:p.font-semibold.text-white.leading-tight "Steve Schoger"]
-        [:p.text-sm.text-gray-500.leading-tight
-         "Author of Refactoring UI"]]]]]]])
-
-(rum/defc home <
-  {:will-mount (fn [state]
-                 (when-not (db/get-github-token)
-                   (handler/get-github-access-token))
-                 state)}
-  [state]
-  (if (db/get-github-token)
-    (sidebar/sidebar (sidebar/main-content))
-    (front-page)))

+ 0 - 105
web/src/frontend/components/journal.cljs

@@ -1,105 +0,0 @@
-(ns frontend.components.journal
-  (:require [rum.core :as rum]
-            [frontend.util :as util]
-            [frontend.handler :as handler]
-            [clojure.string :as string]
-            [frontend.ui :as ui]
-            [frontend.format :as format]
-            [frontend.mixins :as mixins]
-            [frontend.db :as db]
-            [frontend.state :as state]
-            [frontend.format.org-mode :as org]
-            [goog.object :as gobj]
-            [frontend.image :as image]
-            [frontend.components.content :as content]))
-
-(def edit-content (atom ""))
-(rum/defc editor-box <
-  (mixins/event-mixin
-   (fn [state]
-     (let [heading (first (:rum/args state))]
-       (mixins/hide-when-esc-or-outside
-        state
-        nil
-        :show-fn (fn []
-                   (:edit? @state/state))
-        :on-hide (fn []
-                   (handler/save-current-edit-journal! (str heading "\n" @edit-content)))))))
-  [heading content]
-  [:div.flex-1
-   (ui/textarea-autosize
-    {:on-change (fn [e]
-                  (reset! edit-content (util/evalue e)))
-     :default-value content
-     :auto-focus true
-     :style {:border "none"
-             :border-radius 0
-             :background "transparent"
-             :margin-top 12.5}})
-   [:input
-    {:id "files"
-     :type "file"
-     :on-change (fn [e]
-                  (let [files (.-files (.-target e))]
-                    (image/upload
-                     files
-                     (fn [file file-form-data file-name file-type]
-                       ;; TODO: set uploading
-                       (.append file-form-data "name" file-name)
-                       (.append file-form-data file-type true)
-
-                       ;; (citrus/dispatch!
-                       ;;  :image/upload
-                       ;;  file-form-data
-                       ;;  (fn [url]
-                       ;;    (reset! uploading? false)
-                       ;;    (swap! form assoc name url)
-                       ;;    (if on-uploaded
-                       ;;      (on-uploaded form name url))))
-                       ))))
-     ;; :hidden true
-     }]])
-
-(defn split-first [re s]
-  (clojure.string/split s re 2))
-
-(defn- split-heading-body
-  [content]
-  (let [result (split-first #"\n" content)]
-    (if (= 1 (count result))
-      [result ""]
-      result)))
-
-(rum/defc journal-cp < rum/reactive
-  [{:keys [uuid title content] :as journal}]
-  (let [{:keys [edit? edit-journal]} (rum/react state/state)
-        [heading content] (split-heading-body content)]
-    [:div.flex-1
-     [:h1.text-gray-600 {:style {:font-weight "450"}}
-      title]
-
-     (if (and edit? (= uuid (:uuid edit-journal)))
-       (editor-box heading content)
-       [:div {:on-click (fn []
-                          (handler/edit-journal! content journal)
-                          (reset! edit-content content))
-              :style {:padding 8
-                      :min-height 200}}
-        (if (or (not content)
-                (string/blank? content))
-          [:div]
-          (content/html content "org" org/config-with-line-break))])]))
-
-(rum/defcs journals < rum/reactive
-  {:will-mount (fn [state]
-                 (handler/set-latest-journals!)
-                 state)}
-  [state]
-  (let [{:keys [latest-journals]} (rum/react state/state)]
-    [:div#journals
-     (ui/infinite-list
-      (for [journal latest-journals]
-        [:div.journal.content {:key (cljs.core/random-uuid)}
-         (journal-cp journal)])
-      {:on-load (fn []
-                  (handler/load-more-journals!))})]))

+ 0 - 42
web/src/frontend/components/repo.cljs

@@ -1,42 +0,0 @@
-(ns frontend.components.repo
-  (:require [rum.core :as rum]
-            [frontend.util :as util]
-            [frontend.handler :as handler]
-            [clojure.string :as string]
-            [frontend.ui :as ui]))
-
-(defn repos
-  [repos]
-  (when (seq repos)
-    [:div#repos
-     [:ul
-      (for [url repos]
-        [:li {:key url}
-         [:button {:on-click (fn []
-                               ;; (handler/set-current-repo url)
-                               )}
-          (string/replace url "https://github.com/" "")]])]]))
-
-(rum/defcs add-repo < (rum/local "https://github.com/" ::repo-url)
-  [state]
-  (let [prefix "https://github.com/"
-        repo-url (get state ::repo-url)]
-    [:div.p-8.flex.items-center.justify-center.bg-white
-     [:div.w-full.max-w-xs.mx-auto
-      [:div
-       [:div
-        [:h2 "Specify your repo:"]
-        [:div.mt-2.mb-2.relative.rounded-md.shadow-sm
-         [:div.absolute.inset-y-0.left-0.pl-3.flex.items-center.pointer-events-none
-          [:span.text-gray-500.sm:text-sm.sm:leading-5
-           prefix]]
-         [:input#repo.form-input.block.w-full.pl-16.sm:pl-14.sm:text-sm.sm:leading-5
-          {:autoFocus true
-           :placeholder "username/repo"
-           :on-change (fn [e]
-                        (reset! repo-url (util/evalue e)))
-           :style {:padding-left "9.1em"}}]]]]
-      (ui/button
-        "Clone"
-        (fn []
-          (handler/clone-and-pull (str prefix @repo-url))))]]))

+ 0 - 51
web/src/frontend/components/settings.cljs

@@ -1,51 +0,0 @@
-(ns frontend.components.settings
-  ;; (:require [rum.core :as rum]
-  ;;           [frontend.mui :as mui]
-  ;;           [frontend.util :as util]
-  ;;           [frontend.state :as state]
-  ;;           [frontend.handler :as handler]
-  ;;           [clojure.string :as string])
-  )
-
-;; (defn settings-form
-;;   [github-token github-repo]
-;;   [:form {:style {:min-width 300}}
-;;         (mui/grid
-;;          {:container true
-;;           :direction "column"}
-;;          (mui/text-field {:id "standard-basic"
-;;                           :style {:margin-bottom 12}
-;;                           :label "Github repo"
-;;                           :on-change (fn [event]
-;;                                        (let [v (util/evalue event)]
-;;                                          (swap! state/state assoc :github-repo v)))
-;;                           :value github-repo
-;;                           })
-;;          (mui/button {:variant "contained"
-;;                       :color "primary"
-;;                       :on-click (fn []
-;;                                   (when (and github-token github-repo)
-;;                                     (handler/clone github-token github-repo)))}
-;;            "Sync"))])
-
-;; (rum/defc settings < rum/reactive
-;;   []
-;;   ;; Change repo and basic token
-;;   (let [state (rum/react state/state)
-;;         {:keys [github-token github-repo]} state]
-;;     (mui/container
-;;      {:id "root-container"
-;;       :style {:display "flex"
-;;               :justify-content "center"
-;;               :margin-top 64}}
-
-;;      [:div
-
-;;       (settings-form github-token github-repo)
-
-;;       (mui/divider {:style {:margin "24px 0"}})
-
-;;       ;; clear storage
-;;       (mui/button {:on-click handler/clear-storage
-;;                    :color "primary"}
-;;         "Clear storage and clone")])))

+ 0 - 157
web/src/frontend/components/sidebar.cljs

@@ -1,157 +0,0 @@
-(ns frontend.components.sidebar
-  (:require [rum.core :as rum]
-            [frontend.ui :as ui]
-            [frontend.mixins :as mixins]
-            [frontend.db :as db]
-            [frontend.components.repo :as repo]
-            [frontend.components.journal :as journal]
-            [goog.crypt.base64 :as b64]
-            [frontend.util :as util]
-            [frontend.state :as state]))
-
-(defonce active-button :a.group.flex.items-center.px-2.py-2.text-base.leading-6.font-medium.rounded-md.text-white.bg-gray-900.focus:outline-none.focus:bg-gray-700.transition.ease-in-out.duration-150)
-(defonce inactive-button :a.mt-1.group.flex.items-center.px-2.py-2.text-base.leading-6.font-medium.rounded-md.text-gray-300.hover:text-white.hover:bg-gray-700.focus:outline-none.focus:text-white.focus:bg-gray-700.transition.ease-in-out.duration-150)
-
-(defn nav-item
-  ([title href svg-d]
-   (nav-item title href svg-d false))
-  ([title href svg-d active?]
-   (let [a (if active? active-button inactive-button)]
-     [a {:href href}
-      [:svg.mr-4.h-6.w-6.text-gray-400.group-hover:text-gray-300.group-focus:text-gray-300.transition.ease-in-out.duration-150
-       {:viewBox "0 0 24 24", :fill "none", :stroke "currentColor"}
-       [:path
-        {:d svg-d
-         :stroke-width "2",
-         :stroke-linejoin "round",
-         :stroke-linecap "round"}]]
-      title])))
-
-(rum/defc files-list
-  [file-active?]
-  (let [files (db/get-files)]
-    [:div.cursor-pointer.my-1.flex.flex-col.ml-2
-     (if (seq files)
-       (for [file files]
-         (let [encoded-path (b64/encodeString file)]
-           [:a {:key file
-                :class (util/hiccup->class "mt-1.group.flex.items-center.px-2.py-1.text-base.leading-6.font-medium.rounded-md.text-gray-500.hover:text-white.hover:bg-gray-700.focus:outline-none.focus:text-white.focus:bg-gray-700.transition.ease-in-out.duration-150")
-                :style {:color (if (file-active? encoded-path) "#FFF")}
-                :href (str "/file/" encoded-path)}
-            file])))]))
-
-(rum/defc sidebar-nav < rum/reactive
-  []
-  (let [{:keys [:route-match]} (rum/react state/state)
-        active? (fn [route] (= route (get-in route-match [:data :name])))
-        file-active? (fn [path]
-                       (= path (get-in route-match [:parameters :path :path])))]
-    [:nav.flex-1.px-2.py-4.bg-gray-800
-     (nav-item "Journals" "/"
-               "M3 12l9-9 9 9M5 10v10a1 1 0 001 1h3a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1h3a1 1 0 001-1V10M9 21h6"
-               (active? :home))
-     (nav-item "Agenda" "/agenda"
-               "M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
-               (active? :agenda))
-     (files-list file-active?)]))
-
-(rum/defc main-content
-  []
-  (let [repos (db/get-repos)]
-    [:div.max-w-7xl.mx-auto.px-4.sm:px-6.md:px-8
-    (if (seq repos)
-      (journal/journals)
-      (repo/add-repo))]))
-
-(rum/defcs sidebar < (mixins/modal)
-  [state main-content]
-  (let [{:keys [open? close-fn open-fn]} state]
-    [:div.h-screen.flex.overflow-hidden.bg-gray-100
-     [:div.md:hidden
-      [:div.fixed.inset-0.z-30.bg-gray-600.opacity-0.pointer-events-none.transition-opacity.ease-linear.duration-300
-       {:class (if @open?
-                 "opacity-75 pointer-events-auto"
-                 "opacity-0 pointer-events-none")
-        :on-click close-fn}]
-      [:div.fixed.inset-y-0.left-0.flex.flex-col.z-40.max-w-xs.w-full.bg-gray-800.transform.ease-in-out.duration-300
-       {:class (if @open?
-                 "translate-x-0"
-                 "-translate-x-full")}
-       (if @open?
-         [:div.absolute.top-0.right-0.-mr-14.p-1
-          [:button.flex.items-center.justify-center.h-12.w-12.rounded-full.focus:outline-none.focus:bg-gray-600
-           {:on-click close-fn}
-           [:svg.h-6.w-6.text-white
-            {:viewBox "0 0 24 24", :fill "none", :stroke "currentColor"}
-            [:path
-             {:d "M6 18L18 6M6 6l12 12",
-              :stroke-width "2",
-              :stroke-linejoin "round",
-              :stroke-linecap "round"}]]]])
-       [:div.flex-shrink-0.flex.items-center.h-16.px-4.bg-gray-900
-        [:img.h-8.w-auto
-         {:alt "Logseq",
-          :src "/img/logo.png"}]]
-       [:div.flex-1.h-0.overflow-y-auto
-        (sidebar-nav)]
-       ]]
-     [:div.hidden.md:flex.md:flex-shrink-0
-      [:div.flex.flex-col.w-64
-       [:div.flex.items-center.h-16.flex-shrink-0.px-4.bg-gray-900
-        [:img.h-8.w-auto
-         {:alt "Logseq",
-          :src "/img/logo.png"}]]
-       [:div.h-0.flex-1.flex.flex-col.overflow-y-auto
-        (sidebar-nav)]]]
-     [:div.flex.flex-col.w-0.flex-1.overflow-hidden
-      [:div.relative.z-10.flex-shrink-0.flex.h-16.bg-white.shadow
-       [:button.px-4.border-r.border-gray-200.text-gray-500.focus:outline-none.focus:bg-gray-100.focus:text-gray-600.md:hidden
-        {:on-click open-fn}
-        [:svg.h-6.w-6
-         {:viewBox "0 0 24 24", :fill "none", :stroke "currentColor"}
-         [:path
-          {:d "M4 6h16M4 12h16M4 18h7",
-           :stroke-width "2",
-           :stroke-linejoin "round",
-           :stroke-linecap "round"}]]]
-       [:div.flex-1.px-4.flex.justify-between
-        [:div.flex-1.flex
-         [:div.w-full.flex.md:ml-0
-          [:label.sr-only {:for "search_field"} "Search"]
-          [:div.relative.w-full.text-gray-400.focus-within:text-gray-600
-           [:div.absolute.inset-y-0.left-0.flex.items-center.pointer-events-none
-            [:svg.h-5.w-5
-             {:viewBox "0 0 20 20", :fill "currentColor"}
-             [:path
-              {:d
-               "M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z",
-               :clip-rule "evenodd",
-               :fill-rule "evenodd"}]]]
-           [:input#search_field.block.w-full.h-full.pl-8.pr-3.py-2.rounded-md.text-gray-900.placeholder-gray-500.focus:outline-none.focus:placeholder-gray-400.sm:text-sm
-            {:placeholder "Search"}]]]]
-        [:div.ml-4.flex.items-center.md:ml-6
-         [:button.p-1.text-gray-400.rounded-full.hover:bg-gray-100.hover:text-gray-500.focus:outline-none.focus:shadow-outline.focus:text-gray-500
-          [:svg.h-6.w-6
-           {:viewBox "0 0 24 24", :fill "none", :stroke "currentColor"}
-           [:path
-            {:d
-             "M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9",
-             :stroke-width "2",
-             :stroke-linejoin "round",
-             :stroke-linecap "round"}]]]
-         (ui/dropdown-with-links
-          [{:title "Your Profile"
-            :options {:href "#"}}
-           {:title "Settings"
-            :options {:href "#"}}
-           {:title "Sign out"
-            :options {:href "#"}}])]]]
-      [:main.flex-1.relative.z-0.overflow-y-auto.py-6.focus:outline-none
-       ;; {:x-init "$el.focus()", :x-data "x-data", :tabindex "0"}
-       {:tabIndex "0"}
-       [:div.flex.justify-center
-        [:div.flex-1.m-6 {:style {:position "relative"
-                                  :max-width 800}}
-         main-content]]]
-
-      (ui/notification)]]))

+ 0 - 14
web/src/frontend/config.cljs

@@ -1,14 +0,0 @@
-(ns frontend.config)
-
-(defonce tasks-org "tasks.org")
-(defonce hidden-file ".hidden")
-(defonce dev? ^boolean goog.DEBUG)
-(def website
-  (if dev?
-    "http://localhost:3000"
-    "https://logseq.com"))
-
-(def api
-  (if dev?
-    "http://localhost:3000/api/v1/"
-    (str website "/api/v1/")))

+ 0 - 40
web/src/frontend/core.cljs

@@ -1,40 +0,0 @@
-(ns frontend.core
-  (:require [rum.core :as rum]
-            [frontend.handler :as handler]
-            [frontend.page :as page]
-            [frontend.routes :as routes]
-            [reitit.frontend :as rf]
-            [reitit.frontend.easy :as rfe]))
-
-(defn set-router!
-  []
-  (rfe/start!
-   (rf/router routes/routes {})
-   handler/set-route-match!
-   ;; set to false to enable HistoryAPI
-   {:use-fragment false}))
-
-(defn start []
-  (rum/mount
-   (page/current-page)
-   (.getElementById js/document "root"))
-  (set-router!))
-
-(defn ^:export init []
-  ;; init is called ONCE when the page loads
-  ;; this is called in the index.html and must be exported
-  ;; so it is available even in :advanced release builds
-
-  (handler/start!)
-
-  ;; popup to notify user, could be toggled in settings
-  ;; (handler/request-notifications-if-not-asked)
-
-  ;; (handler/run-notify-worker!)
-
-  (start))
-
-(defn stop []
-  ;; stop is called before any code is reloaded
-  ;; this is controlled by :before-load in the config
-  (js/console.log "stop"))

+ 0 - 483
web/src/frontend/db.cljs

@@ -1,483 +0,0 @@
-(ns frontend.db
-  (:require [datascript.core :as d]
-            [frontend.util :as util]
-            [medley.core :as medley]
-            [datascript.transit :as dt]
-            [frontend.format.org-mode :as org]
-            [frontend.format.org.block :as block]
-            [clojure.string :as string]
-            [frontend.utf8 :as utf8]))
-
-;; TODO: don't persistent :github/token
-
-(def datascript-db "logseq/DB")
-(def schema
-  {:db/ident        {:db/unique :db.unique/identity}
-   :github/token    {}
-   ;; repo
-   :repo/url        {:db/unique :db.unique/identity}
-   :repo/cloning?   {}
-   :repo/cloned?    {}
-   :repo/current    {:db/valueType   :db.type/ref}
-
-   ;; file
-   :file/path       {:db/unique :db.unique/identity}
-   :file/repo       {:db/valueType   :db.type/ref}
-   :file/raw        {}
-   :file/html       {}
-   ;; TODO: calculate memory/disk usage
-   ;; :file/size       {}
-
-   ;; heading
-   :heading/uuid   {:db/unique      :db.unique/identity}
-   :heading/repo   {:db/valueType   :db.type/ref}
-   :heading/file   {:db/valueType   :db.type/ref}
-   :heading/anchor {}
-   :heading/marker {}
-   :heading/priority {}
-   :heading/level {}
-   :heading/tags {:db/valueType   :db.type/ref
-                  :db/cardinality :db.cardinality/many
-                  :db/isComponent true}
-
-   ;; tag
-   :tag/name       {:db/unique :db.unique/identity}
-
-   ;; task
-   :task/scheduled {:db/index       true}
-   :task/deadline  {:db/index       true}
-   })
-
-(defonce conn
-  (d/create-conn schema))
-
-;; transit serialization
-
-(defn db->string [db]
-  (dt/write-transit-str db))
-
-(defn string->db [s]
-  (dt/read-transit-str s))
-
-;; persisting DB between page reloads
-(defn persist [db]
-  (js/localStorage.setItem datascript-db (db->string db)))
-
-(defn reset-conn! [db]
-  (reset! conn db))
-
-;; (new TextEncoder().encode('foo')).length
-(defn db-size
-  []
-  (when-let [store (js/localStorage.getItem datascript-db)]
-    (let [bytes (.-length (.encode (js/TextEncoder.) store))]
-      (/ bytes 1000))))
-
-(defn restore! []
-  (when-let [stored (js/localStorage.getItem datascript-db)]
-    (let [stored-db (string->db stored)]
-      (when (= (:schema stored-db) schema) ;; check for code update
-        (reset-conn! stored-db)))))
-
-;; TODO: added_at, started_at, schedule, deadline
-(def qualified-map
-  {:file :heading/file
-   :anchor :heading/anchor
-   :title :heading/title
-   :marker :heading/marker
-   :priority :heading/priority
-   :level :heading/level
-   :timestamps :heading/timestamps
-   :children :heading/children
-   :tags :heading/tags
-   :meta :heading/meta
-   })
-
-;; (def schema
-;;   [{:db/ident       {:db/unique :db.unique/identity}}
-
-;;    ;; {:db/ident       :heading/title
-;;    ;;  :db/valueType   :db.type/string
-;;    ;;  :db/cardinality :db.cardinality/one}
-
-;;    ;; {:db/ident       :heading/parent-title
-;;    ;;  :db/valueType   :db.type/string
-;;    ;;  :db/cardinality :db.cardinality/one}
-
-;;    ;; TODO: timestamps, meta
-;;    ;; scheduled, deadline
-;;    ])
-
-(defn ->tags
-  [tags]
-  (map (fn [tag]
-         {:db/id tag
-          :tag/name tag})
-    tags))
-
-(defn extract-timestamps
-  [{:keys [meta] :as heading}]
-  (let [{:keys [pos timestamps]} meta]
-    ))
-
-(defn- safe-headings
-  [headings]
-  (mapv (fn [heading]
-          (let [heading (-> (util/remove-nils heading)
-                            (assoc :heading/uuid (d/squuid)))
-                heading (assoc heading :tags
-                               (->tags (:tags heading)))]
-            (medley/map-keys
-             (fn [k] (get qualified-map k k))
-             heading)))
-        headings))
-
-;; queries
-
-(defn- distinct-result
-  [query-result]
-  (-> query-result
-      seq
-      flatten
-      distinct))
-
-(def seq-flatten (comp flatten seq))
-
-(defn get-all-tags
-  []
-  (distinct-result
-   (d/q '[:find ?tags
-          :where
-          [?h :heading/tags ?tags]]
-     @conn)))
-
-(defn get-repo-headings
-  [repo-url]
-  (-> (d/q '[:find ?heading
-             :in $ ?repo-url
-             :where
-             [?repo :repo/url ?repo-url]
-             [?heading :heading/repo ?repo]]
-        @conn repo-url)
-      seq-flatten))
-
-(defn delete-headings!
-  [repo-url]
-  (let [headings (get-repo-headings repo-url)
-        headings (mapv (fn [eid] [:db.fn/retractEntity eid]) headings)]
-    (d/transact! conn headings)))
-
-(defn get-file-headings
-  [repo-url path]
-  (-> (d/q '[:find ?heading
-             :in $ ?repo-url ?path
-             :where
-             [?repo :repo/url ?repo-url]
-             [?file :file/path ?path]
-             [?heading :heading/file ?file]
-             [?heading :heading/repo ?repo]]
-        @conn repo-url path)
-      seq-flatten))
-
-(defn delete-file-headings!
-  [repo-url path]
-  (let [headings (get-file-headings repo-url path)]
-    (mapv (fn [eid] [:db.fn/retractEntity eid]) headings)))
-
-;; transactions
-(defn reset-headings!
-  [repo-url headings]
-  (delete-headings! repo-url)
-  (let [headings (safe-headings headings)]
-    (d/transact! conn headings)))
-
-(defn get-all-headings
-  []
-  (seq-flatten
-   (d/q '[:find (pull ?h [*])
-          :where
-          [?h :heading/title]]
-     @conn)))
-
-(defn search-headings-by-title
-  [title])
-
-(defn get-headings-by-tag
-  [tag]
-  (let [pred (fn [db tags]
-               (some #(= tag %) tags))]
-    (d/q '[:find (flatten (pull ?h [*]))
-           :in $ ?pred
-           :where
-           [?h :heading/tags ?tags]
-           [(?pred $ ?tags)]]
-      @conn pred)))
-
-(defn transact!
-  [tx-data]
-  (d/transact! conn tx-data))
-
-(defn set-key-value
-  [key value]
-  (transact! [{:db/id -1
-               :db/ident key
-               key value}]))
-
-(defn transact-github-token!
-  [token]
-  (set-key-value :github/token token))
-
-(defn get-key-value
-  [key]
-  (some-> (d/entity (d/db conn) key)
-          key))
-
-(defn get-github-token
-  []
-  (get-key-value :github/token))
-
-(defn set-current-repo!
-  [repo]
-  (set-key-value :repo/current [:repo/url repo]))
-
-(defn get-current-repo
-  []
-  (:repo/url (get-key-value :repo/current)))
-
-(defn get-repos
-  []
-  (->> (d/q '[:find ?url
-              :where [_ :repo/url ?url]]
-         @conn)
-       (map first)
-       distinct))
-
-(defn get-files
-  []
-  (->> (d/q '[:find ?path
-              :where
-              [_     :repo/current ?repo]
-              [?file :file/repo ?repo]
-              [?file :file/path ?path]]
-         @conn)
-       (map first)
-       distinct))
-
-(defn set-repo-cloning
-  [repo-url value]
-  (d/transact! conn
-    [{:repo/url repo-url
-      :repo/cloning? value}]))
-
-(defn mark-repo-as-cloned
-  [repo-url]
-  (d/transact! conn
-    [{:repo/url repo-url
-      :repo/cloned? true}]))
-
-;; file
-(defn transact-files!
-  [repo-url files]
-  (d/transact! conn
-    (for [file files]
-      {:file/repo [:repo/url repo-url]
-       :file/path file})))
-
-(defn get-repo-files
-  [repo-url]
-  (->> (d/q '[:find ?path
-              :in $ ?repo-url
-              :where
-              [?repo :repo/url ?repo-url]
-              [?file :file/repo ?repo]
-              [?file :file/path ?path]]
-         @conn repo-url)
-       (map first)
-       distinct))
-
-(defn set-file-content!
-  [repo-url file content]
-  (d/transact! conn
-    [{:file/repo [:repo/url repo-url]
-      :file/path file
-      :file/content content}]))
-
-(defn extract-headings
-  [repo-url file content]
-  (if (string/blank? content)
-    []
-    (let [headings (org/->clj content)
-          headings (block/extract-headings headings)]
-      (map (fn [heading]
-             (assoc heading
-                    :heading/repo [:repo/url repo-url]
-                    :heading/file [:file/path file]))
-        headings))))
-
-(defn get-all-files-content
-  [repo-url]
-  (d/q '[:find ?path ?content
-         :in $ ?repo-url
-         :where
-         [?repo :repo/url ?repo-url]
-         [?file :file/repo ?repo]
-         [?file :file/content ?content]
-         [?file :file/path ?path]]
-    @conn repo-url))
-
-(defn extract-all-headings
-  [repo-url]
-  (let [contents (get-all-files-content repo-url)]
-    (vec
-     (mapcat
-      (fn [[file content] contents]
-        (extract-headings repo-url file content))
-      contents))))
-
-(defn reset-file!
-  [repo-url file content]
-  (let [file-content [{:file/repo [:repo/url repo-url]
-                       :file/path file
-                       :file/content content}]
-        delete-headings (delete-file-headings! repo-url file)
-        headings (extract-headings repo-url file content)
-        headings (safe-headings headings)]
-    (d/transact! conn (concat file-content delete-headings headings))))
-
-(defn get-file-content
-  [repo-url path]
-  (->> (d/q '[:find ?content
-              :in $ ?repo-url ?path
-              :where
-              [?repo :repo/url ?repo-url]
-              [?file :file/repo ?repo]
-              [?file :file/path ?path]
-              [?file :file/content ?content]]
-         @conn repo-url path)
-       (map first)
-       first))
-
-(defn get-file
-  [path]
-  (->
-   (d/q '[:find ?content
-          :in $ ?path
-          :where
-          [_     :repo/current ?repo]
-          [?file :file/repo ?repo]
-          [?file :file/path ?path]
-          [?file :file/content ?content]]
-     @conn
-     path)
-   ffirst))
-
-;; marker should be one of: TODO, DOING, IN-PROGRESS
-;; time duration
-(defn get-agenda
-  ([]
-   (get-agenda :week))
-  ([time]
-   (let [duration (case time
-                    :today []
-                    :week  []
-                    :month [])]
-     (->
-      (d/q '[:find (pull ?h [*])
-             :where
-             (or [?h :heading/marker "TODO"]
-                 [?h :heading/marker "DOING"]
-                 [?h :heading/marker "IN-PROGRESS"]
-                 [?h :heading/marker "DONE"])]
-        @conn)
-      seq-flatten))))
-
-(defn entity
-  [id-or-lookup-ref]
-  (d/entity (d/db conn) id-or-lookup-ref))
-
-(defn get-current-journal
-  []
-  (get-file (util/current-journal-path)))
-
-(defn get-month-journals
-  [journal-path content before-date days]
-  (let [[month day year] (string/split before-date #"/")
-        day' (util/zero-pad (inc (util/parse-int day)))
-        before-date (string/join "/" [month day' year])
-        content-arr (utf8/encode content)
-        end-pos (utf8/length content-arr)
-        blocks (reverse (org/->clj content))
-        headings (some->>
-                  blocks
-                  (filter (fn [block]
-                            (and
-                             (block/heading-block? block)
-
-                             (= 1 (:level (second block)))
-
-                             (let [[_ {:keys [title meta]}] block]
-                               (when-let [title (last (first title))]
-                                 (let [date (last (string/split title #", "))]
-                                   (<= (compare date before-date) 0)))))))
-                  (map (fn [[_ {:keys [title meta]}]]
-                         {:title (last (first title))
-                          :file-path journal-path
-                          :start-pos (:pos meta)}))
-                  (take (inc days)))
-        [_ journals] (reduce (fn [[last-end-pos acc] heading]
-                               (let [end-pos last-end-pos
-                                     acc (conj acc (assoc heading
-                                                          :uuid (cljs.core/random-uuid)
-                                                          :end-pos end-pos
-                                                          :content (utf8/substring content-arr
-                                                                                   (:start-pos heading)
-                                                                                   end-pos)))]
-                                 [(:start-pos heading) acc])) [end-pos []] headings)]
-    (if (> (count journals) days)
-      (drop 1 journals)
-      journals)))
-
-(defn- compute-journal-path
-  [before-date]
-  (let [[month day year] (->> (string/split before-date #"/")
-                              (mapv util/parse-int))
-        [year month] (cond
-                       (and (= month 1)
-                            (= day 1))
-                       [(dec year) 12]
-
-                       (= day 1)
-                       [year (dec month)]
-
-                       :else
-                       [year month])]
-    (util/journals-path year month)))
-
-;; before-date should be a string joined with "/", like "month/day/year"
-(defn get-latest-journals
-  ([]
-   (get-latest-journals {}))
-  ([{:keys [content before-date days]
-     :or {days 3}}]
-   (let [before-date (if before-date
-                       before-date
-                       (let [{:keys [year month day]} (util/year-month-day-padded)]
-                         (string/join "/" [month day year])))
-
-         journal-path (compute-journal-path before-date)]
-     (when-let [content (or content (get-file journal-path))]
-       (get-month-journals journal-path content before-date days)))))
-
-(comment
-  (d/transact! conn [{:db/id -1
-                      :repo/url "https://github.com/tiensonqin/notes"
-                      :repo/cloned? false}])
-  (d/entity (d/db conn) [:repo/url "https://github.com/tiensonqin/notes"])
-  (d/transact! conn
-    (safe-headings [{:heading/repo [:repo/url "https://github.com/tiensonqin/notes"]
-                     :heading/file "test.org"
-                     :heading/anchor "hello"
-                     :heading/marker "TODO"
-                     :heading/priority "A"
-                     :heading/level "10"
-                     :heading/title "hello world"}])))

+ 0 - 54
web/src/frontend/exif.js

@@ -1,54 +0,0 @@
-// copied from https://stackoverflow.com/questions/7584794/accessing-jpeg-exif-rotation-data-in-javascript-on-the-client-side
-
-function objectURLToBlob(url, callback) {
-  var http = new XMLHttpRequest();
-  http.open("GET", url, true);
-  http.responseType = "blob";
-  http.onload = function(e) {
-    if (this.status == 200 || this.status === 0) {
-      callback(this.response);
-    }
-  };
-  http.send();
-}
-
-export var getEXIFOrientation = function (img, callback) {
-  var reader = new FileReader();
-  reader.onload = e => {
-    var view = new DataView(e.target.result)
-
-    if (view.getUint16(0, false) !== 0xFFD8) {
-      return callback(-2)
-    }
-    var length = view.byteLength
-    var offset = 2
-    while (offset < length) {
-      var marker = view.getUint16(offset, false)
-      offset += 2
-      if (marker === 0xFFE1) {
-        if (view.getUint32(offset += 2, false) !== 0x45786966) {
-          return callback(-1)
-        }
-        var little = view.getUint16(offset += 6, false) === 0x4949
-        offset += view.getUint32(offset + 4, little)
-        var tags = view.getUint16(offset, little)
-        offset += 2
-        for (var i = 0; i < tags; i++) {
-          if (view.getUint16(offset + (i * 12), little) === 0x0112) {
-            var o = view.getUint16(offset + (i * 12) + 8, little);
-            return callback(o)
-          }
-        }
-      } else if ((marker & 0xFF00) !== 0xFF00) {
-        break
-      } else {
-        offset += view.getUint16(offset, false)
-      }
-    }
-    return callback(-1)
-  };
-
-  objectURLToBlob(img.src, function (blob) {
-    reader.readAsArrayBuffer(blob.slice(0, 65536));
-  });
-}

+ 0 - 18
web/src/frontend/format.cljs

@@ -1,18 +0,0 @@
-(ns frontend.format
-  (:require [frontend.format.org-mode :as org :refer [->OrgMode]]
-            [frontend.format.markdown :as markdown :refer [->Markdown]]
-            [frontend.format.protocol :as protocol]))
-
-(defn to-html
-  ([content suffix]
-   (to-html content suffix nil))
-  ([content suffix config]
-   (when-let [record (case (keyword suffix)
-                       :org
-                       (->OrgMode content)
-                       (list :md :markdown)
-                       (->Markdown content)
-                       nil)]
-     (if config
-       (protocol/toHtml record config)
-       (protocol/toHtml record)))))

+ 0 - 13
web/src/frontend/format/markdown.cljs

@@ -1,13 +0,0 @@
-(ns frontend.format.markdown
-  (:require ["showdown" :refer [Converter]]
-            [frontend.format.protocol :as protocol]))
-
-(defonce converter (Converter.))
-
-(defrecord Markdown [content]
-  protocol/Format
-  (toHtml [this]
-    (.makeHtml converter content))
-  (toHtml [this config]
-    ;; TODO:
-    (.makeHtml converter content)))

+ 0 - 99
web/src/frontend/format/org/block.cljs

@@ -1,99 +0,0 @@
-(ns frontend.format.org.block
-  (:require [frontend.util :as util]))
-
-(defn heading-block?
-  [block]
-  (and
-   (vector? block)
-   (= "Heading" (first block))))
-
-(defn task-block?
-  [block]
-  (and
-   (heading-block? block)
-   (some? (:marker (second block)))))
-
-;; FIXME:
-(defn extract-title
-  [block]
-  (-> (:title (second block))
-      first
-      second))
-
-(defn- paragraph-block?
-  [block]
-  (and
-   (vector? block)
-   (= "Paragraph" (first block))))
-
-(defn- timestamp-block?
-  [block]
-  (and
-   (vector? block)
-   (= "Timestamp" (first block))))
-
-(defn- paragraph-timestamp-block?
-  [block]
-  (and (paragraph-block? block)
-       (timestamp-block? (first (second block)))))
-
-(defn extract-timestamp
-  [block]
-  (-> block
-      second
-      first
-      second))
-
-(defn extract-headings
-  [blocks]
-  (loop [headings []
-         heading-children []
-         blocks (reverse blocks)
-         timestamps {}
-         last-pos nil]
-    (if (seq blocks)
-      (let [block (first blocks)
-            level (:level (second block))]
-        (cond
-          (paragraph-timestamp-block? block)
-          (let [timestamp (extract-timestamp block)
-                timestamps' (conj timestamps timestamp)]
-            (recur headings heading-children (rest blocks) timestamps' last-pos))
-
-          (heading-block? block)
-          (let [heading (-> (assoc (second block)
-                                   :children (reverse heading-children)
-                                   :timestamps timestamps)
-                            (assoc-in [:meta :end-pos] last-pos))
-                last-pos' (get-in heading [:meta :pos])]
-            (recur (conj headings heading) [] (rest blocks) {} last-pos'))
-
-          :else
-          (let [heading-children' (conj heading-children block)]
-            (recur headings heading-children' (rest blocks) timestamps last-pos))))
-      (reverse headings))))
-
-;; marker: DOING | IN-PROGRESS > TODO > WAITING | WAIT > DONE > CANCELED | CANCELLED
-;; priority: A > B > C
-(defn sort-tasks
-  [headings]
-  (let [markers ["DOING" "IN-PROGRESS" "TODO" "WAITING" "WAIT" "DONE" "CANCELED" "CANCELLED"]
-        markers (zipmap markers (reverse (range 1 (count markers))))
-        priorities ["A" "B" "C" "D" "E" "F" "G"]
-        priorities (zipmap priorities (reverse (range 1 (count priorities))))]
-    (sort (fn [t1 t2]
-            (let [m1 (get markers (:heading/marker t1) 0)
-                  m2 (get markers (:heading/marker t2) 0)
-                  p1 (get priorities (:heading/priority t1) 0)
-                  p2 (get priorities (:heading/priority t2) 0)]
-              (cond
-                (and (= m1 m2)
-                     (= p1 p2))
-                (compare (str (:heading/title t1))
-                         (str (:heading/title t2)))
-
-                (= m1 m2)
-                (> p1 p2)
-                :else
-                (> m1 m2))))
-          headings)))

+ 0 - 48
web/src/frontend/format/org_mode.cljs

@@ -1,48 +0,0 @@
-(ns frontend.format.org-mode
-  (:require ["mldoc_org" :as org]
-            [frontend.format.protocol :as protocol]
-            [frontend.util :as util]
-            [clojure.string :as string]))
-
-(def default-config
-  (js/JSON.stringify
-   #js {:toc false
-        :heading_number false
-        :keep_line_break false}))
-
-(def config-with-line-break
-  (js/JSON.stringify
-   #js {:toc false
-        :heading_number false
-        :keep_line_break true}))
-
-(def Org (.-MldocOrg org))
-
-(defrecord OrgMode [content]
-  protocol/Format
-  (toHtml [this]
-    (.parseHtml Org content default-config))
-  (toHtml [this config]
-    (.parseHtml Org content config)))
-
-(defn parse-json
-  ([content]
-   (parse-json content default-config))
-  ([content config]
-   (.parseJson Org content config)))
-
-(defn ->clj
-  [content]
-  (if (string/blank? content)
-    {}
-    (-> content
-       (parse-json)
-       (util/json->clj))))
-
-(defn inline-list->html
-  [json]
-  (.inlineListToHtmlStr Org json))
-
-(defn json->html
-  [json]
-  (.jsonToHtmlStr Org json default-config))

+ 0 - 4
web/src/frontend/format/protocol.cljs

@@ -1,4 +0,0 @@
-(ns frontend.format.protocol)
-
-(defprotocol Format
-  (toHtml [this] [this config]))

+ 0 - 44
web/src/frontend/fs.cljs

@@ -1,44 +0,0 @@
-(ns frontend.fs
-  (:require [frontend.util :as util]
-            [promesa.core :as p]))
-
-(defn mkdir
-  [dir]
-  (js/pfs.mkdir dir))
-
-(defn readdir
-  [dir]
-  (js/pfs.readdir dir))
-
-(defn read-file
-  [dir path]
-  (js/pfs.readFile (str dir "/" path)
-                   (clj->js {:encoding "utf8"})))
-
-(defn read-file-2
-  [dir path]
-  (js/pfs.readFile (str dir "/" path)
-                   (clj->js {})))
-
-(defn write-file
-  [dir path content]
-  (js/pfs.writeFile (str dir "/" path) content))
-
-(defn stat
-  [dir path]
-  (js/pfs.stat (str dir "/" path)))
-
-(defn create-if-not-exists
-  ([dir path]
-   (create-if-not-exists dir path ""))
-  ([dir path initial-content]
-   (util/p-handle
-    (stat dir path)
-    (fn [_stat] true)
-    (fn [error]
-      (write-file dir path initial-content)
-      false))))
-
-(comment
-  (def dir "/notes")
-  )

+ 0 - 133
web/src/frontend/git.cljs

@@ -1,133 +0,0 @@
-(ns frontend.git
-  (:refer-clojure :exclude [clone])
-  (:require [promesa.core :as p]
-            [frontend.util :as util]
-            [clojure.string :as string]))
-
-;; only support Github now
-(defn auth
-  [token]
-  {:username token
-   :password "x-oauth-basic"})
-
-(defn set-username-email
-  [dir username email]
-  (prn {:dir dir
-        :username username
-        :email email})
-  (util/p-handle (js/git.config (clj->js
-                   {:dir dir
-                    :path "user.name"
-                    :value username}))
-                 (fn [result]
-                   (js/git.config (clj->js
-                                   {:dir dir
-                                    :path "user.email"
-                                    :value email})))
-                 (fn [error]
-                   (prn "error:" error))))
-
-(defn with-auth
-  [token m]
-  (clj->js
-   (merge (auth token)
-          m)))
-
-(defn get-repo-dir
-  [repo-url]
-  (str "/" (last (string/split repo-url #"/"))))
-
-(defn clone
-  [repo-url token]
-  (js/git.clone (with-auth token
-                  {:dir (get-repo-dir repo-url)
-                   :url repo-url
-                   :corsProxy "https://cors.isomorphic-git.org"
-                   :singleBranch true
-                   :depth 1})))
-
-(defn list-files
-  [repo-url]
-  (js/git.listFiles (clj->js
-                     {:dir (get-repo-dir repo-url)
-                      :ref "HEAD"})))
-
-(defn fetch
-  [repo-url token]
-  (js/git.fetch (with-auth token
-                  {:dir (get-repo-dir repo-url)
-                   :ref "master"
-                   :singleBranch true})))
-
-(defn log
-  [repo-url token depth]
-  (js/git.log (with-auth token
-                {:dir (get-repo-dir repo-url)
-                 :ref "master"
-                 :depth depth
-                 :singleBranch true})))
-
-(defn pull
-  [repo-url token]
-  (js/git.pull (with-auth token
-                 {:dir (get-repo-dir repo-url)
-                  :ref "master"
-                  :singleBranch true})))
-(defn add
-  [repo-url file]
-  (js/git.add (clj->js
-               {:dir (get-repo-dir repo-url)
-                :filepath file})))
-
-;; TODO: cache email and name
-(defn commit
-  [repo-url message]
-  (js/git.commit (clj->js
-                  {:dir (get-repo-dir repo-url)
-                   :author {:name "Orgnote"
-                            :email "[email protected]"}
-                   :message message})))
-
-(defn push
-  [repo-url token]
-  (js/git.push (with-auth token
-                 {:dir (get-repo-dir repo-url)
-                  :remote "origin"
-                  :ref "master"
-                  })))
-
-(defn add-commit-push
-  [repo-url file message token push-ok-handler push-error-handler]
-  (util/p-handle
-   (let [files (if (coll? file) file [file])]
-     (doseq [file files]
-       (add repo-url file)))
-   (fn [_]
-     (util/p-handle
-      (commit repo-url message)
-      (fn [_]
-        (push repo-url token)
-        (push-ok-handler))
-      push-error-handler))))
-
-(defn add-commit
-  [repo-url file message commit-ok-handler commit-error-handler]
-  (let [get-seconds (fn []
-                      (/ (.getTime (js/Date.)) 1000))]
-    (let [t1 (get-seconds)]
-      (util/p-handle
-       (add repo-url file)
-       (fn [_]
-         (let [t2 (get-seconds)]
-           (prn "Add time: " (- t2 t1))
-           (util/p-handle
-            (commit repo-url message)
-            (fn []
-              (let [t3 (get-seconds)]
-                (prn "Commit time: " (- t3 t2)))
-              (prn "Commited")
-              (commit-ok-handler))
-            (fn [error]
-              (commit-error-handler error))))
-         )))
-    ))

+ 0 - 529
web/src/frontend/handler.cljs

@@ -1,529 +0,0 @@
-(ns frontend.handler
-  (:refer-clojure :exclude [clone load-file])
-  (:require [frontend.git :as git]
-            [frontend.fs :as fs]
-            [frontend.state :as state]
-            [frontend.db :as db]
-            [frontend.storage :as storage]
-            [frontend.util :as util]
-            [frontend.config :as config]
-            [clojure.walk :as walk]
-            [clojure.string :as string]
-            [promesa.core :as p]
-            [cljs-bean.core :as bean]
-            [reitit.frontend.easy :as rfe]
-            [goog.crypt.base64 :as b64]
-            [goog.object :as gobj]
-            [goog.dom :as gdom]
-            [rum.core :as rum]
-            [datascript.core :as d]
-            [frontend.utf8 :as utf8]
-            [frontend.image :as image])
-  (:import [goog.events EventHandler]))
-
-;; We only support Github token now
-(defn load-file
-  [repo-url path state-handler]
-  (util/p-handle (fs/read-file (git/get-repo-dir repo-url) path)
-                 (fn [content]
-                   (state-handler content))))
-
-(defn- hidden?
-  [path patterns]
-  (some (fn [pattern]
-          (or
-           (= path pattern)
-           (and (string/starts-with? pattern "/")
-                (= (str "/" (first (string/split path #"/")))
-                   pattern)))) patterns))
-
-(defn load-files
-  [repo-url]
-  (util/p-handle (git/list-files repo-url)
-                 (fn [files]
-                   (when (> (count files) 0)
-                     (let [files (js->clj files)]
-                       ;; FIXME: don't load blobs
-                       (if (contains? (set files) config/hidden-file)
-                         (load-file repo-url config/hidden-file
-                                    (fn [patterns-content]
-                                      (when patterns-content
-                                        (let [patterns (string/split patterns-content #"\n")
-                                              files (remove (fn [path] (hidden? path patterns)) files)]
-                                          (db/transact-files! repo-url files)))))
-                         (p/promise (db/transact-files! repo-url files))))))))
-
-
-;; TODO: remove this
-(declare load-repo-to-db!)
-
-(defn get-latest-commit
-  [handler]
-  (-> (git/log (db/get-current-repo)
-               (db/get-github-token)
-               1)
-      (.then (fn [commits]
-               (handler (first commits))))
-      (.catch (fn [error]
-                (prn "get latest commit failed: " error)))))
-
-(defonce latest-commit (atom nil))
-
-;; TODO: Maybe replace with fetch?
-;; TODO: callback hell
-(defn pull
-  [repo-url token]
-  (when (and (nil? (:git-error @state/state))
-             (nil? (:git-status @state/state)))
-    (util/p-handle
-     (git/pull repo-url token)
-     (fn [result]
-       (prn "pull successfully!")
-       (get-latest-commit
-        (fn [commit]
-          (when (or (nil? @latest-commit)
-                    (and @latest-commit
-                         commit
-                         (not= (gobj/get commit "oid")
-                               (gobj/get @latest-commit "oid"))))
-            (prn "New commit oid: " (gobj/get commit "oid"))
-            (-> (load-files repo-url)
-                (p/then
-                 (fn []
-                   (load-repo-to-db! repo-url)))))
-          (reset! latest-commit commit)))))))
-
-(defn periodically-pull
-  [repo-url]
-  (when-let [token (db/get-github-token)]
-    (pull repo-url token)
-    (js/setInterval #(pull repo-url token)
-                    (* 60 1000))))
-
-(defn git-add-commit
-  [repo-url file message content]
-  (swap! state/state assoc :git-status :commit)
-  (db/reset-file! repo-url file content)
-  (git/add-commit repo-url file message
-                  (fn []
-                    (swap! state/state assoc
-                           :git-status :should-push))
-                  (fn [error]
-                    (prn "Commit failed, "
-                         {:repo repo-url
-                          :file file
-                          :message message})
-                    (swap! state/state assoc
-                           :git-status :commit-failed
-                           :git-error error))))
-
-;; TODO: update latest commit
-(defn push
-  [repo-url file]
-  (when (and (= :should-push (:git-status @state/state))
-             (nil? (:git-error @state/state)))
-    (swap! state/state assoc :git-status :push)
-    (let [token (db/get-github-token)]
-      (util/p-handle
-       (git/push repo-url token)
-       (fn []
-         (prn "Push successfully!")
-         (swap! state/state assoc
-                :git-status nil
-                :git-error nil)
-         ;; TODO: update latest-commit
-         (get-latest-commit
-          (fn [commit]
-            (reset! latest-commit commit))))
-       (fn [error]
-         (prn "Failed to push, error: " error)
-         (swap! state/state assoc
-                :git-status :push-failed
-                :git-error error))))))
-
-(defn clone
-  [repo]
-  (let [token (db/get-github-token)]
-    (util/p-handle
-     (do
-       (db/set-repo-cloning repo true)
-       (git/clone repo token))
-     (fn []
-       (db/set-repo-cloning repo false)
-       (db/mark-repo-as-cloned repo)
-       (db/set-current-repo! repo)
-       ;; load contents
-       (load-files repo))
-     (fn [e]
-       (db/set-repo-cloning repo false)
-       (prn "Clone failed, reason: " e)))))
-
-(defn new-notification
-  [text]
-  (js/Notification. "Logseq" #js {:body text
-                                  ;; :icon logo
-                                  }))
-
-(defn request-notifications
-  []
-  (util/p-handle (.requestPermission js/Notification)
-                 (fn [result]
-                   (storage/set :notification-permission-asked? true)
-
-                   (when (= "granted" result)
-                     (storage/set :notification-permission? true)))))
-
-(defn request-notifications-if-not-asked
-  []
-  (when-not (storage/get :notification-permission-asked?)
-    (request-notifications)))
-
-;; notify deadline or scheduled tasks
-(defn run-notify-worker!
-  []
-  (when (storage/get :notification-permission?)
-    (let [notify-fn (fn []
-                      (let [tasks (:tasks @state/state)
-                            tasks (flatten (vals tasks))]
-                        (doseq [{:keys [marker title] :as task} tasks]
-                          (when-not (contains? #{"DONE" "CANCElED" "CANCELLED"} marker)
-                            (doseq [[type {:keys [date time] :as timestamp}] (:timestamps task)]
-                              (let [{:keys [year month day]} date
-                                    {:keys [hour min]
-                                     :or {hour 9
-                                          min 0}} time
-                                    now (util/get-local-date)]
-                                (when (and (contains? #{"Scheduled" "Deadline"} type)
-                                           (= (assoc date :hour hour :minute min) now))
-                                  (let [notification-text (str type ": " (second (first title)))]
-                                    (new-notification notification-text)))))))))]
-      (notify-fn)
-      (js/setInterval notify-fn (* 1000 60)))))
-
-(defn show-notification!
-  [text]
-  (swap! state/state assoc
-         :notification/show? true
-         :notification/text text)
-  (js/setTimeout #(swap! state/state assoc
-                         :notification/show? false
-                         :notification/text nil)
-                 3000))
-
-(defn alter-file
-  ([path commit-message content]
-   (alter-file path commit-message content true))
-  ([path commit-message content redirect?]
-   (let [token (db/get-github-token)
-         repo-url (db/get-current-repo)]
-     (util/p-handle
-      (fs/write-file (git/get-repo-dir repo-url) path content)
-      (fn [_]
-        (when redirect?
-          (rfe/push-state :file {:path (b64/encodeString path)}))
-        (git-add-commit repo-url path commit-message content))))))
-
-(defn clear-storage
-  [repo-url]
-  (js/window.pfs._idb.wipe)
-  (clone repo-url))
-
-;; TODO: utf8 encode performance
-(defn check
-  [heading]
-  (let [{:heading/keys [repo file marker meta uuid]} heading
-        pos (:pos meta)
-        repo (db/entity (:db/id repo))
-        file (db/entity (:db/id file))
-        repo-url (:repo/url repo)
-        file (:file/path file)
-        token (db/get-github-token)]
-    (when-let [content (db/get-file-content repo-url file)]
-      (let [encoded-content (utf8/encode content)
-            content' (str (utf8/substring encoded-content 0 pos)
-                          (-> (utf8/substring encoded-content pos)
-                              (string/replace-first marker "DONE")))]
-        (util/p-handle
-         (fs/write-file (git/get-repo-dir repo-url) file content')
-         (fn [_]
-           (prn "check successfully, " file)
-           (git-add-commit repo-url file
-                           (util/format "`%s` marked as DONE." marker)
-                           content')))))))
-
-(defn uncheck
-  [heading]
-  (let [{:heading/keys [repo file marker meta]} heading
-        pos (:pos meta)
-        repo (db/entity (:db/id repo))
-        file (db/entity (:db/id file))
-        repo-url (:repo/url repo)
-        file (:file/path file)
-        token (db/get-github-token)]
-    (when-let [content (db/get-file-content repo-url file)]
-      (let [encoded-content (utf8/encode content)
-            content' (str (utf8/substring encoded-content 0 pos)
-                          (-> (utf8/substring encoded-content pos)
-                              (string/replace-first "DONE" "TODO")))]
-        (util/p-handle
-         (fs/write-file (git/get-repo-dir repo-url) file content')
-         (fn [_]
-           (prn "uncheck successfully, " file)
-           (git-add-commit repo-url file
-                           "DONE rollbacks to TODO."
-                           content')))))))
-
-(defn remove-non-text-files
-  [files]
-  (remove
-   (fn [file]
-     (not (contains?
-           #{"org"
-             "md"
-             "markdown"
-             "txt"}
-           (string/lower-case (last (string/split file #"\."))))))
-   files))
-
-(defn load-all-contents!
-  [repo-url ok-handler]
-  (let [files (db/get-repo-files repo-url)
-        files (remove-non-text-files files)]
-    (-> (p/all (for [file files]
-                 (load-file repo-url file
-                            (fn [content]
-                              (db/set-file-content! repo-url file content)))))
-        (p/then
-         (fn [_]
-           (ok-handler))))))
-
-(defonce headings-atom (atom nil))
-
-(defn load-repo-to-db!
-  [repo-url]
-  (load-all-contents!
-   repo-url
-   (fn []
-     (let [headings (db/extract-all-headings repo-url)]
-       (reset! headings-atom headings)
-       (db/reset-headings! repo-url headings)))))
-
-
-;; (defn sync
-;;   []
-;;   (let [[_user token repos] (get-user-token-repos)]
-;;     (doseq [repo repos]
-;;       (pull repo token))))
-
-;; {:resp {:user {:name "tiensonqin", :email "[email protected]", :id "2e3a6ebf-9ee6-40a8-8734-47f9f2697a1a", :created_at "2020-03-01T04:27:08Z"}, :tokens [{:user_id "2e3a6ebf-9ee6-40a8-8734-47f9f2697a1a", :oauth_token "", :id "203ca06a-0721-4322-b184-931c4c5f3dc3", :created_at "2020-03-01T04:27:08Z", :oauth_id "479169", :oauth_type "github"}], :repos [{:created_at "2020-03-01T04:27:43Z", :user_id "2e3a6ebf-9ee6-40a8-8734-47f9f2697a1a", :url "https://github.com/tiensonqin/notes", :id "0fcd3cfb-ce1f-4f9c-9d88-eecd8776777d"}]}}
-
-(defn- extract-github-token
-  [{:keys [user tokens repos]}]
-  (:oauth_token (first tokens)))
-
-(defn get-github-access-token
-  []
-  (util/fetch (str config/api "me")
-              (fn [resp]
-                (when-let [github-token (extract-github-token resp)]
-                  (prn {:github-token github-token})
-                  (db/transact-github-token! github-token)))
-              (fn [_error]
-                ;; (prn "Get token failed, error: " error)
-                )))
-
-;; org-journal format, something like `* Tuesday, 06/04/13`
-(defn default-month-journal-content
-  []
-  (let [{:keys [year month day]} (util/get-date)
-        last-day (util/get-month-last-day)
-        month-pad (if (< month 10) (str "0" month) month)]
-    (->> (map
-           (fn [day]
-             (let [day-pad (if (< day 10) (str "0" day) day)
-                   weekday (util/get-weekday (js/Date. year (dec month) (dec day)))]
-               (util/format "* %s, %s/%s/%d\n\n" weekday month-pad day-pad year)))
-           (range 1 (inc last-day)))
-         (apply str))))
-
-;; journals
-(defn create-month-journal-if-not-exists
-  [repo-url]
-  (let [repo-dir (git/get-repo-dir repo-url)
-        path (util/current-journal-path)
-        file-path (str "/" path)
-        default-content (default-month-journal-content)]
-    (->
-     (util/p-handle
-      (fs/mkdir (str repo-dir "/journals"))
-      (fn [result]
-        (fs/create-if-not-exists repo-dir file-path default-content))
-      (fn [error]
-        (fs/create-if-not-exists repo-dir file-path default-content)))
-     (util/p-handle
-      (fn [file-exists?]
-        (if file-exists?
-          (prn "Month journal already exists!")
-          (do
-            (prn "create a month journal")
-            (git-add-commit repo-url path "create a month journal" default-content))))
-      (fn [error]
-        (prn error))))))
-
-(defn clone-and-pull
-  [repo]
-  (p/then (clone repo)
-          (fn []
-            (create-month-journal-if-not-exists repo)
-            (periodically-pull repo))))
-
-(defn set-route-match!
-  [route]
-  (swap! state/state assoc :route-match route))
-
-(defn set-ref-component!
-  [k ref]
-  (swap! state/state assoc :ref-components k ref))
-
-(defn set-root-component!
-  [comp]
-  (swap! state/state assoc :root-component comp))
-
-(defn re-render!
-  []
-  (when-let [comp (get @state/state :root-component)]
-    (when-not (:edit? @state/state)
-      (rum/request-render comp))))
-
-(defn db-listen-to-tx!
-  []
-  (d/listen! db/conn :persistence
-             (fn [tx-report] ;; FIXME do not notify with nil as db-report
-               ;; FIXME do not notify if tx-data is empty
-               (when-let [db (:db-after tx-report)]
-                 (prn "DB changed, re-rendered!")
-                 (re-render!)
-                 (js/setTimeout (fn []
-                                  (db/persist db)) 0)))))
-
-(defn periodically-push-tasks
-  [repo-url]
-  (let [token (db/get-github-token)
-        push (fn []
-               (push repo-url token))]
-    (js/setInterval push
-                    (* 10 1000))))
-
-(defn periodically-pull-and-push
-  [repo-url]
-  (periodically-pull repo-url)
-  ;; (periodically-push-tasks repo-url)
-  )
-
-(defn set-state-kv!
-  [key value]
-  (swap! state/state assoc key value))
-
-(defn edit-journal!
-  [content journal]
-  (swap! state/state assoc
-         :edit? true
-         :edit-journal journal))
-
-(defn set-latest-journals!
-  []
-  (set-state-kv! :latest-journals (db/get-latest-journals {})))
-
-(defn set-journal-content!
-  [uuid content]
-  (swap! state/state update :latest-journals
-         (fn [journals]
-           (mapv
-            (fn [journal]
-              (if (= (:uuid journal) uuid)
-                (assoc journal :content content)
-                journal))
-            journals))))
-
-(defn save-current-edit-journal!
-  [edit-content]
-  (let [{:keys [edit-journal]} @state/state
-        {:keys [start-pos end-pos]} edit-journal]
-    (swap! state/state assoc
-           :edit? false
-           :edit-journal nil)
-    (when-not (= edit-content (:content edit-journal)) ; if new changes
-      (let [path (:file-path edit-journal)
-            current-journals (db/get-file path)
-            new-content (utf8/insert! current-journals start-pos end-pos edit-content)]
-        (set-state-kv! :latest-journals (db/get-latest-journals {:content new-content}))
-        (alter-file path "Auto save" new-content false)))))
-
-(defn render-local-images!
-  []
-  (let [images (array-seq (gdom/getElementsByTagName "img"))
-        get-src (fn [image] (.getAttribute image "src"))
-        local-images (filter
-                      (fn [image]
-                        (let [src (get-src image)]
-                          (and src
-                               (not (or (string/starts-with? src "http://")
-                                        (string/starts-with? src "https://"))))))
-                      images)]
-    (doseq [img local-images]
-      (gobj/set img
-                "onerror"
-                (fn []
-                  (gobj/set (gobj/get img "style")
-                            "display" "none")))
-      (let [path (get-src img)
-            path (if (= (first path) \.)
-                   (subs path 1)
-                   path)]
-        (util/p-handle
-         (fs/read-file-2 (git/get-repo-dir (db/get-current-repo))
-                         path)
-         (fn [blob]
-           (let [blob (js/Blob. (array blob) (clj->js {:type "image"}))
-                 img-url (image/create-object-url blob)]
-             (gobj/set img "src" img-url)
-             (gobj/set (gobj/get img "style")
-                       "display" "initial"))))))))
-
-;; FIXME:
-(defn set-username-email
-  []
-  (git/set-username-email
-   (git/get-repo-dir (db/get-current-repo))
-   "Tienson Qin"
-   "[email protected]"))
-
-(defn load-more-journals!
-  []
-  (let [journals (:latest-journals @state/state)]
-    (when-let [title (:title (last journals))]
-      (let [before-date (last (string/split title #", "))
-            more-journals (->> (db/get-latest-journals {:before-date before-date
-                                                        :days 4})
-                               (drop 1))
-            journals (concat journals more-journals)]
-        (set-state-kv! :latest-journals journals)))))
-
-(defn start!
-  []
-  (db/restore!)
-  (db-listen-to-tx!)
-  (when-let [first-repo (first (db/get-repos))]
-    (db/set-current-repo! first-repo))
-  (let [repos (db/get-repos)]
-    (doseq [repo repos]
-      (create-month-journal-if-not-exists repo)
-      (periodically-pull-and-push repo))))
-
-(comment
-  (util/p-handle (fs/read-file (git/get-repo-dir (db/get-current-repo)) "test.org")
-                 (fn [content]
-                   (prn content)))
-
-  (pull (db/get-current-repo) (db/get-github-token))
-  )

+ 0 - 103
web/src/frontend/image.cljs

@@ -1,103 +0,0 @@
-(ns frontend.image
-  (:require [goog.object :as gobj]
-            [frontend.blob :as blob]
-            ["/frontend/exif" :as exif]
-            [frontend.util :as util]
-            [clojure.string :as string]))
-
-(defn reverse?
-  [exif-orientation]
-  (contains? #{5 6 7 8} exif-orientation))
-
-(defn re-scale
-  [exif-orientation width height max-width max-height]
-  (let [[width height]
-        (if (reverse? exif-orientation)
-          [height width]
-          [width height])]
-    (let [ratio (/ width height)
-          to-width (if (> width max-width) max-width width)
-          to-height (if (> height max-height) max-height height)
-          new-ratio (/ to-width to-height)]
-      (let [[w h] (cond
-                    (> new-ratio ratio)
-                    [(* ratio to-height) to-height]
-
-                    (< new-ratio ratio)
-                    [to-width (/ to-width ratio)]
-
-                    :else
-                    [to-width to-height])]
-        [(int w) (int h)]))))
-
-
-(defn fix-orientation
-  "Given image and exif orientation, ensure the photo is displayed
-  rightside up"
-  [img exif-orientation cb max-width max-height]
-  (let [off-canvas (js/document.createElement "canvas")
-        ctx ^js (.getContext off-canvas "2d")
-        width (gobj/get img "width")
-        height (gobj/get img "height")
-        [to-width to-height] (re-scale exif-orientation width height max-width max-height)]
-    (gobj/set ctx "imageSmoothingEnabled" false)
-    (set! (.-width off-canvas) to-width)
-    (set! (.-height off-canvas) to-height)
-    ;; rotate
-    (let [[width height] (if (reverse? exif-orientation)
-                           [to-height to-width]
-                           [to-width to-height])]
-      (case exif-orientation
-        2 (.transform ctx -1  0  0  1 width  0)
-        3 (.transform ctx -1  0  0 -1 width  height)
-        4 (.transform ctx  1  0  0 -1 0      height)
-        5 (.transform ctx  0  1  1  0 0      0)
-        6 (.transform ctx  0  1 -1  0 height 0)
-        7 (.transform ctx  0 -1 -1  0 height width)
-        8 (.transform ctx  0 -1  1  0 0      width)
-        (.transform ctx  1  0  0  1 0      0))
-      (.drawImage ctx img 0 0 width height))
-    (cb off-canvas)))
-
-(defn get-orientation
-  [img cb max-width max-height]
-  (exif/getEXIFOrientation
-   img
-   (fn [orientation]
-     (fix-orientation img orientation cb max-width max-height))))
-
-(defn create-object-url
-  [file]
-  (.createObjectURL (or (.-URL js/window)
-                        (.-webkitURL js/window))
-                    file))
-
-;; (defn build-image
-;;   []
-;;   (let [img (js/Image.)]
-;;     ))
-
-(defn upload
-  [files file-cb & {:keys [max-width max-height]
-                    :or {max-width 1920
-                         max-height 1080}}]
-  (doseq [file (array-seq files)]
-    (let [file-type (gobj/get file "type")
-          ymd (->> (vals (util/year-month-day-padded))
-                   (string/join "_"))
-          file-name (str ymd "_" (gobj/get file "name"))]
-      (if (= 0 (.indexOf type "image/"))
-        (let [img (js/Image.)]
-          (set! (.-onload img)
-                (fn []
-                  (get-orientation img
-                                   (fn [^js off-canvas]
-                                     (let [file-form-data ^js (js/FormData.)
-                                           data-url (.toDataURL off-canvas)
-                                           blob (blob/blob data-url)]
-                                       (.append file-form-data "file" blob)
-                                       (file-cb file file-form-data file-name file-type)))
-                                   max-width
-                                   max-height)))
-          (set! (.-src img)
-                (create-object-url file)))))))

+ 0 - 117
web/src/frontend/mixins.cljs

@@ -1,117 +0,0 @@
-(ns frontend.mixins
-  (:require [rum.core :as rum]
-            [goog.dom :as dom])
-  (:import [goog.events EventHandler]))
-
-(defn detach
-  "Detach all event listeners."
-  [state]
-  (some-> state ::event-handler .removeAll))
-
-(defn listen
-  "Register an event `handler` for events of `type` on `target`."
-  [state target type handler & [opts]]
-  (when-let [event-handler (::event-handler state)]
-    (.listen event-handler target (name type) handler (clj->js opts))))
-
-(def event-handler-mixin
-  "The event handler mixin."
-  {:will-mount
-   (fn [state]
-     (assoc state ::event-handler (EventHandler.)))
-   :will-unmount
-   (fn [state]
-     (detach state)
-     (dissoc state ::event-handler))})
-
-;; (defn timeout-mixin
-;;   "The setTimeout mixin."
-;;   [name t f]
-;;   {:will-mount
-;;    (fn [state]
-;;      (assoc state name (util/set-timeout t f)))
-;;    :will-unmount
-;;    (fn [state]
-;;      (let [timeout (get state name)]
-;;        (util/clear-timeout timeout)
-;;        (dissoc state name)))})
-
-;; (defn interval-mixin
-;;   "The setInterval mixin."
-;;   [name t f]
-;;   {:will-mount
-;;    (fn [state]
-;;      (assoc state name (util/set-interval t f)))
-;;    :will-unmount
-;;    (fn [state]
-;;      (when-let [interval (get state name)]
-;;        (util/clear-interval interval))
-;;      (dissoc state name))})
-
-(defn hide-when-esc-or-outside
-  [state show? & {:keys [on-hide node show-fn]}]
-  (let [node (or node (rum/dom-node state))
-        show? (if (and show-fn (fn? show-fn))
-                (show-fn)
-                @show?)]
-    (when show?
-      (listen state js/window "click"
-              (fn [e]
-                ;; If the click target is outside of current node
-                (when-not (dom/contains node (.. e -target))
-                  (on-hide e))))
-
-      (listen state js/window "keydown"
-              (fn [e]
-                (case (.-keyCode e)
-                  ;; Esc
-                  27 (on-hide e)
-                  nil))))))
-
-(defn event-mixin
-  ([attach-listeners]
-   (event-mixin attach-listeners identity))
-  ([attach-listeners init-callback]
-   (merge
-    event-handler-mixin
-    {:init (fn [state props]
-             (init-callback state))
-     :did-mount (fn [state]
-                  (attach-listeners state)
-                  state)
-     :did-remount (fn [old-state new-state]
-                    (detach old-state)
-                    (attach-listeners new-state)
-                    new-state)})))
-
-;; TODO: is it possible that multiple nested components using the same key `:open?`?
-(defn modal
-  []
-  (let [k :open?]
-    (event-mixin
-     (fn [state]
-       (let [open? (get state k)]
-         (hide-when-esc-or-outside state
-                                    open?
-                                    :on-hide (fn []
-                                                (reset! open? false)))))
-     (fn [state]
-       (let [open? (atom false)
-             component (:rum/react-component state)]
-         (add-watch open? ::open
-                    (fn [_ _ _ _]
-                      (rum/request-render component)))
-         (assoc state
-                k open?
-                :close-fn (fn []
-                            (reset! open? false))
-                :open-fn (fn []
-                           (reset! open? true))
-                :toggle-fn (fn []
-                             (swap! open? not))))))))
-
-(defn will-mount-effect
-  [handler]
-  {:will-mount (fn [state]
-                 (handler (:rum/args state))
-                 state)})

+ 0 - 16
web/src/frontend/page.cljs

@@ -1,16 +0,0 @@
-(ns frontend.page
-  (:require [rum.core :as rum]
-            [frontend.state :as state]
-            [frontend.handler :as handler]))
-
-(rum/defc current-page < rum/reactive
-  {:did-mount (fn [state]
-                (handler/set-root-component! (:rum/react-component state))
-                state)}
-  []
-  (let [state (rum/react state/state)
-        route-match (:route-match state)]
-    (if route-match
-      (when-let [view (:view (:data route-match))]
-        (view route-match))
-      [:div "404 Page"])))

+ 0 - 37
web/src/frontend/routes.cljs

@@ -1,37 +0,0 @@
-(ns frontend.routes
-  (:require [frontend.components.home :as home]
-            [frontend.components.sidebar :as sidebar]
-            [frontend.components.repo :as repo]
-            [frontend.components.file :as file]
-            [frontend.components.agenda :as agenda]
-            ))
-
-(def routes
-  [["/"
-    {:name :home
-     :view home/home}]
-
-   ["/repo/add"
-    {:name :repo-add
-     :view repo/add-repo}]
-
-   ["/file/:path"
-    {:name :file
-     :view file/file}]
-
-   ["/file/:path/edit"
-    {:name :file-edit
-     :view file/edit}]
-
-   ["/agenda"
-    {:name :agenda
-     :view agenda/agenda}]
-
-   ;; TODO: edit file
-   ;; Settings
-   ;; ["/item/:id"
-   ;;  {:name ::item
-   ;;   :view item-page
-   ;;   :parameters {:path {:id int?}
-   ;;                :query {(ds/opt :foo) keyword?}}}]
-   ])

+ 0 - 59
web/src/frontend/rum.cljs

@@ -1,59 +0,0 @@
-(ns frontend.rum
-  (:require [clojure.string :as s]
-            [clojure.set :as set]
-            [clojure.walk :as w]))
-
-;; copy from https://github.com/priornix/antizer/blob/35ba264cf48b84e6597743e28b3570d8aa473e74/src/antizer/core.cljs
-
-(defn kebab-case->camel-case
-  "Converts from kebab case to camel case, eg: on-click to onClick"
-  [input]
-  (let [words (s/split input #"-")
-        capitalize (->> (rest words)
-                        (map #(apply str (s/upper-case (first %)) (rest %))))]
-    (apply str (first words) capitalize)))
-
-(defn map-keys->camel-case
-  "Stringifys all the keys of a cljs hashmap and converts them
-   from kebab case to camel case. If :html-props option is specified,
-   then rename the html properties values to their dom equivalent
-   before conversion"
-  [data & {:keys [html-props]}]
-  (let [convert-to-camel (fn [[key value]]
-                           [(kebab-case->camel-case (name key)) value])]
-    (w/postwalk (fn [x]
-                  (if (map? x)
-                    (let [new-map (if html-props
-                                    (set/rename-keys x {:class :className :for :htmlFor})
-                                    x)]
-                      (into {} (map convert-to-camel new-map)))
-                    x))
-                data)))
-
-;; adapted from https://github.com/tonsky/rum/issues/20
-(defn adapt-class [react-class]
-     (fn [& args]
-       (let [[opts children] (if (map? (first args))
-                               [(first args) (rest args)]
-                               [{} args])
-             type# (first children)
-             ;; we have to make sure to check if the children is sequential
-             ;; as a list can be returned, eg: from a (for)
-             new-children (if (sequential? type#)
-                            (let [result (sablono.interpreter/interpret children)]
-                              (if (sequential? result)
-                                result
-                                [result]))
-                            children)
-             ;; convert any options key value to a react element, if
-             ;; a valid html element tag is used, using sablono
-             vector->react-elems (fn [[key val]]
-                                   (if (sequential? val)
-                                     [key (sablono.interpreter/interpret val)]
-                                     [key val]))
-             new-options (into {} (map vector->react-elems opts))]
-         ;; (.dir js/console new-children)
-         (apply js/React.createElement react-class
-           ;; sablono html-to-dom-attrs does not work for nested hashmaps
-           (clj->js (map-keys->camel-case new-options :html-props true))
-           new-children))))

+ 0 - 12
web/src/frontend/state.cljs

@@ -1,12 +0,0 @@
-(ns frontend.state)
-
-;; TODO: replace this with datascript
-(def state (atom
-            {:route-match nil
-             :notification/show? false
-             :notification/text nil
-             :root-component nil
-             :git-status nil
-             :git-error nil
-             :edit? false
-             :latest-journals []}))

+ 0 - 20
web/src/frontend/storage.cljs

@@ -1,20 +0,0 @@
-(ns frontend.storage
-  (:refer-clojure :exclude [get set remove])
-  (:require [cljs.reader :as reader]))
-
-;; TODO: deprecate this, will persistent datascript
-(defn get
-  [key]
-  (reader/read-string ^js (.getItem js/localStorage (name key))))
-
-(defn set
-  [key value]
-  (.setItem ^js js/localStorage (name key) (pr-str value)))
-
-(defn remove
-  [key]
-  (.removeItem ^js js/localStorage (name key)))
-
-(defn clear
-  []
-  (.clear ^js js/localStorage))

+ 0 - 142
web/src/frontend/ui.cljs

@@ -1,142 +0,0 @@
-(ns frontend.ui
-  (:require [rum.core :as rum]
-            [frontend.rum :as r]
-            ["react-transition-group" :refer [TransitionGroup CSSTransition]]
-            ["react-textarea-autosize" :as Textarea]
-            [frontend.util :as util]
-            [frontend.mixins :as mixins]
-            [frontend.state :as state]
-            [goog.object :as gobj]
-            [goog.dom :as gdom]))
-
-(defonce transition-group (r/adapt-class TransitionGroup))
-(defonce css-transition (r/adapt-class CSSTransition))
-
-(defonce textarea-autosize (r/adapt-class (gobj/get Textarea "default")))
-
-(rum/defc dropdown-content-wrapper [state content]
-  [:div.origin-top-right.absolute.right-0.mt-2.w-48.rounded-md.shadow-lg
-   {:class (case state
-             "entering" "transition ease-out duration-100 transform opacity-0 scale-95"
-             "entered" "transition ease-out duration-100 transform opacity-100 scale-100"
-             "exiting" "transition ease-in duration-75 transform opacity-100 scale-100"
-             "exited" "transition ease-in duration-75 transform opacity-0 scale-95")}
-   content])
-
-;; public exports
-(rum/defcs dropdown < (mixins/modal)
-  [state content]
-  (let [{:keys [open? toggle-fn]} state]
-    [:div.ml-3.relative
-     [:div
-      [:button.max-w-xs.flex.items-center.text-sm.rounded-full.focus:outline-none.focus:shadow-outline
-       {:on-click toggle-fn}
-       [:img.h-8.w-8.rounded-full
-        {:src
-         "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"}]]]
-     (css-transition
-      {:in @open? :timeout 0}
-      (fn [state]
-        (dropdown-content-wrapper state content)))]))
-
-(defn dropdown-with-links
-  [links]
-  (dropdown
-   [:div.py-1.rounded-md.bg-white.shadow-xs
-    (for [{:keys [options title]} links]
-      [:a.block.px-4.py-2.text-sm.text-gray-700.hover:bg-gray-100.transition.ease-in-out.duration-150
-       (merge {:key (cljs.core/random-uuid)}
-              options)
-       title])]))
-
-(rum/defc button
-  [text on-click]
-  [:button.inline-flex.items-center.px-3.py-2.border.border-transparent.text-sm.leading-4.font-medium.rounded-md.text-white.bg-indigo-600.hover:bg-indigo-500.focus:outline-none.focus:border-indigo-700.focus:shadow-outline-indigo.active:bg-indigo-700.transition.ease-in-out.duration-150.mt-1
-   {:type "button"
-    :on-click on-click}
-   text])
-
-(rum/defc notification-content
-  [state text]
-  [:div.fixed.inset-0.flex.items-end.justify-center.px-4.py-6.pointer-events-none.sm:p-6.sm:items-start.sm:justify-end
-   [:div.max-w-sm.w-full.bg-white.shadow-lg.rounded-lg.pointer-events-auto
-    {:class (case state
-              "entering" "transition ease-out duration-300 transform opacity-0 translate-y-2 sm:translate-x-0"
-              "entered" "transition ease-out duration-300 transform translate-y-0 opacity-100 sm:translate-x-0"
-              "exiting" "transition ease-in duration-100 opacity-100"
-              "exited" "transition ease-in duration-100 opacity-0")}
-    [:div.rounded-lg.shadow-xs.overflow-hidden
-     [:div.p-4
-      [:div.flex.items-start
-       [:div.flex-shrink-0
-        [:svg.h-6.w-6.text-green-400
-         {:stroke "currentColor", :viewBox "0 0 24 24", :fill "none"}
-         [:path
-          {:d "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z",
-           :stroke-width "2",
-           :stroke-linejoin "round",
-           :stroke-linecap "round"}]]]
-       [:div.ml-3.w-0.flex-1.pt-0.5
-        [:p.text-sm.leading-5.font-medium.text-gray-900
-         text]]
-       [:div.ml-4.flex-shrink-0.flex
-        [:button.inline-flex.text-gray-400.focus:outline-none.focus:text-gray-500.transition.ease-in-out.duration-150
-         {:on-click (fn []
-                      (swap! state/state assoc :notification/show? false))}
-         [:svg.h-5.w-5
-          {:fill "currentColor", :viewBox "0 0 20 20"}
-          [:path
-           {:clip-rule "evenodd",
-            :d
-            "M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z",
-            :fill-rule "evenodd"}]]]]]]]]])
-
-(rum/defc notification < rum/reactive
-  []
-  (let [{:keys [:notification/show? :notification/text]} (rum/react state/state)]
-    (css-transition
-     {:in show? :timeout 100}
-     (fn [state]
-       (notification-content state text)))))
-
-(rum/defc checkbox
-  [option]
-  [:input.form-checkbox.h-4.w-4.text-indigo-600.transition.duration-150.ease-in-out
-   (merge {:type "checkbox"} option)])
-
-(rum/defc badge
-  [text option]
-  [:span.inline-flex.items-center.px-2.5.py-0.5.rounded-full.text-xs.font-medium.leading-4.bg-purple-100.text-purple-800
-   option
-   text])
-
-;; scroll
-(defn main-node
-  []
-  (first (array-seq (js/document.querySelectorAll "main"))))
-
-(defn get-scroll-top []
-  (.-scrollTop (main-node)))
-
-(defn on-scroll
-  [on-load]
-  (let [node (main-node)
-        full-height (gobj/get node "scrollHeight")
-        scroll-top (gobj/get node "scrollTop")
-        bottom-reached? (>= (- full-height scroll-top 300) 0)]
-    (when bottom-reached?
-      (on-load))))
-
-(defn attach-listeners
-  "Attach scroll and resize listeners."
-  [state]
-  (let [opts (-> state :rum/args second)
-        debounced-on-scroll (util/debounce 500 #(on-scroll (:on-load opts)))]
-    (mixins/listen state (main-node) :scroll debounced-on-scroll)))
-
-(rum/defcs infinite-list <
-  (mixins/event-mixin attach-listeners)
-  "Render an infinite list."
-  [state body {:keys [on-load]
-               :as opts}]
-  body)

+ 0 - 31
web/src/frontend/utf8.cljs

@@ -1,31 +0,0 @@
-(ns frontend.utf8
-  (:require [goog.object :as gobj]))
-
-(defonce encoder
-  (js/TextEncoder. "utf-8"))
-
-(defonce decoder
-  (js/TextDecoder. "utf-8"))
-
-(defn encode
-  [s]
-  (.encode encoder s))
-
-(defn substring
-  ([arr start]
-   (->> (.subarray arr start)
-        (.decode decoder)))
-  ([arr start end]
-   (->> (.subarray arr start end)
-        (.decode decoder))))
-
-(defn length
-  [arr]
-  (gobj/get arr "length"))
-
-(defn insert!
-  [s start-pos end-pos content]
-  (let [arr (encode s)]
-    (str (substring arr 0 start-pos)
-         content
-         (substring arr end-pos))))

+ 0 - 184
web/src/frontend/util.cljs

@@ -1,184 +0,0 @@
-(ns frontend.util
-  (:require [goog.object :as gobj]
-            [promesa.core :as p]
-            [clojure.walk :as walk]
-            [clojure.string :as string]
-            [cljs-bean.core :as bean]))
-
-(defn evalue
-  [event]
-  (gobj/getValueByKeys event "target" "value"))
-
-(defn p-handle
-  ([p ok-handler]
-   (p-handle p ok-handler (fn [error] (prn "p-handle error: " error))))
-  ([p ok-handler error-handler]
-   (-> p
-       (p/then (fn [result]
-                 (ok-handler result)))
-       (p/catch (fn [error]
-                  (error-handler error))))))
-
-(defn get-width
-  []
-  (gobj/get js/window "innerWidth"))
-
-(defn listen
-  "Register an event `handler` for events of `type` on `target`."
-  [event-handler target type handler & [opts]]
-  (.listen event-handler target (name type) handler (clj->js opts)))
-
-(defn indexed
-  [coll]
-  (map-indexed vector coll))
-
-(defn find-first
-  [pred coll]
-  (first (filter pred coll)))
-
-(defn get-local-date
-  []
-  (let [date (js/Date.)
-        year (.getFullYear date)
-        month (inc (.getMonth date))
-        day (.getDate date)
-        hour (.getHours date)
-        minute (.getMinutes date)]
-    {:year year
-     :month month
-     :day day
-     :hour hour
-     :minute minute}))
-
-(defn dissoc-in
-  "Dissociates an entry from a nested associative structure returning a new
-  nested structure. keys is a sequence of keys. Any empty maps that result
-  will not be present in the new structure."
-  [m [k & ks :as keys]]
-  (if ks
-    (if-let [nextmap (get m k)]
-      (let [newmap (dissoc-in nextmap ks)]
-        (if (seq newmap)
-          (assoc m k newmap)
-          (dissoc m k)))
-      m)
-    (dissoc m k)))
-
-(defn format
-  [fmt & args]
-  (apply goog.string/format fmt args))
-
-(defn raw-html
-  [content]
-  [:div {:dangerouslySetInnerHTML
-         {:__html content}}])
-
-(defn json->clj
-  [json-string]
-  (-> json-string
-      (js/JSON.parse)
-      (js->clj :keywordize-keys true)))
-
-(defn remove-nils
-  "remove pairs of key-value that has nil value from a (possibly nested) map. also transform map to nil if all of its value are nil"
-  [nm]
-  (walk/postwalk
-   (fn [el]
-     (if (map? el)
-       (not-empty (into {} (remove (comp nil? second)) el))
-       el))
-   nm))
-
-(defn index-by
-  [col k]
-  (->> (map (fn [entry] [(get entry k) entry])
-        col)
-       (into {})))
-
-;; ".lg:absolute.lg:inset-y-0.lg:right-0.lg:w-1/2"
-(defn hiccup->class
-  [class]
-  (some->> (string/split class #"\.")
-           (string/join " ")
-           (string/trim)))
-
-(defn fetch
-  ([url on-ok on-failed]
-   (fetch url #js {} on-ok on-failed))
-  ([url opts on-ok on-failed]
-   (-> (js/fetch url opts)
-       (.then #(if (.-ok %)
-                 (.json %)
-                 (on-failed %)))
-       (.then bean/->clj)
-       (.then #(on-ok %)))))
-
-(defn get-weekday
-  [date]
-  (.toLocaleString date "en-us" (clj->js {:weekday "long"})))
-
-(defn get-date
-  []
-  (let [date (js/Date.)]
-    {:year (.getFullYear date)
-     :month (inc (.getMonth date))
-     :day (.getDate date)
-     :weekday (get-weekday date)}))
-
-(defn journals-path
-  [year month]
-  (let [month (if (< month 10) (str "0" month) month)]
-    (str "journals/" year "_" month ".org")))
-
-(defn current-journal-path
-  []
-  (let [{:keys [year month]} (get-date)]
-    (journals-path year month)))
-
-(defn today
-  []
-  (.toLocaleDateString (js/Date.) "default"
-                       (clj->js {:month "long"
-                                 :year "numeric"
-                                 :day "numeric"
-                                 :weekday "long"})))
-
-(defn zero-pad
-  [n]
-  (if (< n 10)
-    (str "0" n)
-    (str n)))
-
-(defn year-month-day-padded
-  []
-  (let [{:keys [year month day]} (get-date)]
-    {:year year
-     :month (zero-pad month)
-     :day (zero-pad day)}))
-
-(defn get-month-last-day
-  []
-  (let [today (js/Date.)
-        date (js/Date. (.getFullYear today) (inc (.getMonth today)) 0)]
-    (.getDate date)))
-
-(defn parse-int
-  [x]
-  (if (string? x)
-    (js/parseInt x)
-    x))
-
-(defn debounce
-  "Returns a function that will call f only after threshold has passed without new calls
-  to the function. Calls prep-fn on the args in a sync way, which can be used for things like
-  calling .persist on the event object to be able to access the event attributes in f"
-  ([threshold f] (debounce threshold f (constantly nil)))
-  ([threshold f prep-fn]
-   (let [t (atom nil)]
-     (fn [& args]
-       (when @t (js/clearTimeout @t))
-       (apply prep-fn args)
-       (reset! t (js/setTimeout #(do
-                                   (reset! t nil)
-                                   (apply f args))
-                                threshold))))))

+ 0 - 0
web/yarn.lock → yarn.lock


部分文件因文件數量過多而無法顯示