Browse Source

Merge remote-tracking branch 'upstream/master' into whiteboards

Peng Xiao 3 years ago
parent
commit
c7fe4933b4
38 changed files with 496 additions and 659 deletions
  1. 4 0
      ios/App/App/Info.plist
  2. 1 1
      libs/package.json
  3. 15 4
      libs/src/LSPlugin.ts
  4. 12 2
      libs/src/LSPlugin.user.ts
  5. 26 8
      libs/src/modules/LSPlugin.Storage.ts
  6. 1 1
      package.json
  7. 58 383
      resources/css/common.css
  8. 0 0
      resources/js/lsplugin.core.js
  9. 1 1
      resources/package.json
  10. 25 34
      src/electron/electron/file_sync_rsapi.cljs
  11. 10 3
      src/electron/electron/handler.cljs
  12. 4 0
      src/electron/electron/plugin.cljs
  13. 1 1
      src/electron/electron/utils.cljs
  14. 65 40
      src/electron/electron/utils.js
  15. 11 25
      src/main/frontend/components/block.cljs
  16. 0 14
      src/main/frontend/components/block.css
  17. 8 14
      src/main/frontend/components/content.cljs
  18. 4 4
      src/main/frontend/components/encryption.cljs
  19. 2 2
      src/main/frontend/components/file_sync.cljs
  20. 6 0
      src/main/frontend/components/file_sync.css
  21. 2 2
      src/main/frontend/components/page.cljs
  22. 1 6
      src/main/frontend/components/page.css
  23. 2 2
      src/main/frontend/components/page_menu.cljs
  24. 3 3
      src/main/frontend/components/plugins.cljs
  25. 3 3
      src/main/frontend/components/reference.cljs
  26. 7 10
      src/main/frontend/components/svg.cljs
  27. 6 0
      src/main/frontend/config.cljs
  28. 7 0
      src/main/frontend/dicts.cljc
  29. 11 6
      src/main/frontend/extensions/excalidraw.cljs
  30. 10 10
      src/main/frontend/extensions/pdf/pdf.css
  31. 3 3
      src/main/frontend/extensions/zotero.cljs
  32. 9 6
      src/main/frontend/handler/editor.cljs
  33. 1 1
      src/main/frontend/mobile/record.cljs
  34. 15 7
      src/main/frontend/ui.cljs
  35. 101 51
      src/main/logseq/api.cljs
  36. 1 1
      tailwind.all.css
  37. 49 7
      tailwind.config.js
  38. 11 4
      yarn.lock

+ 4 - 0
ios/App/App/Info.plist

@@ -78,6 +78,10 @@
 			<string>ANY</string>
 			<string>ANY</string>
 		</dict>
 		</dict>
 	</dict>
 	</dict>
+	<key>UIBackgroundModes</key>
+	<array>
+		<string>audio</string>
+	</array>
 	<key>UILaunchStoryboardName</key>
 	<key>UILaunchStoryboardName</key>
 	<string>LaunchScreen</string>
 	<string>LaunchScreen</string>
 	<key>UIMainStoryboardFile</key>
 	<key>UIMainStoryboardFile</key>

+ 1 - 1
libs/package.json

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

+ 15 - 4
libs/src/LSPlugin.ts

@@ -3,7 +3,7 @@ import * as CSS from 'csstype'
 import EventEmitter from 'eventemitter3'
 import EventEmitter from 'eventemitter3'
 import { LSPluginCaller } from './LSPlugin.caller'
 import { LSPluginCaller } from './LSPlugin.caller'
 import { LSPluginExperiments } from './modules/LSPlugin.Experiments'
 import { LSPluginExperiments } from './modules/LSPlugin.Experiments'
-import { LSPluginFileStorage } from './modules/LSPlugin.Storage'
+import { IAsyncStorage, LSPluginFileStorage } from './modules/LSPlugin.Storage'
 import { LSPluginRequest } from './modules/LSPlugin.Request'
 import { LSPluginRequest } from './modules/LSPlugin.Request'
 
 
 export type WithOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
 export type WithOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
@@ -169,6 +169,7 @@ export interface BlockEntity {
   unordered: boolean
   unordered: boolean
   content: string
   content: string
   page: IEntityID
   page: IEntityID
+  properties?: Record<string, any>
 
 
   // optional fields in dummy page
   // optional fields in dummy page
   anchor?: string
   anchor?: string
@@ -196,9 +197,12 @@ export interface PageEntity {
   file?: IEntityID
   file?: IEntityID
   namespace?: IEntityID
   namespace?: IEntityID
   children?: Array<PageEntity>
   children?: Array<PageEntity>
+  properties?: Record<string, any>
   format?: 'markdown' | 'org'
   format?: 'markdown' | 'org'
   journalDay?: number
   journalDay?: number
   updatedAt?: number
   updatedAt?: number
+
+  [key: string]: any
 }
 }
 
 
 export type BlockIdentity = BlockUUID | Pick<BlockEntity, 'uuid'>
 export type BlockIdentity = BlockUUID | Pick<BlockEntity, 'uuid'>
@@ -412,9 +416,9 @@ export interface IAppProxy {
 
 
   // hook events
   // hook events
   onCurrentGraphChanged: IUserHook
   onCurrentGraphChanged: IUserHook
-  onGraphAfterIndexed: IUserHook<{repo: string}>
+  onGraphAfterIndexed: IUserHook<{ repo: string }>
   onThemeModeChanged: IUserHook<{ mode: 'dark' | 'light' }>
   onThemeModeChanged: IUserHook<{ mode: 'dark' | 'light' }>
-  onThemeChanged: IUserHook<Partial<{name: string, mode: string, pid: string, url: string}>>
+  onThemeChanged: IUserHook<Partial<{ name: string, mode: string, pid: string, url: string }>>
   onBlockRendererSlotted: IUserSlotHook<{ uuid: BlockUUID }>
   onBlockRendererSlotted: IUserSlotHook<{ uuid: BlockUUID }>
 
 
   /**
   /**
@@ -786,7 +790,7 @@ export interface IAssetsProxy {
    * @added 0.0.2
    * @added 0.0.2
    * @param exts
    * @param exts
    */
    */
-  listFilesOfCurrentGraph(exts: string | string[]): Promise<{
+  listFilesOfCurrentGraph(exts?: string | string[]): Promise<{
     path: string
     path: string
     size: number
     size: number
     accessTime: number
     accessTime: number
@@ -794,6 +798,12 @@ export interface IAssetsProxy {
     changeTime: number
     changeTime: number
     birthTime: number
     birthTime: number
   }>
   }>
+
+  /**
+   * @example https://github.com/logseq/logseq/pull/6488
+   * @added 0.0.10
+   */
+  makeSandboxStorage(): IAsyncStorage
 }
 }
 
 
 export interface ILSPluginThemeManager {
 export interface ILSPluginThemeManager {
@@ -963,6 +973,7 @@ export interface ILSPluginUser extends EventEmitter<LSPluginUserEvents> {
   DB: IDBProxy
   DB: IDBProxy
   Git: IGitProxy
   Git: IGitProxy
   UI: IUIProxy
   UI: IUIProxy
+  Assets: IAssetsProxy
 
 
   Request: LSPluginRequest
   Request: LSPluginRequest
   FileStorage: LSPluginFileStorage
   FileStorage: LSPluginFileStorage

+ 12 - 2
libs/src/LSPlugin.user.ts

@@ -38,7 +38,7 @@ import {
 import Debug from 'debug'
 import Debug from 'debug'
 import * as CSS from 'csstype'
 import * as CSS from 'csstype'
 import EventEmitter from 'eventemitter3'
 import EventEmitter from 'eventemitter3'
-import { LSPluginFileStorage } from './modules/LSPlugin.Storage'
+import { IAsyncStorage, LSPluginFileStorage } from './modules/LSPlugin.Storage'
 import { LSPluginExperiments } from './modules/LSPlugin.Experiments'
 import { LSPluginExperiments } from './modules/LSPlugin.Experiments'
 import { LSPluginRequest } from './modules/LSPlugin.Request'
 import { LSPluginRequest } from './modules/LSPlugin.Request'
 
 
@@ -311,8 +311,18 @@ const db: Partial<IDBProxy> = {
 }
 }
 
 
 const git: Partial<IGitProxy> = {}
 const git: Partial<IGitProxy> = {}
+
 const ui: Partial<IUIProxy> = {}
 const ui: Partial<IUIProxy> = {}
-const assets: Partial<IAssetsProxy> = {}
+
+const assets: Partial<IAssetsProxy> = {
+  makeSandboxStorage(
+    this: LSPluginUser
+  ): IAsyncStorage {
+    return new LSPluginFileStorage(
+      this, { assets: true }
+    )
+  }
+}
 
 
 type uiState = {
 type uiState = {
   key?: number
   key?: number

+ 26 - 8
libs/src/modules/LSPlugin.Storage.ts

@@ -9,6 +9,8 @@ export interface IAsyncStorage {
 
 
   hasItem(key: string): Promise<boolean>
   hasItem(key: string): Promise<boolean>
 
 
+  allKeys(): Promise<Array<string>>
+
   clear(): Promise<void>
   clear(): Promise<void>
 }
 }
 
 
@@ -18,8 +20,14 @@ export interface IAsyncStorage {
 class LSPluginFileStorage implements IAsyncStorage {
 class LSPluginFileStorage implements IAsyncStorage {
   /**
   /**
    * @param ctx
    * @param ctx
+   * @param opts
    */
    */
-  constructor(private ctx: LSPluginUser) {}
+  constructor(
+    private ctx: LSPluginUser,
+    private opts?: {
+      assets: boolean
+    }
+  ) {}
 
 
   /**
   /**
    * plugin id
    * plugin id
@@ -32,20 +40,20 @@ class LSPluginFileStorage implements IAsyncStorage {
    * @param key A string as file name that support nested directory
    * @param key A string as file name that support nested directory
    * @param value Storage value
    * @param value Storage value
    */
    */
-  setItem(key: string, value: string): Promise<void> {
+  setItem(key: string, value: string | any): Promise<void> {
     return this.ctx.caller.callAsync(`api:call`, {
     return this.ctx.caller.callAsync(`api:call`, {
       method: 'write-plugin-storage-file',
       method: 'write-plugin-storage-file',
-      args: [this.ctxId, key, value],
+      args: [this.ctxId, key, value, this.opts?.assets],
     })
     })
   }
   }
 
 
   /**
   /**
    * @param key
    * @param key
    */
    */
-  getItem(key: string): Promise<string | undefined> {
+  getItem(key: string): Promise<string | any> {
     return this.ctx.caller.callAsync(`api:call`, {
     return this.ctx.caller.callAsync(`api:call`, {
       method: 'read-plugin-storage-file',
       method: 'read-plugin-storage-file',
-      args: [this.ctxId, key],
+      args: [this.ctxId, key, this.opts?.assets],
     })
     })
   }
   }
 
 
@@ -55,7 +63,17 @@ class LSPluginFileStorage implements IAsyncStorage {
   removeItem(key: string): Promise<void> {
   removeItem(key: string): Promise<void> {
     return this.ctx.caller.call(`api:call`, {
     return this.ctx.caller.call(`api:call`, {
       method: 'unlink-plugin-storage-file',
       method: 'unlink-plugin-storage-file',
-      args: [this.ctxId, key],
+      args: [this.ctxId, key, this.opts?.assets],
+    })
+  }
+
+  /**
+   * Get all path file keys
+   */
+  allKeys(): Promise<Array<string>> {
+    return this.ctx.caller.callAsync(`api:call`, {
+      method: 'list-plugin-storage-files',
+      args: [this.ctxId, this.opts?.assets]
     })
     })
   }
   }
 
 
@@ -65,7 +83,7 @@ class LSPluginFileStorage implements IAsyncStorage {
   clear(): Promise<void> {
   clear(): Promise<void> {
     return this.ctx.caller.call(`api:call`, {
     return this.ctx.caller.call(`api:call`, {
       method: 'clear-plugin-storage-files',
       method: 'clear-plugin-storage-files',
-      args: [this.ctxId],
+      args: [this.ctxId, this.opts?.assets],
     })
     })
   }
   }
 
 
@@ -75,7 +93,7 @@ class LSPluginFileStorage implements IAsyncStorage {
   hasItem(key: string): Promise<boolean> {
   hasItem(key: string): Promise<boolean> {
     return this.ctx.caller.callAsync(`api:call`, {
     return this.ctx.caller.callAsync(`api:call`, {
       method: 'exist-plugin-storage-file',
       method: 'exist-plugin-storage-file',
-      args: [this.ctxId, key],
+      args: [this.ctxId, key, this.opts?.assets],
     })
     })
   }
   }
 }
 }

+ 1 - 1
package.json

@@ -83,7 +83,7 @@
         "@capacitor/share": "^4.0.0",
         "@capacitor/share": "^4.0.0",
         "@capacitor/splash-screen": "^4.0.0",
         "@capacitor/splash-screen": "^4.0.0",
         "@capacitor/status-bar": "^4.0.0",
         "@capacitor/status-bar": "^4.0.0",
-        "@excalidraw/excalidraw": "0.10.0",
+        "@excalidraw/excalidraw": "0.12.0",
         "@kanru/rage-wasm": "^0.3.0",
         "@kanru/rage-wasm": "^0.3.0",
         "@logseq/capacitor-file-sync": "0.0.11",
         "@logseq/capacitor-file-sync": "0.0.11",
         "@logseq/react-tweet-embed": "1.3.1-1",
         "@logseq/react-tweet-embed": "1.3.1-1",

+ 58 - 383
resources/css/common.css

@@ -14,9 +14,9 @@
   --ls-left-sidebar-width: 246px;
   --ls-left-sidebar-width: 246px;
   --ls-left-sidebar-sm-width: 70%;
   --ls-left-sidebar-sm-width: 70%;
   --ls-left-sidebar-nav-btn-size: 38px;
   --ls-left-sidebar-nav-btn-size: 38px;
-  --ls-color-file-sync-error: #ff0000;
-  --ls-color-file-sync-pending: #ffbb4d;
-  --ls-color-file-sync-idle: #04b404;
+  --ls-error-color: var(--color-red-500);
+  --ls-warning-color: var(--color-orange-500);
+  --ls-success-color: var(--color-green-500);
 }
 }
 
 
 @media (prefers-color-scheme: dark) {
 @media (prefers-color-scheme: dark) {
@@ -91,6 +91,17 @@ html[data-theme='dark'] {
   --ls-right-sidebar-code-bg-color: #04303c;
   --ls-right-sidebar-code-bg-color: #04303c;
   --ls-pie-bg-color: #01303b;
   --ls-pie-bg-color: #01303b;
   --ls-pie-fg-color: #0b5869;
   --ls-pie-fg-color: #0b5869;
+  --ls-highlight-color-gray: var(--color-gray-900);
+  --ls-highlight-color-red: var(--color-red-900);
+  --ls-highlight-color-yellow: var(--color-yellow-900);
+  --ls-highlight-color-green: var(--color-green-900);
+  --ls-highlight-color-blue: var(--color-blue-900);
+  --ls-highlight-color-purple: var(--color-purple-900);
+  --ls-highlight-color-pink: var(--color-pink-900);
+  --ls-error-text-color: var(--color-red-100);
+  --ls-error-background-color: var(--color-red-900);
+  --ls-warning-text-color: var(--color-yellow-100);
+  --ls-warning-background-color: var(--color-yellow-900);
   --ls-focus-ring-color: rgba(18, 98, 119, 0.5);
   --ls-focus-ring-color: rgba(18, 98, 119, 0.5);
   --color-level-1: var(--ls-secondary-background-color);
   --color-level-1: var(--ls-secondary-background-color);
   --color-level-2: var(--ls-tertiary-background-color);
   --color-level-2: var(--ls-tertiary-background-color);
@@ -154,6 +165,17 @@ html[data-theme='light'] {
   --ls-right-sidebar-code-bg-color: var(--ls-secondary-background-color);
   --ls-right-sidebar-code-bg-color: var(--ls-secondary-background-color);
   --ls-pie-bg-color: #e1e1e1;
   --ls-pie-bg-color: #e1e1e1;
   --ls-pie-fg-color: #0a4a5d;
   --ls-pie-fg-color: #0a4a5d;
+  --ls-highlight-color-gray: var(--color-gray-100);
+  --ls-highlight-color-red: var(--color-red-100);
+  --ls-highlight-color-yellow: var(--color-yellow-100);
+  --ls-highlight-color-green: var(--color-green-100);
+  --ls-highlight-color-blue: var(--color-blue-100);
+  --ls-highlight-color-purple: var(--color-purple-100);
+  --ls-highlight-color-pink: var(--color-pink-100);
+  --ls-error-text-color: var(--color-red-800);
+  --ls-error-background-color: var(--color-red-100);
+  --ls-warning-text-color: var(--color-yellow-800);
+  --ls-warning-background-color: var(--color-yellow-100);
   --ls-focus-ring-color: rgba(66, 133, 244, 0.5);
   --ls-focus-ring-color: rgba(66, 133, 244, 0.5);
   --color-level-1: var(--ls-secondary-background-color);
   --color-level-1: var(--ls-secondary-background-color);
   --color-level-2: var(--ls-tertiary-background-color);
   --color-level-2: var(--ls-tertiary-background-color);
@@ -311,373 +333,6 @@ video {
 
 
 /* endregion */
 /* endregion */
 
 
-/** region Common utilities **/
-.w10 {
-  max-width: 10%;
-}
-
-.w20 {
-  max-width: 20%;
-}
-
-.w30 {
-  max-width: 30%;
-}
-
-.w40 {
-  max-width: 40%;
-}
-
-.w50 {
-  max-width: 50%;
-}
-
-.w60 {
-  max-width: 60%;
-}
-
-.w70 {
-  max-width: 70%;
-}
-
-.w80 {
-  max-width: 80%;
-}
-
-.w90 {
-  max-width: 90%;
-}
-
-.w100 {
-  max-width: 100%;
-}
-
-.bg-black {
-  background-color: rgba(0, 0, 0);
-}
-
-.bg-white {
-  background-color: rgba(255, 255, 255);
-}
-
-.bg-gray-50 {
-  background-color: rgba(249, 250, 251);
-}
-
-.bg-gray-100 {
-  background-color: rgba(243, 244, 246);
-}
-
-.bg-gray-200 {
-  background-color: rgba(229, 231, 235);
-}
-
-.bg-gray-300 {
-  background-color: rgba(209, 213, 219);
-}
-
-.bg-gray-400 {
-  background-color: rgba(156, 163, 175);
-}
-
-.bg-gray-500 {
-  background-color: rgba(107, 114, 128);
-}
-
-.bg-gray-600 {
-  background-color: rgba(75, 85, 99);
-}
-
-.bg-gray-700 {
-  background-color: rgba(55, 65, 81);
-}
-
-.bg-gray-800 {
-  background-color: rgba(31, 41, 55);
-}
-
-.bg-gray-900 {
-  background-color: rgba(17, 24, 39);
-}
-
-.bg-red-50 {
-  background-color: rgba(254, 242, 242);
-}
-
-.bg-red-100 {
-  background-color: rgba(254, 226, 226);
-}
-
-.bg-red-200 {
-  background-color: rgba(254, 202, 202);
-}
-
-.bg-red-300 {
-  background-color: rgba(252, 165, 165);
-}
-
-.bg-red-400 {
-  background-color: rgba(248, 113, 113);
-}
-
-.bg-red-500 {
-  background-color: rgba(239, 68, 68);
-}
-
-.bg-red-600 {
-  background-color: rgba(220, 38, 38);
-}
-
-.bg-red-700 {
-  background-color: rgba(185, 28, 28);
-}
-
-.bg-red-800 {
-  background-color: rgba(153, 27, 27);
-}
-
-.bg-red-900 {
-  background-color: rgba(127, 29, 29);
-}
-
-.bg-yellow-50 {
-  background-color: rgba(255, 251, 235);
-}
-
-.bg-yellow-100 {
-  background-color: rgba(254, 243, 199);
-}
-
-.bg-yellow-200 {
-  background-color: rgba(253, 230, 138);
-}
-
-.bg-yellow-300 {
-  background-color: rgba(252, 211, 77);
-}
-
-.bg-yellow-400 {
-  background-color: rgba(251, 191, 36);
-}
-
-.bg-yellow-500 {
-  background-color: rgba(245, 158, 11);
-}
-
-.bg-yellow-600 {
-  background-color: rgba(217, 119, 6);
-}
-
-.bg-yellow-700 {
-  background-color: rgba(180, 83, 9);
-}
-
-.bg-yellow-800 {
-  background-color: rgba(146, 64, 14);
-}
-
-.bg-yellow-900 {
-  background-color: rgba(120, 53, 15);
-}
-
-.bg-green-50 {
-  background-color: rgba(236, 253, 245);
-}
-
-.bg-green-100 {
-  background-color: rgba(209, 250, 229);
-}
-
-.bg-green-200 {
-  background-color: rgba(167, 243, 208);
-}
-
-.bg-green-300 {
-  background-color: rgba(110, 231, 183);
-}
-
-.bg-green-400 {
-  background-color: rgba(52, 211, 153);
-}
-
-.bg-green-500 {
-  background-color: rgba(16, 185, 129);
-}
-
-.bg-green-600 {
-  background-color: rgba(5, 150, 105);
-}
-
-.bg-green-700 {
-  background-color: rgba(4, 120, 87);
-}
-
-.bg-green-800 {
-  background-color: rgba(6, 95, 70);
-}
-
-.bg-green-900 {
-  background-color: rgba(6, 78, 59);
-}
-
-.bg-blue-50 {
-  background-color: rgba(239, 246, 255);
-}
-
-.bg-blue-100 {
-  background-color: rgba(219, 234, 254);
-}
-
-.bg-blue-200 {
-  background-color: rgba(191, 219, 254);
-}
-
-.bg-blue-300 {
-  background-color: rgba(147, 197, 253);
-}
-
-.bg-blue-400 {
-  background-color: rgba(96, 165, 250);
-}
-
-.bg-blue-500 {
-  background-color: rgba(59, 130, 246);
-}
-
-.bg-blue-600 {
-  background-color: rgba(37, 99, 235);
-}
-
-.bg-blue-700 {
-  background-color: rgba(29, 78, 216);
-}
-
-.bg-blue-800 {
-  background-color: rgba(30, 64, 175);
-}
-
-.bg-blue-900 {
-  background-color: rgba(30, 58, 138);
-}
-
-.bg-indigo-50 {
-  background-color: rgba(238, 242, 255);
-}
-
-.bg-indigo-100 {
-  background-color: rgba(224, 231, 255);
-}
-
-.bg-indigo-200 {
-  background-color: rgba(199, 210, 254);
-}
-
-.bg-indigo-300 {
-  background-color: rgba(165, 180, 252);
-}
-
-.bg-indigo-400 {
-  background-color: rgba(129, 140, 248);
-}
-
-.bg-indigo-500 {
-  background-color: rgba(99, 102, 241);
-}
-
-.bg-indigo-600 {
-  background-color: rgba(79, 70, 229);
-}
-
-.bg-indigo-700 {
-  background-color: rgba(67, 56, 202);
-}
-
-.bg-indigo-800 {
-  background-color: rgba(55, 48, 163);
-}
-
-.bg-indigo-900 {
-  background-color: rgba(49, 46, 129);
-}
-
-.bg-purple-50 {
-  background-color: rgba(245, 243, 255);
-}
-
-.bg-purple-100 {
-  background-color: rgba(237, 233, 254);
-}
-
-.bg-purple-200 {
-  background-color: rgba(221, 214, 254);
-}
-
-.bg-purple-300 {
-  background-color: rgba(196, 181, 253);
-}
-
-.bg-purple-400 {
-  background-color: rgba(167, 139, 250);
-}
-
-.bg-purple-500 {
-  background-color: rgba(139, 92, 246);
-}
-
-.bg-purple-600 {
-  background-color: rgba(124, 58, 237);
-}
-
-.bg-purple-700 {
-  background-color: rgba(109, 40, 217);
-}
-
-.bg-purple-800 {
-  background-color: rgba(91, 33, 182);
-}
-
-.bg-purple-900  {
-  background-color: rgba(76, 29, 149);
-}
-
-.bg-pink-100 {
-  background-color: #fff5f7;
-}
-
-.bg-pink-200 {
-  background-color: #fed7e2;
-}
-
-.bg-pink-300 {
-  background-color: #fbb6ce;
-}
-
-.bg-pink-400 {
-  background-color: #f687b3;
-}
-
-.bg-pink-500 {
-  background-color: #ed64a6;
-}
-
-.bg-pink-600 {
-  background-color: #d53f8c;
-}
-
-.bg-pink-700 {
-  background-color: #b83280;
-}
-
-.bg-pink-800 {
-  background-color: #97266d;
-}
-
-.bg-pink-900 {
-  background-color: #702459;
-}
-
-/** endregion **/
-
 /** region App utilities **/
 /** region App utilities **/
 .ls-center {
 .ls-center {
   position: absolute;
   position: absolute;
@@ -782,11 +437,6 @@ li p:last-child,
   opacity: 0.7;
   opacity: 0.7;
 }
 }
 
 
-.svg-shadow {
-  -webkit-filter: drop-shadow(1px 1px 2px rgba(0, 0, 0, 0.7));
-  filter: drop-shadow(1px 1px 2px rgba(0, 0, 0, 0.5));
-}
-
 .tip-shadow {
 .tip-shadow {
   -webkit-filter: drop-shadow(1px 1px 2px rgba(155, 155, 0, 0.8));
   -webkit-filter: drop-shadow(1px 1px 2px rgba(155, 155, 0, 0.8));
   filter: drop-shadow(1px 1px 2px rgba(155, 155, 0, 0.8));
   filter: drop-shadow(1px 1px 2px rgba(155, 155, 0, 0.8));
@@ -847,8 +497,8 @@ i.ti {
 
 
 .heading-bg {
 .heading-bg {
   border-radius: 50%;
   border-radius: 50%;
-  width: 14px;
-  height: 14px;
+  width: 18px;
+  height: 18px;
 
 
   &.remove {
   &.remove {
     @apply border flex items-center justify-center;
     @apply border flex items-center justify-center;
@@ -857,6 +507,10 @@ i.ti {
   }
   }
 }
 }
 
 
+.to-heading-button {
+  @apply px-1 !important;
+}
+
 /** endregion **/
 /** endregion **/
 
 
 /* region FIXME: override elements (?) */
 /* region FIXME: override elements (?) */
@@ -964,24 +618,45 @@ a.chosen {
 
 
 a.warning,
 a.warning,
 span.warning, div.warning:not(.admonitionblock), p.warning {
 span.warning, div.warning:not(.admonitionblock), p.warning {
-  background: #f56565;
+  background: var(--ls-warning-background-color);
   padding: 0.1em 0.4em;
   padding: 0.1em 0.4em;
   border-radius: var(--ls-border-radius-low);
   border-radius: var(--ls-border-radius-low);
-  color: #fff;
+  color: var(--ls-warning-text-color);
+}
+
+.text-warning {
+  color: var(--ls-warning-text-color);
 }
 }
 
 
-.warning-text {
-  color: #f56565;
+.bg-warning {
+  background: var(--ls-waring-background-color);
 }
 }
 
 
 a.error,
 a.error,
 span.error {
 span.error {
-    background: red;
+    background: var(--ls-error-background-color);
     padding: 0.1em 0.4em;
     padding: 0.1em 0.4em;
     border-radius: var(--ls-border-radius-low);
     border-radius: var(--ls-border-radius-low);
-    color: #fff;
+    color: var(--ls-error-text-color);
+}
+
+.text-error {
+  color: var(--ls-error-text-color);
+}
+
+.bg-error {
+  background: var(--ls-error-background-color);
+}
+
+.text-success {
+  color: var(--ls-success-text-color);
 }
 }
 
 
+.bg-success {
+  background: var(--ls-success-background-color);
+}
+
+
 img.small {
 img.small {
   display: inline;
   display: inline;
   width: 20px;
   width: 20px;

File diff suppressed because it is too large
+ 0 - 0
resources/js/lsplugin.core.js


+ 1 - 1
resources/package.json

@@ -37,7 +37,7 @@
     "https-proxy-agent": "5.0.0",
     "https-proxy-agent": "5.0.0",
     "@sentry/electron": "2.5.1",
     "@sentry/electron": "2.5.1",
     "posthog-js": "1.10.2",
     "posthog-js": "1.10.2",
-    "@logseq/rsapi": "0.0.48",
+    "@logseq/rsapi": "0.0.50",
     "electron-deeplink": "1.0.10",
     "electron-deeplink": "1.0.10",
     "abort-controller": "3.0.0"
     "abort-controller": "3.0.0"
   },
   },

+ 25 - 34
src/electron/electron/file_sync_rsapi.cljs

@@ -1,69 +1,60 @@
 (ns electron.file-sync-rsapi
 (ns electron.file-sync-rsapi
-  (:require ["os" :as os]
+  (:require ["@logseq/rsapi" :as rsapi]
             [electron.window :as window]
             [electron.window :as window]
-            [cljs-bean.core :as bean]
-            [clojure.string :as string]))
+            [cljs-bean.core :as bean]))
 
 
-(if (and (= (.platform os) "win32")
-         (string/starts-with? (.release os) "6."))
-  (defonce rsapi nil)
-  (defonce rsapi (js/require "@logseq/rsapi")))
-
-(defn key-gen []
-  (.keygen rsapi))
+(defn key-gen [] (rsapi/keygen))
 
 
 (defn set-env [graph-uuid env private-key public-key]
 (defn set-env [graph-uuid env private-key public-key]
-  (.setEnv rsapi graph-uuid env private-key public-key))
+  (rsapi/setEnv graph-uuid env private-key public-key))
 
 
 (defn set-progress-callback [callback]
 (defn set-progress-callback [callback]
-  (.setProgressCallback rsapi callback))
+  (rsapi/setProgressCallback callback))
 
 
 (defn get-local-files-meta [graph-uuid base-path file-paths]
 (defn get-local-files-meta [graph-uuid base-path file-paths]
-  (.getLocalFilesMeta rsapi graph-uuid base-path (clj->js file-paths)))
+  (rsapi/getLocalFilesMeta graph-uuid base-path (clj->js file-paths)))
 
 
 (defn get-local-all-files-meta [graph-uuid base-path]
 (defn get-local-all-files-meta [graph-uuid base-path]
-  (.getLocalAllFilesMeta rsapi graph-uuid base-path))
+  (rsapi/getLocalAllFilesMeta graph-uuid base-path))
 
 
 (defn rename-local-file [graph-uuid base-path from to]
 (defn rename-local-file [graph-uuid base-path from to]
-  (.renameLocalFile rsapi graph-uuid base-path from to))
+  (rsapi/renameLocalFile graph-uuid base-path from to))
 
 
 (defn delete-local-files [graph-uuid base-path file-paths]
 (defn delete-local-files [graph-uuid base-path file-paths]
-  (.deleteLocalFiles rsapi graph-uuid base-path (clj->js file-paths)))
+  (rsapi/deleteLocalFiles graph-uuid base-path (clj->js file-paths)))
 
 
 (defn update-local-files [graph-uuid base-path file-paths token]
 (defn update-local-files [graph-uuid base-path file-paths token]
-  (.updateLocalFiles rsapi graph-uuid base-path (clj->js file-paths) token))
+  (rsapi/updateLocalFiles graph-uuid base-path (clj->js file-paths) token))
 
 
 (defn download-version-files [graph-uuid base-path file-paths token]
 (defn download-version-files [graph-uuid base-path file-paths token]
-  (.updateLocalVersionFiles rsapi graph-uuid base-path (clj->js file-paths) token))
+  (rsapi/updateLocalVersionFiles graph-uuid base-path (clj->js file-paths) token))
 
 
 (defn delete-remote-files [graph-uuid base-path file-paths txid token]
 (defn delete-remote-files [graph-uuid base-path file-paths txid token]
-  (.deleteRemoteFiles rsapi graph-uuid base-path (clj->js file-paths) txid token))
+  (rsapi/deleteRemoteFiles graph-uuid base-path (clj->js file-paths) txid token))
 
 
 (defn update-remote-files [graph-uuid base-path file-paths txid token]
 (defn update-remote-files [graph-uuid base-path file-paths txid token]
-  (.updateRemoteFiles rsapi graph-uuid base-path (clj->js file-paths) txid token true))
+  (rsapi/updateRemoteFiles graph-uuid base-path (clj->js file-paths) txid token true))
 
 
 (defn encrypt-fnames [graph-uuid fnames]
 (defn encrypt-fnames [graph-uuid fnames]
-  (.encryptFnames rsapi graph-uuid (clj->js fnames)))
+  (rsapi/encryptFnames graph-uuid (clj->js fnames)))
 
 
 (defn decrypt-fnames [graph-uuid fnames]
 (defn decrypt-fnames [graph-uuid fnames]
-  (.decryptFnames rsapi graph-uuid (clj->js fnames)))
+  (rsapi/decryptFnames graph-uuid (clj->js fnames)))
 
 
 (defn encrypt-with-passphrase [passphrase data]
 (defn encrypt-with-passphrase [passphrase data]
-  (.ageEncryptWithPassphrase rsapi passphrase data))
+  (rsapi/ageEncryptWithPassphrase passphrase data))
 
 
 (defn decrypt-with-passphrase [passphrase data]
 (defn decrypt-with-passphrase [passphrase data]
-  (.ageDecryptWithPassphrase rsapi passphrase data))
+  (rsapi/ageDecryptWithPassphrase passphrase data))
 
 
 (defn cancel-all-requests []
 (defn cancel-all-requests []
-  (.cancelAllRequests rsapi))
+  (rsapi/cancelAllRequests))
 
 
 (defonce progress-notify-chan "file-sync-progress")
 (defonce progress-notify-chan "file-sync-progress")
-
-(when rsapi
-  (set-progress-callback (fn [error progress-info]
-                           (when-not error
-                             (doseq [^js win (window/get-all-windows)]
-                               (when-not (.isDestroyed win)
-                                 (.. win -webContents
-                                     (send progress-notify-chan (bean/->js progress-info)))))))))
-
+(set-progress-callback (fn [error progress-info]
+                         (when-not error
+                           (doseq [^js win (window/get-all-windows)]
+                             (when-not (.isDestroyed win)
+                               (.. win -webContents
+                                   (send progress-notify-chan (bean/->js progress-info))))))))
+                                   

+ 10 - 3
src/electron/electron/handler.cljs

@@ -55,8 +55,13 @@
 (defmethod handle :readdir [_window [_ dir]]
 (defmethod handle :readdir [_window [_ dir]]
   (readdir dir))
   (readdir dir))
 
 
+(defmethod handle :listdir [_window [_ dir flat?]]
+  (when (and dir (fs-extra/pathExistsSync dir))
+    (js-utils/deepReadDir dir (if (boolean? flat?) flat? true))))
+
 (defmethod handle :unlink [_window [_ repo-dir path]]
 (defmethod handle :unlink [_window [_ repo-dir path]]
-  (if (plugin/dotdir-file? path)
+  (if (or (plugin/dotdir-file? path)
+          (plugin/assetsdir-file? path))
     (fs/unlinkSync path)
     (fs/unlinkSync path)
     (try
     (try
       (logger/info ::unlink {:path path})
       (logger/info ::unlink {:path path})
@@ -374,8 +379,10 @@
 
 
 (defmethod handle :getAssetsFiles [^js win [_ {:keys [exts]}]]
 (defmethod handle :getAssetsFiles [^js win [_ {:keys [exts]}]]
   (when-let [graph-path (state/get-window-graph-path win)]
   (when-let [graph-path (state/get-window-graph-path win)]
-    (p/let [^js files (js-utils/getAllFiles (.join path graph-path "assets") (clj->js exts))]
-      files)))
+    (when-let [assets-path (.join path graph-path "assets")]
+      (when (fs-extra/pathExistsSync assets-path)
+        (p/let [^js files (js-utils/getAllFiles assets-path (clj->js exts))]
+          files)))))
 
 
 (defn close-watcher-when-orphaned!
 (defn close-watcher-when-orphaned!
   "When it's the last window for the directory, close the watcher."
   "When it's the last window for the directory, close the watcher."

+ 4 - 0
src/electron/electron/plugin.cljs

@@ -23,6 +23,10 @@
   [file]
   [file]
   (and file (string/starts-with? (path/normalize file) cfgs/dot-root)))
   (and file (string/starts-with? (path/normalize file) cfgs/dot-root)))
 
 
+(defn assetsdir-file?
+  [file]
+  (and (string? file) (string/includes? file "assets/storages")))
+
 ;; Get a release by tag name: /repos/{owner}/{repo}/releases/tags/{tag}
 ;; Get a release by tag name: /repos/{owner}/{repo}/releases/tags/{tag}
 ;; Get the latest release: /repos/{owner}/{repo}/releases/latest
 ;; Get the latest release: /repos/{owner}/{repo}/releases/latest
 ;; Zipball https://api.github.com/repos/{owner}/{repo}/zipball
 ;; Zipball https://api.github.com/repos/{owner}/{repo}/zipball

+ 1 - 1
src/electron/electron/utils.cljs

@@ -35,7 +35,7 @@
 
 
 (defn get-ls-dotdir-root
 (defn get-ls-dotdir-root
   []
   []
-  (let [lg-dir (str (.getPath app "home") "/.logseq")]
+  (let [lg-dir (path/join (.getPath app "home") ".logseq")]
     (if-not (fs/existsSync lg-dir)
     (if-not (fs/existsSync lg-dir)
       (do (fs/mkdirSync lg-dir) lg-dir)
       (do (fs/mkdirSync lg-dir) lg-dir)
       lg-dir)))
       lg-dir)))

+ 65 - 40
src/electron/electron/utils.js

@@ -1,5 +1,5 @@
-import path from "path";
-import fs from 'fs';
+import path from 'path'
+import fse from 'fs-extra'
 
 
 // workaround from https://github.com/electron/electron/issues/426#issuecomment-658901422
 // workaround from https://github.com/electron/electron/issues/426#issuecomment-658901422
 // We set an intercept on incoming requests to disable x-frame-options
 // We set an intercept on incoming requests to disable x-frame-options
@@ -7,36 +7,35 @@ import fs from 'fs';
 
 
 // Should we do this? Does this make evil sites doing danagerous things?
 // Should we do this? Does this make evil sites doing danagerous things?
 export const disableXFrameOptions = (win) => {
 export const disableXFrameOptions = (win) => {
-  win.webContents.session.webRequest.onHeadersReceived(
-    (d, c) => {
-      if (d.responseHeaders['X-Frame-Options']) {
-        delete d.responseHeaders['X-Frame-Options']
-      } else if (d.responseHeaders['x-frame-options']) {
-        delete d.responseHeaders['x-frame-options']
-      }
-      
-      if (d.responseHeaders['Content-Security-Policy']) {
-        delete d.responseHeaders['Content-Security-Policy']
-      }
+  win.webContents.session.webRequest.onHeadersReceived((d, c) => {
+    if (d.responseHeaders['X-Frame-Options']) {
+      delete d.responseHeaders['X-Frame-Options']
+    }
 
 
-      if (d.responseHeaders['content-security-policy']) {
-        delete d.responseHeaders['content-security-policy']
-      }
+    if (d.responseHeaders['x-frame-options']) {
+      delete d.responseHeaders['x-frame-options']
+    }
 
 
+    if (d.responseHeaders['Content-Security-Policy']) {
+      delete d.responseHeaders['Content-Security-Policy']
+    }
 
 
-      c({ cancel: false, responseHeaders: d.responseHeaders })
+    if (d.responseHeaders['content-security-policy']) {
+      delete d.responseHeaders['content-security-policy']
     }
     }
-  )
+
+    c({ cancel: false, responseHeaders: d.responseHeaders })
+  })
 }
 }
 
 
 export async function getAllFiles(dir, exts) {
 export async function getAllFiles(dir, exts) {
-  const dirents = await readdir(dir, { withFileTypes: true })
+  const dirents = await fse.readdir(dir, { withFileTypes: true })
 
 
-  if (exts) {
+  if (exts != null) {
     !Array.isArray(exts) && (exts = [exts])
     !Array.isArray(exts) && (exts = [exts])
 
 
-    exts = exts.map(it => {
-      if (it && !it.startsWith('.')) {
+    exts = exts.map((it) => {
+      if (typeof it === 'string' && it !== '' && !it.startsWith('.')) {
         it = '.' + it
         it = '.' + it
       }
       }
 
 
@@ -44,23 +43,49 @@ export async function getAllFiles(dir, exts) {
     })
     })
   }
   }
 
 
-  const files = await Promise.all(dirents.map(async (dirent) => {
-    if (exts && !exts.includes(path.extname(dirent.name))) {
-      return null
-    }
+  const files = await Promise.all(
+    dirents.map(async (dirent) => {
+      const filePath = path.resolve(dir, dirent.name)
 
 
-    const filePath = path.resolve(dir, dirent.name)
-    const fileStats = await lstat(filePath)
-    const stats = {
-      size: fileStats.size,
-      accessTime: fileStats.atimeMs,
-      modifiedTime: fileStats.mtimeMs,
-      changeTime: fileStats.ctimeMs,
-      birthTime: fileStats.birthtimeMs
-    }
-    return dirent.isDirectory() ? getAllFiles(filePath) : {
-      path: filePath, ...stats
-    }
-  }))
-  return files.flat().filter(it => it != null)
+      if (dirent.isDirectory()) {
+        return getAllFiles(filePath, exts)
+      }
+
+      if (exts && !exts.includes(path.extname(dirent.name)?.toLowerCase())) {
+        return null
+      }
+
+      const fileStats = await fse.lstat(filePath)
+
+      const stats = {
+        size: fileStats.size,
+        accessTime: fileStats.atimeMs,
+        modifiedTime: fileStats.mtimeMs,
+        changeTime: fileStats.ctimeMs,
+        birthTime: fileStats.birthtimeMs,
+      }
+
+      return { path: filePath, ...stats }
+    })
+  )
+  return files.flat().filter((it) => it != null)
+}
+
+export async function deepReadDir(dirPath, flat = true) {
+  const ret = await Promise.all(
+    (
+      await fse.readdir(dirPath)
+    ).map(async (entity) => {
+      const root = path.join(dirPath, entity)
+      return (await fse.lstat(root)).isDirectory()
+        ? await deepReadDir(root)
+        : root
+    })
+  )
+
+  if (flat) {
+    return ret?.flat()
+  }
+
+  return ret
 }
 }

+ 11 - 25
src/main/frontend/components/block.cljs

@@ -240,7 +240,7 @@
         (if (or (not asset-file?)
         (if (or (not asset-file?)
                 (and exist? (not loading?)))
                 (and exist? (not loading?)))
           (content-fn)
           (content-fn)
-          [:p.text-red-500.text-xs [:small.opacity-80
+          [:p.text-error.text-xs [:small.opacity-80
                                     (util/format "%s not found!" (string/capitalize type))]])))))
                                     (util/format "%s not found!" (string/capitalize type))]])))))
 
 
 (defn open-lightbox
 (defn open-lightbox
@@ -1916,8 +1916,10 @@
                  (not= "nil" marker))
                  (not= "nil" marker))
         {:class (str (string/lower-case marker))})
         {:class (str (string/lower-case marker))})
       (when bg-color
       (when bg-color
-        {:style {:background-color bg-color}
-         :class "with-bg-color"}))
+        {:style {:background-color (if (some #{bg-color} ui/block-background-colors) 
+                                     (str "var(--ls-highlight-color-" bg-color ")")
+                                     bg-color)}
+         :class "px-1 with-bg-color"}))
      (remove-nils
      (remove-nils
       (concat
       (concat
        [(when-not slide? checkbox)
        [(when-not slide? checkbox)
@@ -3174,22 +3176,6 @@
     {:debug-id q
     {:debug-id q
      :trigger-once? false})))
      :trigger-once? false})))
 
 
-(defn admonition
-  [config type result]
-  (when-let [icon (case (string/lower-case (name type))
-                    "note" svg/note
-                    "tip" svg/tip
-                    "important" svg/important
-                    "caution" svg/caution
-                    "warning" svg/warning
-                    "pinned" svg/pinned
-                    nil)]
-    [:div.flex.flex-row.admonitionblock.align-items {:class type}
-     [:div.pr-4.admonition-icon.flex.flex-col.justify-center
-      {:title (string/upper-case type)} (icon)]
-     [:div.ml-4.text-lg
-      (markup-elements-cp config result)]]))
-
 ;; TODO: move to mldoc
 ;; TODO: move to mldoc
 ;; (defn- convert-md-src-to-custom-block
 ;; (defn- convert-md-src-to-custom-block
 ;;   [item]
 ;;   [item]
@@ -3333,22 +3319,22 @@
           (ui/block-error "Invalid query:" {:content content})))
           (ui/block-error "Invalid query:" {:content content})))
 
 
       ["Custom" "note" _options result _content]
       ["Custom" "note" _options result _content]
-      (admonition config "note" result)
+      (ui/admonition "note" (markup-elements-cp config result))
 
 
       ["Custom" "tip" _options result _content]
       ["Custom" "tip" _options result _content]
-      (admonition config "tip" result)
+      (ui/admonition "tip" (markup-elements-cp config result))
 
 
       ["Custom" "important" _options result _content]
       ["Custom" "important" _options result _content]
-      (admonition config "important" result)
+      (ui/admonition "important" (markup-elements-cp config result))
 
 
       ["Custom" "caution" _options result _content]
       ["Custom" "caution" _options result _content]
-      (admonition config "caution" result)
+      (ui/admonition "caution" (markup-elements-cp config result))
 
 
       ["Custom" "warning" _options result _content]
       ["Custom" "warning" _options result _content]
-      (admonition config "warning" result)
+      (ui/admonition "warning" (markup-elements-cp config result))
 
 
       ["Custom" "pinned" _options result _content]
       ["Custom" "pinned" _options result _content]
-      (admonition config "pinned" result)
+      (ui/admonition "pinned" (markup-elements-cp config result))
 
 
       ["Custom" "center" _options l _content]
       ["Custom" "center" _options l _content]
       (->elem
       (->elem

+ 0 - 14
src/main/frontend/components/block.css

@@ -305,20 +305,6 @@
   }
   }
 }
 }
 
 
-.with-bg-color {
-  @apply px-1;
-
-  color: #fff;
-
-  a, .page-reference:not(:hover) {
-    color: #aacece;
-
-    .bracket {
-      color: #aacece;
-    }
-  }
-}
-
 .block-properties {
 .block-properties {
   margin: 4px 0;
   margin: 4px 0;
   padding: 4px 8px;
   padding: 4px 8px;

+ 8 - 14
src/main/frontend/components/content.cljs

@@ -93,15 +93,6 @@
     "Cycle todos"
     "Cycle todos"
     nil)])
     nil)])
 
 
-;; FIXME: Make it configurable
-(def block-background-colors
-  ["#533e7d"
-   "#497d46"
-   "#787f97"
-   "#978626"
-   "#49767b"
-   "#264c9b"])
-
 (defonce *template-including-parent? (atom nil))
 (defonce *template-including-parent? (atom nil))
 
 
 (rum/defc template-checkbox
 (rum/defc template-checkbox
@@ -164,13 +155,14 @@
     (when-let [block (db/entity [:block/uuid block-id])]
     (when-let [block (db/entity [:block/uuid block-id])]
       (let [format (:block/format block)]
       (let [format (:block/format block)]
         [:.menu-links-wrapper
         [:.menu-links-wrapper
-         [:div.flex.flex-row.justify-between.pb-2.pt-1.px-2.items-center
+         [:div.flex.flex-row.justify-between.py-1.px-2.items-center
           [:div.flex.flex-row.justify-between.flex-1
           [:div.flex.flex-row.justify-between.flex-1
-           (for [color block-background-colors]
+           (for [color ui/block-background-colors]
              [:a.m-2.shadow-sm
              [:a.m-2.shadow-sm
-              {:on-click (fn [_e]
+              {:title (t (keyword "color" color))
+               :on-click (fn [_e]
                            (editor-handler/set-block-property! block-id "background-color" color))}
                            (editor-handler/set-block-property! block-id "background-color" color))}
-              [:div.heading-bg {:style {:background-color color}}]])
+              [:div.heading-bg {:style {:background-color (str "var(--color-" color "-500)")}}]])
            [:a.m-2.shadow-sm
            [:a.m-2.shadow-sm
             {:title    (t :remove-background)
             {:title    (t :remove-background)
              :on-click (fn [_e]
              :on-click (fn [_e]
@@ -178,16 +170,18 @@
             [:div.heading-bg.remove "-"]]]]
             [:div.heading-bg.remove "-"]]]]
 
 
          [:div.flex.flex-row.justify-between.pb-2.pt-1.px-2.items-center
          [:div.flex.flex-row.justify-between.pb-2.pt-1.px-2.items-center
-          [:div.flex.flex-row.justify-between.flex-1
+          [:div.flex.flex-row.justify-between.flex-1.px-1
            (for [i (range 1 7)]
            (for [i (range 1 7)]
              (ui/button
              (ui/button
               (str "H" i)
               (str "H" i)
+              :class "to-heading-button"
               :on-click (fn [_e]
               :on-click (fn [_e]
                           (editor-handler/set-heading! block-id format i))
                           (editor-handler/set-heading! block-id format i))
               :intent "link"
               :intent "link"
               :small? true))
               :small? true))
            (ui/button
            (ui/button
             "H-"
             "H-"
+            :class "to-heading-button"
             :title (t :remove-heading)
             :title (t :remove-heading)
             :on-click (fn [_e]
             :on-click (fn [_e]
                         (editor-handler/remove-heading! block-id format))
                         (editor-handler/remove-heading! block-id format))

+ 4 - 4
src/main/frontend/components/encryption.cljs

@@ -170,8 +170,8 @@
          [:div.input-hints.text-sm.py-2.px-3.rounded.mb-2.mt-2.flex.items-center
          [:div.input-hints.text-sm.py-2.px-3.rounded.mb-2.mt-2.flex.items-center
           (if-let [display-str (:fail set-remote-graph-pwd-result)]
           (if-let [display-str (:fail set-remote-graph-pwd-result)]
             [:<>
             [:<>
-             [:span.flex.pr-1.text-red-600 (ui/icon "alert-circle" {:class "text-md mr-1"})]
-             [:span.text-red-600 display-str]]
+             [:span.flex.pr-1.text-error (ui/icon "alert-circle" {:class "text-md mr-1"})]
+             [:span.text-error display-str]]
             [:<>
             [:<>
              [:span.flex.pr-1 (ui/icon "bulb" {:class "text-md mr-1"})]
              [:span.flex.pr-1 (ui/icon "bulb" {:class "text-md mr-1"})]
              [:span "Please enter the password for this graph to continue syncing."]])]])
              [:span "Please enter the password for this graph to continue syncing."]])]])
@@ -187,8 +187,8 @@
                     (not (string/blank? @*pw-confirm)))
                     (not (string/blank? @*pw-confirm)))
               (if (or (not (pattern-ok?))
               (if (or (not (pattern-ok?))
                       (not= @*password @*pw-confirm))
                       (not= @*password @*pw-confirm))
-                [:span.flex.pr-1.text-red-600 (ui/icon "alert-circle" {:class "text-md mr-1"})]
-                [:span.flex.pr-1.text-green-600 (ui/icon "circle-check" {:class "text-md mr-1"})])
+                [:span.flex.pr-1.text-error (ui/icon "alert-circle" {:class "text-md mr-1"})]
+                [:span.flex.pr-1.text-success (ui/icon "circle-check" {:class "text-md mr-1"})])
               [:span.flex.pr-1 (ui/icon "bulb" {:class "text-md mr-1"})])
               [:span.flex.pr-1 (ui/icon "bulb" {:class "text-md mr-1"})])
 
 
             (if (not (string/blank? @*password))
             (if (not (string/blank? @*password))

+ 2 - 2
src/main/frontend/components/file_sync.cljs

@@ -85,8 +85,8 @@
       (when (not (string/blank? selected-path))
       (when (not (string/blank? selected-path))
         [:h5.text-xs.pt-1.-mb-1.flex.items-center.leading-none
         [:h5.text-xs.pt-1.-mb-1.flex.items-center.leading-none
          (if (mobile-util/iCloud-container-path? selected-path)
          (if (mobile-util/iCloud-container-path? selected-path)
-           [:span.inline-block.pr-1.text-red-600.scale-75 (ui/icon "alert-circle")]
-           [:span.inline-block.pr-1.text-green-600.scale-75 (ui/icon "circle-check")])
+           [:span.inline-block.pr-1.text-error.scale-75 (ui/icon "alert-circle")]
+           [:span.inline-block.pr-1.text-success.scale-75 (ui/icon "circle-check")])
          selected-path])
          selected-path])
 
 
       [:div.out-icloud
       [:div.out-icloud

+ 6 - 0
src/main/frontend/components/file_sync.css

@@ -1,3 +1,9 @@
+:root {
+  --ls-color-file-sync-error: var(--ls-error-color);
+  --ls-color-file-sync-pending: var(--color-yellow-500);
+  --ls-color-file-sync-idle: var(--ls-success-color);
+}
+
 .cp__file-sync {
 .cp__file-sync {
   &-indicator {
   &-indicator {
     a.cloud {
     a.cloud {

+ 2 - 2
src/main/frontend/components/page.cljs

@@ -778,8 +778,8 @@
   (fn [close-fn]
   (fn [close-fn]
     [:div
     [:div
      [:div.sm:flex.items-center
      [:div.sm:flex.items-center
-      [:div.mx-auto.flex-shrink-0.flex.items-center.justify-center.h-12.w-12.rounded-full.bg-red-100.sm:mx-0.sm:h-10.sm:w-10
-       [:span.text-red-600.text-xl
+      [:div.mx-auto.flex-shrink-0.flex.items-center.justify-center.h-12.w-12.rounded-full.bg-error.sm:mx-0.sm:h-10.sm:w-10
+       [:span.text-error.text-xl
         (ui/icon "alert-triangle")]]
         (ui/icon "alert-triangle")]]
       [:div.mt-3.text-center.sm:mt-0.sm:ml-4.sm:text-left
       [:div.mt-3.text-center.sm:mt-0.sm:ml-4.sm:text-left
        [:h3#modal-headline.text-lg.leading-6.font-medium
        [:h3#modal-headline.text-lg.leading-6.font-medium

+ 1 - 6
src/main/frontend/components/page.css

@@ -133,7 +133,7 @@
 
 
     .r {
     .r {
       @apply text-base space-x-2;
       @apply text-base space-x-2;
-      
+
       a.button {
       a.button {
         color: var(--ls-primary-text-color);
         color: var(--ls-primary-text-color);
         margin-top: 1px;
         margin-top: 1px;
@@ -364,11 +364,6 @@ html.is-native-ios {
   }
   }
  }
  }
 
 
- .text-2xs {
-  font-size: 0.625rem; /* 10px */
-  line-height: 0.875rem; /* 14px */
- }
-
  .toned-down {
  .toned-down {
    opacity: 0.5;
    opacity: 0.5;
   color: var(--ls-secondary-text-color);
   color: var(--ls-secondary-text-color);

+ 2 - 2
src/main/frontend/components/page_menu.cljs

@@ -33,8 +33,8 @@
   (fn [close-fn]
   (fn [close-fn]
     [:div
     [:div
      [:div.sm:flex.items-center
      [:div.sm:flex.items-center
-      [:div.mx-auto.flex-shrink-0.flex.items-center.justify-center.h-12.w-12.rounded-full.bg-red-100.sm:mx-0.sm:h-10.sm:w-10
-       [:span.text-red-600.text-xl
+      [:div.mx-auto.flex-shrink-0.flex.items-center.justify-center.h-12.w-12.rounded-full.bg-error.sm:mx-0.sm:h-10.sm:w-10
+       [:span.text-error.text-xl
         (ui/icon "alert-triangle")]]
         (ui/icon "alert-triangle")]]
       [:div.mt-3.text-center.sm:mt-0.sm:ml-4.sm:text-left
       [:div.mt-3.text-center.sm:mt-0.sm:ml-4.sm:text-left
        [:h3#modal-headline.text-lg.leading-6.font-medium
        [:h3#modal-headline.text-lg.leading-6.font-medium

+ 3 - 3
src/main/frontend/components/plugins.cljs

@@ -292,7 +292,7 @@
         svg/folder)
         svg/folder)
 
 
       (when (and (not market?) unpacked?)
       (when (and (not market?) unpacked?)
-        [:span.flex.justify-center.text-xs.text-red-500.pt-2 (t :plugin/unpacked)])]
+        [:span.flex.justify-center.text-xs.text-error.pt-2 (t :plugin/unpacked)])]
 
 
      [:div.r
      [:div.r
       [:h3.head.text-xl.font-bold.pt-1.5
       [:h3.head.text-xl.font-bold.pt-1.5
@@ -1004,7 +1004,7 @@
   [:div
   [:div
    [:span.block.whitespace-normal
    [:span.block.whitespace-normal
     "This plugin "
     "This plugin "
-    [:strong.text-red-500 "#" name]
+    [:strong.text-error "#" name]
     " takes too long to load, affecting the application startup time and
     " takes too long to load, affecting the application startup time and
      potentially causing other plugins to fail to load."]
      potentially causing other plugins to fail to load."]
 
 
@@ -1021,7 +1021,7 @@
                                 (notification/clear! pid)
                                 (notification/clear! pid)
                                 (notification/show!
                                 (notification/show!
                                  [:span "The plugin "
                                  [:span "The plugin "
-                                  [:strong.text-red-500 "#" name]
+                                  [:strong.text-error "#" name]
                                   " is disabled."] :success
                                   " is disabled."] :success
                                  true nil 3000)))
                                  true nil 3000)))
                      (p/catch #(js/console.error %)))))]])
                      (p/catch #(js/console.error %)))))]])

+ 3 - 3
src/main/frontend/components/reference.cljs

@@ -148,11 +148,11 @@
                                    (empty? filter-state)
                                    (empty? filter-state)
                                    ""
                                    ""
                                    (every? true? (vals filter-state))
                                    (every? true? (vals filter-state))
-                                   "text-green-400"
+                                   "text-success"
                                    (every? false? (vals filter-state))
                                    (every? false? (vals filter-state))
-                                   "text-red-400"
+                                   "text-error"
                                    :else
                                    :else
-                                   "text-yellow-400")
+                                   "text-warning")
                           :style {:fontSize 24}})]]
                           :style {:fontSize 24}})]]
 
 
      (fn []
      (fn []

+ 7 - 10
src/main/frontend/components/svg.cljs

@@ -70,7 +70,7 @@
      :d               "M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"}]])
      :d               "M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"}]])
 (rum/defc note
 (rum/defc note
   []
   []
-  [:svg.h-8.w-8.svg-shadow.note
+  [:svg.h-8.w-8.note
    {:view-box "0 0 512 512"
    {:view-box "0 0 512 512"
     :fill     "currentColor"}
     :fill     "currentColor"}
    [:path
    [:path
@@ -88,20 +88,18 @@
 
 
 (rum/defc important
 (rum/defc important
   []
   []
-  [:svg.h-8.w-8.svg-shadow.important
+  [:svg.h-8.w-8.important
    {:view-box "0 0 512 512"
    {:view-box "0 0 512 512"
-    :fill     "currentColor"
-    :color    "#bf0000"}
+    :fill     "var(--ls-error-color)"}
    [:path
    [:path
     {:d
     {:d
      "M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"}]])
      "M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"}]])
 
 
 (rum/defc caution
 (rum/defc caution
   []
   []
-  [:svg.h-8.w-8.svg-shadow.caution
+  [:svg.h-8.w-8.caution
    {:view-box "0 0 384 512"
    {:view-box "0 0 384 512"
-    :fill     "currentColor"
-    :color    "#bf3400"}
+    :fill     "var(--ls-warning-color)"}
    [:path
    [:path
     {:d
     {:d
      "M216 23.86c0-23.8-30.65-32.77-44.15-13.04C48 191.85 224 200 224 288c0 35.63-29.11 64.46-64.85 63.99-35.17-.45-63.15-29.77-63.15-64.94v-85.51c0-21.7-26.47-32.23-41.43-16.5C27.8 213.16 0 261.33 0 320c0 105.87 86.13 192 192 192s192-86.13 192-192c0-170.29-168-193-168-296.14z"}]])
      "M216 23.86c0-23.8-30.65-32.77-44.15-13.04C48 191.85 224 200 224 288c0 35.63-29.11 64.46-64.85 63.99-35.17-.45-63.15-29.77-63.15-64.94v-85.51c0-21.7-26.47-32.23-41.43-16.5C27.8 213.16 0 261.33 0 320c0 105.87 86.13 192 192 192s192-86.13 192-192c0-170.29-168-193-168-296.14z"}]])
@@ -110,11 +108,10 @@
   ([]
   ([]
    (warning nil))
    (warning nil))
   ([opts]
   ([opts]
-   [:svg.h-8.w-8.svg-shadow.warning
+   [:svg.h-8.w-8.warning
     (merge
     (merge
       {:view-box "0 0 576 512"
       {:view-box "0 0 576 512"
-       :fill     "currentColor"
-       :color    "#bf6900"}
+       :fill     "var(--ls-warning-color)"}
       opts)
       opts)
     [:path
     [:path
      {:d
      {:d

+ 6 - 0
src/main/frontend/config.cljs

@@ -425,6 +425,12 @@
     (string/replace
     (string/replace
      source "../assets" (util/format "%s://%s/assets" protocol (get-repo-dir (state/get-current-repo))))))
      source "../assets" (util/format "%s://%s/assets" protocol (get-repo-dir (state/get-current-repo))))))
 
 
+(defn get-current-repo-assets-root
+  []
+  (when-let [repo-root (and (local-db? (state/get-current-repo))
+                            (get-repo-dir (state/get-current-repo)))]
+    (util/node-path.join repo-root "assets")))
+
 (defn get-custom-js-path
 (defn get-custom-js-path
   ([]
   ([]
    (get-custom-js-path (state/get-current-repo)))
    (get-custom-js-path (state/get-current-repo)))

+ 7 - 0
src/main/frontend/dicts.cljc

@@ -148,6 +148,13 @@
         :asset/maximize "Maximize image"
         :asset/maximize "Maximize image"
         :asset/confirm-delete "Are you sure you want to delete this {1}?"
         :asset/confirm-delete "Are you sure you want to delete this {1}?"
         :asset/physical-delete "Remove the file too (notice it can't be restored)"
         :asset/physical-delete "Remove the file too (notice it can't be restored)"
+        :color/gray "Gray"
+        :color/red "Red"
+        :color/yellow "Yellow"
+        :color/green "Green"
+        :color/blue "Blue"
+        :color/purple "Purple"
+        :color/pink "Pink"
         :content/copy "Copy"
         :content/copy "Copy"
         :content/cut "Cut"
         :content/cut "Cut"
         :content/make-todos "Make {1}s"
         :content/make-todos "Make {1}s"

+ 11 - 6
src/main/frontend/extensions/excalidraw.cljs

@@ -3,7 +3,7 @@
             [clojure.string :as string]
             [clojure.string :as string]
             ;; NOTE: Always use production build of excalidraw
             ;; NOTE: Always use production build of excalidraw
             ;; See-also: https://github.com/excalidraw/excalidraw/pull/3330
             ;; See-also: https://github.com/excalidraw/excalidraw/pull/3330
-            ["@excalidraw/excalidraw/dist/excalidraw.production.min" :as Excalidraw]
+            ["@excalidraw/excalidraw/dist/excalidraw.production.min" :refer [Excalidraw serializeAsJSON]]
             [frontend.config :as config]
             [frontend.config :as config]
             [frontend.db :as db]
             [frontend.db :as db]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.editor :as editor-handler]
@@ -15,11 +15,11 @@
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util :as util]
             [goog.object :as gobj]
             [goog.object :as gobj]
+            [goog.functions :refer [debounce]]
             [rum.core :as rum]
             [rum.core :as rum]
             [frontend.mobile.util :as mobile-util]))
             [frontend.mobile.util :as mobile-util]))
 
 
-(def excalidraw (r/adapt-class (gobj/get Excalidraw "default")))
-(def serialize-as-json (gobj/get Excalidraw "serializeAsJSON"))
+(def excalidraw (r/adapt-class Excalidraw))
 
 
 (defn from-json
 (defn from-json
   [text]
   [text]
@@ -64,8 +64,13 @@
   (rum/local false ::view-mode?)
   (rum/local false ::view-mode?)
   (rum/local false ::grid-mode?)
   (rum/local false ::grid-mode?)
   (rum/local nil ::elements)
   (rum/local nil ::elements)
-  {:did-mount update-draw-content-width
-   :did-update update-draw-content-width}
+  (rum/local nil ::resize-observer)
+  {:did-mount (fn [state]
+                (reset! (::resize-observer state) (js/ResizeObserver. (debounce #(reset! (::draw-width state) 0) 300)))
+                (.observe @(::resize-observer state) (ui/main-node))
+                (update-draw-content-width state))
+   :did-update update-draw-content-width
+   :will-unmount (fn [state] (.disconnect @(::resize-observer state)))}
   [state data option]
   [state data option]
   (let [*draw-width (get state ::draw-width)
   (let [*draw-width (get state ::draw-width)
         *zen-mode? (get state ::zen-mode?)
         *zen-mode? (get state ::zen-mode?)
@@ -109,7 +114,7 @@
                               (reset! *elements elements->clj)
                               (reset! *elements elements->clj)
                               (draw/save-excalidraw!
                               (draw/save-excalidraw!
                                file
                                file
-                               (serialize-as-json elements app-state))))))
+                               (serializeAsJSON elements app-state))))))
 
 
            :zen-mode-enabled @*zen-mode?
            :zen-mode-enabled @*zen-mode?
            :view-mode-enabled @*view-mode?
            :view-mode-enabled @*view-mode?

+ 10 - 10
src/main/frontend/extensions/pdf/pdf.css

@@ -1,11 +1,11 @@
 @import "_viewer.css";
 @import "_viewer.css";
 
 
 :root {
 :root {
-  --ph-highlight-color-blue: #55b8fe;
-  --ph-highlight-color-green: #46da48;
-  --ph-highlight-color-red: #fd709a;
-  --ph-highlight-color-purple: #ac8cf1;
-  --ph-highlight-color-yellow: #fcd713;
+  --ph-highlight-color-blue: var(--color-blue-100);
+  --ph-highlight-color-green: var(--color-green-100);
+  --ph-highlight-color-red: var(--color-red-100);
+  --ph-highlight-color-purple: var(--color-purple-100);
+  --ph-highlight-color-yellow: var(--color-yellow-100);
 
 
   --ph-highlight-scroll-into-color: rgba(255, 75, 93, 0.67);
   --ph-highlight-scroll-into-color: rgba(255, 75, 93, 0.67);
 
 
@@ -385,23 +385,23 @@ input::-webkit-inner-spin-button {
           }
           }
 
 
           &[data-color=yellow] {
           &[data-color=yellow] {
-            background-color: var(--ph-highlight-color-yellow);
+            background-color: var(--color-yellow-500);
           }
           }
 
 
           &[data-color=blue] {
           &[data-color=blue] {
-            background-color: var(--ph-highlight-color-blue);
+            background-color: var(--color-blue-500);
           }
           }
 
 
           &[data-color=green] {
           &[data-color=green] {
-            background-color: var(--ph-highlight-color-green);
+            background-color: var(--color-green-500);
           }
           }
 
 
           &[data-color=red] {
           &[data-color=red] {
-            background-color: var(--ph-highlight-color-red);
+            background-color: var(--color-red-500);
           }
           }
 
 
           &[data-color=purple] {
           &[data-color=purple] {
-            background-color: var(--ph-highlight-color-purple);
+            background-color: var(--color-purple-500);
           }
           }
         }
         }
       }
       }

+ 3 - 3
src/main/frontend/extensions/zotero.cljs

@@ -95,7 +95,7 @@
 
 
        [:span.animate-spin-reverse {:style {:visibility (if is-searching "visible"  "hidden")}}  svg/refresh]]]
        [:span.animate-spin-reverse {:style {:visibility (if is-searching "visible"  "hidden")}}  svg/refresh]]]
 
 
-     [:div.h-2.text-sm.text-red-400.mb-2 (if search-error (str "Search error: " search-error) "")]
+     [:div.h-2.text-sm.text-error.mb-2 (if search-error (str "Search error: " search-error) "")]
 
 
      [:div
      [:div
       (map
       (map
@@ -154,7 +154,7 @@
          (not (re-matches #"^\d+$" (str @(::type-id state)))))
          (not (re-matches #"^\d+$" (str @(::type-id state)))))
      (ui/admonition
      (ui/admonition
       :warning
       :warning
-      [:p.text-red-500
+      [:p.text-error
        "User ID is different from username and can be found on the "
        "User ID is different from username and can be found on the "
        [:a {:href "https://www.zotero.org/settings/keys" :target "_blank"}
        [:a {:href "https://www.zotero.org/settings/keys" :target "_blank"}
         "https://www.zotero.org/settings/keys"]
         "https://www.zotero.org/settings/keys"]
@@ -176,7 +176,7 @@
    (when (setting/setting :overwrite-mode?)
    (when (setting/setting :overwrite-mode?)
      (ui/admonition
      (ui/admonition
       :warning
       :warning
-      [:p.text-red-500
+      [:p.text-error
        "Dangerous! This will delete and recreate Zotero existing page! Make sure to backup your notes first in case something goes wrong. Make sure you don't put any personal item in previous Zotero page and it's OK to overwrite the page!"]))])
        "Dangerous! This will delete and recreate Zotero existing page! Make sure to backup your notes first in case something goes wrong. Make sure you don't put any personal item in previous Zotero page and it's OK to overwrite the page!"]))])
 
 
 (rum/defc attachment-setting <
 (rum/defc attachment-setting <

+ 9 - 6
src/main/frontend/handler/editor.cljs

@@ -315,12 +315,12 @@
   (let [block (or (and (:db/id block) (db/pull (:db/id block))) block)
   (let [block (or (and (:db/id block) (db/pull (:db/id block))) block)
         block (merge block
         block (merge block
                      (block/parse-title-and-body uuid format pre-block? (:block/content block)))
                      (block/parse-title-and-body uuid format pre-block? (:block/content block)))
-        properties (:block/properties block)
+        properties (-> (:block/properties block)
+                       (dissoc :heading))
         real-content (:block/content block)
         real-content (:block/content block)
-        content (let [properties (if (= format :markdown) (dissoc properties :heading) properties)]
-                  (if (and (seq properties) real-content (not= real-content content))
-                   (property/with-built-in-properties properties content format)
-                   content))
+        content (if (and (seq properties) real-content (not= real-content content))
+                  (property/with-built-in-properties properties content format)
+                  content)
         content (drawer/with-logbook block content)
         content (drawer/with-logbook block content)
         content (with-timetracking block content)
         content (with-timetracking block content)
         first-block? (= left page)
         first-block? (= left page)
@@ -370,6 +370,7 @@
     (profile
     (profile
      "Save block: "
      "Save block: "
      (let [block' (wrap-parse-block block)]
      (let [block' (wrap-parse-block block)]
+       (util/pprint block')
        (outliner-tx/transact!
        (outliner-tx/transact!
          {:outliner-op :save-block}
          {:outliner-op :save-block}
          (outliner-core/save-block! block'))
          (outliner-core/save-block! block'))
@@ -3495,7 +3496,9 @@
           block (db/entity [:block/uuid block-id])
           block (db/entity [:block/uuid block-id])
           content' (commands/set-markdown-heading (:block/content block) heading)]
           content' (commands/set-markdown-heading (:block/content block) heading)]
       (save-block! repo block-id content'))
       (save-block! repo block-id content'))
-    (set-block-property! block-id "heading" heading)))
+    (do
+      (save-current-block!)
+      (set-block-property! block-id "heading" heading))))
 
 
 (defn remove-heading!
 (defn remove-heading!
   [block-id format]
   [block-id format]

+ 1 - 1
src/main/frontend/mobile/record.cljs

@@ -44,7 +44,7 @@
 
 
 (defn- embed-audio [database64]
 (defn- embed-audio [database64]
   (p/let [page (or (state/get-current-page) (string/lower-case (date/journal-name)))
   (p/let [page (or (state/get-current-page) (string/lower-case (date/journal-name)))
-          filename (str (date/get-date-time-string-2) ".mp3")
+          filename (str (date/get-date-time-string-2) ".m4a")
           edit-block (state/get-edit-block)
           edit-block (state/get-edit-block)
           format (or (:block/format edit-block) (db/get-page-format page))
           format (or (:block/format edit-block) (db/get-page-format page))
           path (editor-handler/get-asset-path filename)
           path (editor-handler/get-asset-path filename)

+ 15 - 7
src/main/frontend/ui.cljs

@@ -52,6 +52,14 @@
 
 
 (defonce icon-size (if (mobile-util/native-platform?) 26 20))
 (defonce icon-size (if (mobile-util/native-platform?) 26 20))
 
 
+(def block-background-colors
+  ["gray"
+   "red"
+   "yellow"
+   "green"
+   "blue"
+   "purple"])
+
 (rum/defc ls-textarea
 (rum/defc ls-textarea
   < rum/reactive
   < rum/reactive
   {:did-mount (fn [state]
   {:did-mount (fn [state]
@@ -207,7 +215,7 @@
           (case status
           (case status
             :success
             :success
             [:svg.h-6.w-6.text-green-400
             [:svg.h-6.w-6.text-green-400
-             {:stroke "currentColor", :viewBox "0 0 24 24", :fill "none"}
+             {:stroke "var(--ls-success-color)", :viewBox "0 0 24 24", :fill "none"}
              [:path
              [:path
               {:d               "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
               {:d               "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
                :stroke-width    "2"
                :stroke-width    "2"
@@ -215,7 +223,7 @@
                :stroke-linecap  "round"}]]
                :stroke-linecap  "round"}]]
             :warning
             :warning
             [:svg.h-6.w-6.text-yellow-500
             [:svg.h-6.w-6.text-yellow-500
-             {:stroke "currentColor", :viewBox "0 0 24 24", :fill "none"}
+             {:stroke "var(--ls-warning-color)", :viewBox "0 0 24 24", :fill "none"}
              [:path
              [:path
               {:d               "M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
               {:d               "M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
                :stroke-width    "2"
                :stroke-width    "2"
@@ -223,7 +231,7 @@
                :stroke-linecap  "round"}]]
                :stroke-linecap  "round"}]]
 
 
             [:svg.h-6.w-6.text-red-500
             [:svg.h-6.w-6.text-red-500
-             {:view-box "0 0 20 20", :fill "currentColor"}
+             {:view-box "0 0 20 20", :fill "var(--ls-error-color)"}
              [:path
              [:path
               {:clip-rule "evenodd"
               {:clip-rule "evenodd"
                :d
                :d
@@ -602,8 +610,8 @@
       [:div.ui__confirm-modal
       [:div.ui__confirm-modal
        {:class (str "is-" tag)}
        {:class (str "is-" tag)}
        [:div.sm:flex.sm:items-start
        [:div.sm:flex.sm:items-start
-        [:div.mx-auto.flex-shrink-0.flex.items-center.justify-center.h-12.w-12.rounded-full.bg-red-100.sm:mx-0.sm:h-10.sm:w-10
-         [:svg.h-6.w-6.text-red-600
+        [:div.mx-auto.flex-shrink-0.flex.items-center.justify-center.h-12.w-12.rounded-full.bg-error.sm:mx-0.sm:h-10.sm:w-10
+         [:svg.h-6.w-6.text-error
           {:stroke "currentColor", :view-box "0 0 24 24", :fill "none"}
           {:stroke "currentColor", :view-box "0 0 24 24", :fill "none"}
           [:path
           [:path
            {:d
            {:d
@@ -741,7 +749,7 @@
                       nil)]
                       nil)]
       [:div.flex.flex-row.admonitionblock.align-items {:class type}
       [:div.flex.flex-row.admonitionblock.align-items {:class type}
        [:div.pr-4.admonition-icon.flex.flex-col.justify-center
        [:div.pr-4.admonition-icon.flex.flex-col.justify-center
-        {:title (string/upper-case type)} (icon)]
+        {:title (string/capitalize type)} (icon)]
        [:div.ml-4.text-lg
        [:div.ml-4.text-lg
         content]])))
         content]])))
 
 
@@ -774,7 +782,7 @@
   [:section.border.mt-1.p-1.cursor-pointer.block-content-fallback-ui
   [:section.border.mt-1.p-1.cursor-pointer.block-content-fallback-ui
    section-attrs
    section-attrs
    [:div.flex.justify-between.items-center.px-1
    [:div.flex.justify-between.items-center.px-1
-    [:h5.text-red-600.pb-1 title]
+    [:h5.text-error.pb-1 title]
     [:a.text-xs.opacity-50.hover:opacity-80
     [:a.text-xs.opacity-50.hover:opacity-80
      {:href "https://github.com/logseq/logseq/issues/new?labels=from:in-app&template=bug_report.yaml"
      {:href "https://github.com/logseq/logseq/issues/new?labels=from:in-app&template=bug_report.yaml"
       :target "_blank"} "report issue"]]
       :target "_blank"} "report issue"]]

+ 101 - 51
src/main/logseq/api.cljs

@@ -147,86 +147,136 @@
           path (util/node-path.join path "package.json")]
           path (util/node-path.join path "package.json")]
       (fs/write-file! repo "" path (js/JSON.stringify data nil 2) {:skip-compare? true}))))
       (fs/write-file! repo "" path (js/JSON.stringify data nil 2) {:skip-compare? true}))))
 
 
+(defn ^:private write_rootdir_file
+  [file content sub-root root-dir]
+  (p/let [repo           ""
+          path           (util/node-path.join root-dir sub-root)
+          exist?         (fs/file-exists? path "")
+          _              (when-not exist? (fs/mkdir-recur! path))
+          user-path      (util/node-path.join path file)
+          sub-dir?       (string/starts-with? user-path path)
+          _              (when-not sub-dir?
+                           (log/info :debug user-path)
+                           (throw "write file denied"))
+          user-path-root (util/node-path.dirname user-path)
+          exist?         (fs/file-exists? user-path-root "")
+          _              (when-not exist? (fs/mkdir-recur! user-path-root))
+          _              (fs/write-file! repo "" user-path content {:skip-compare? true})]
+    user-path))
+
 (defn ^:private write_dotdir_file
 (defn ^:private write_dotdir_file
   [file content sub-root]
   [file content sub-root]
-  (p/let [repo ""
-          path (plugin-handler/get-ls-dotdir-root)
-          path (util/node-path.join path sub-root)
-          exist? (fs/file-exists? path "")
-          _ (when-not exist? (fs/mkdir-recur! path))
+  (some-> (plugin-handler/get-ls-dotdir-root)
+          (p/then #(write_rootdir_file file content sub-root %))))
+
+(defn ^:private write_assetsdir_file
+  [file content sub-root]
+  (if-let [assets-dir (config/get-current-repo-assets-root)]
+    (write_rootdir_file file content sub-root assets-dir)
+    false))
+
+(defn ^:private read_rootdir_file
+  [file sub-root root-dir]
+  (p/let [path      (util/node-path.join root-dir sub-root)
           user-path (util/node-path.join path file)
           user-path (util/node-path.join path file)
-          sub-dir? (string/starts-with? user-path path)
-          _ (when-not sub-dir?
-              (log/info :debug user-path)
-              (throw "write file denied"))
-          user-path-root (util/node-path.dirname user-path)
-          exist? (fs/file-exists? user-path-root "")
-          _ (when-not exist? (fs/mkdir-recur! user-path-root))
-          _ (fs/write-file! repo "" user-path content {:skip-compare? true})]
-    user-path))
+          sub-dir?  (string/starts-with? user-path path)
+          _         (when-not sub-dir? (log/info :debug user-path) (throw "read file denied"))
+          exist?    (fs/file-exists? "" user-path)
+          _         (when-not exist? (log/info :debug user-path) (throw "file not existed"))
+          content   (fs/read-file "" user-path)]
+    content))
 
 
 (defn ^:private read_dotdir_file
 (defn ^:private read_dotdir_file
   [file sub-root]
   [file sub-root]
-  (p/let [path (plugin-handler/get-ls-dotdir-root)
-          path (util/node-path.join path sub-root)
+  (some-> (plugin-handler/get-ls-dotdir-root)
+          (p/then #(read_rootdir_file file sub-root %))))
+
+(defn ^:private read_assetsdir_file
+  [file sub-root]
+  (when-let [root-dir (config/get-current-repo-assets-root)]
+    (read_rootdir_file file sub-root root-dir)))
+
+(defn ^:private unlink_rootdir_file!
+  [file sub-root root-dir]
+  (p/let [repo      ""
+          path      (util/node-path.join root-dir sub-root)
           user-path (util/node-path.join path file)
           user-path (util/node-path.join path file)
-          sub-dir? (string/starts-with? user-path path)
-          _ (when-not sub-dir? (log/info :debug user-path) (throw "read file denied"))
-          exist? (fs/file-exists? "" user-path)
-          _ (when-not exist? (log/info :debug user-path) (throw "file not existed"))
-          content (fs/read-file "" user-path)]
-    content))
+          sub-dir?  (string/starts-with? user-path path)
+          _         (when-not sub-dir? (log/info :debug user-path) (throw "access file denied"))
+          exist?    (fs/file-exists? "" user-path)
+          _         (when-not exist? (log/info :debug user-path) (throw "file not existed"))
+          _         (fs/unlink! repo user-path {})]))
 
 
 (defn ^:private unlink_dotdir_file!
 (defn ^:private unlink_dotdir_file!
   [file sub-root]
   [file sub-root]
-  (p/let [repo ""
-          path (plugin-handler/get-ls-dotdir-root)
-          path (util/node-path.join path sub-root)
-          user-path (util/node-path.join path file)
-          sub-dir? (string/starts-with? user-path path)
-          _ (when-not sub-dir? (log/info :debug user-path) (throw "access file denied"))
-          exist? (fs/file-exists? "" user-path)
-          _ (when-not exist? (log/info :debug user-path) (throw "file not existed"))
-          _ (fs/unlink! repo user-path {})]))
+  (some-> (plugin-handler/get-ls-dotdir-root)
+          (p/then #(unlink_rootdir_file! file sub-root %))))
+
+(defn ^:private unlink_assetsdir_file!
+  [file sub-root]
+  (when-let [root-dir (config/get-current-repo-assets-root)]
+    (unlink_rootdir_file! file sub-root root-dir)))
 
 
 (def ^:export write_user_tmp_file
 (def ^:export write_user_tmp_file
   (fn [file content]
   (fn [file content]
     (write_dotdir_file file content "tmp")))
     (write_dotdir_file file content "tmp")))
 
 
 (def ^:export write_plugin_storage_file
 (def ^:export write_plugin_storage_file
-  (fn [plugin-id file content]
-    (write_dotdir_file
-      file content
-      (let [plugin-id (util/node-path.basename plugin-id)]
-        (util/node-path.join "storages" plugin-id)))))
+  (fn [plugin-id file content assets?]
+    (let [plugin-id (util/node-path.basename plugin-id)
+          sub-root  (util/node-path.join "storages" plugin-id)]
+      (if (true? assets?)
+        (write_assetsdir_file file content sub-root)
+        (write_dotdir_file file content sub-root)))))
 
 
 (def ^:export read_plugin_storage_file
 (def ^:export read_plugin_storage_file
-  (fn [plugin-id file]
-    (let [plugin-id (util/node-path.basename plugin-id)]
-      (read_dotdir_file
-        file (util/node-path.join "storages" plugin-id)))))
+  (fn [plugin-id file assets?]
+    (let [plugin-id (util/node-path.basename plugin-id)
+          sub-root (util/node-path.join "storages" plugin-id)]
+      (if (true? assets?)
+        (read_assetsdir_file file sub-root)
+        (read_dotdir_file file sub-root)))))
 
 
 (def ^:export unlink_plugin_storage_file
 (def ^:export unlink_plugin_storage_file
-  (fn [plugin-id file]
-    (let [plugin-id (util/node-path.basename plugin-id)]
-      (unlink_dotdir_file!
-        file (util/node-path.join "storages" plugin-id)))))
+  (fn [plugin-id file assets?]
+    (let [plugin-id (util/node-path.basename plugin-id)
+          sub-root (util/node-path.join "storages" plugin-id)]
+      (if (true? assets?)
+        (unlink_assetsdir_file! file sub-root)
+        (unlink_dotdir_file! file sub-root)))))
 
 
 (def ^:export exist_plugin_storage_file
 (def ^:export exist_plugin_storage_file
-  (fn [plugin-id file]
-    (p/let [root (plugin-handler/get-ls-dotdir-root)
+  (fn [plugin-id file assets?]
+    (p/let [root      (if (true? assets?)
+                        (config/get-current-repo-assets-root)
+                        (plugin-handler/get-ls-dotdir-root))
             plugin-id (util/node-path.basename plugin-id)
             plugin-id (util/node-path.basename plugin-id)
-            exist? (fs/file-exists?
-                     (util/node-path.join root "storages" plugin-id)
-                     file)]
+            exist?    (fs/file-exists?
+                       (util/node-path.join root "storages" plugin-id)
+                       file)]
       exist?)))
       exist?)))
 
 
 (def ^:export clear_plugin_storage_files
 (def ^:export clear_plugin_storage_files
-  (fn [plugin-id]
-    (p/let [root (plugin-handler/get-ls-dotdir-root)
+  (fn [plugin-id assets?]
+    (p/let [root      (if (true? assets?)
+                        (config/get-current-repo-assets-root)
+                        (plugin-handler/get-ls-dotdir-root))
             plugin-id (util/node-path.basename plugin-id)]
             plugin-id (util/node-path.basename plugin-id)]
       (fs/rmdir! (util/node-path.join root "storages" plugin-id)))))
       (fs/rmdir! (util/node-path.join root "storages" plugin-id)))))
 
 
+(def ^:export list_plugin_storage_files
+  (fn [plugin-id assets?]
+    (p/let [root       (if (true? assets?)
+                         (config/get-current-repo-assets-root)
+                         (plugin-handler/get-ls-dotdir-root))
+            plugin-id  (util/node-path.basename plugin-id)
+            files-path (util/node-path.join root "storages" plugin-id)
+            ^js files  (ipc/ipc :listdir files-path)]
+      (when (js-iterable? files)
+        (bean/->js
+         (map #(some-> (string/replace-first % files-path "")
+                       (string/replace #"^/+" "")) files))))))
+
 (def ^:export load_user_preferences
 (def ^:export load_user_preferences
   (fn []
   (fn []
     (p/let [repo ""
     (p/let [repo ""

+ 1 - 1
tailwind.all.css

@@ -1,6 +1,7 @@
 @charset "utf-8";
 @charset "utf-8";
 @import "tailwindcss/base";
 @import "tailwindcss/base";
 @import "tailwindcss/components";
 @import "tailwindcss/components";
+@import "tailwindcss/utilities";
 @import "resources/css/inter.css";
 @import "resources/css/inter.css";
 @import "resources/css/reveal.min.css";
 @import "resources/css/reveal.min.css";
 @import "resources/css/reveal_black.min.css";
 @import "resources/css/reveal_black.min.css";
@@ -21,4 +22,3 @@
 @import "resources/css/srs_cards.css";
 @import "resources/css/srs_cards.css";
 @import "resources/css/tabler-extension.css";
 @import "resources/css/tabler-extension.css";
 @import-glob "src/main/frontend/**/[!_]*.css";
 @import-glob "src/main/frontend/**/[!_]*.css";
-@import "tailwindcss/utilities";

+ 49 - 7
tailwind.config.js

@@ -1,16 +1,56 @@
 const colors = require('tailwindcss/colors')
 const colors = require('tailwindcss/colors')
 
 
+function exposeColorsToCssVars({ addBase, theme }) {
+  function extractColorVars(colorObj, colorGroup = '') {
+    return Object.keys(colorObj).reduce((vars, colorKey) => {
+      const value = colorObj[colorKey];
+
+      const newVars =
+        typeof value === 'string'
+          ? { [`--color${colorGroup}-${colorKey}`]: value }
+          : extractColorVars(value, `-${colorKey}`);
+
+      return { ...vars, ...newVars };
+    }, {});
+  }
+
+  addBase({
+    ':root': extractColorVars(theme('colors')),
+  });
+}
+
 module.exports = {
 module.exports = {
   mode: 'jit',
   mode: 'jit',
-  purge: [
-    './src/**/*.js',
-    './src/**/*.cljs',
-    './resources/**/*.html',
-  ],
-  plugins: [require('@tailwindcss/ui')],
+  purge: {
+    content: [
+      './src/**/*.js',
+      './src/**/*.cljs',
+      './resources/**/*.html'
+    ],
+    safelist: [
+      "bg-black",
+      "bg-white",
+      "bg-gray-50",  "bg-red-50",  "bg-yellow-50",  "bg-green-50",  "bg-blue-50",  "bg-indigo-50",  "bg-orange-50",  "bg-rose-50",  "bg-purple-50",
+      "bg-gray-100", "bg-red-100", "bg-yellow-100", "bg-green-100", "bg-blue-100", "bg-indigo-100", "bg-orange-100", "bg-rose-100", "bg-purple-100", "bg-pink-100",
+      "bg-gray-200", "bg-red-200", "bg-yellow-200", "bg-green-200", "bg-blue-200", "bg-indigo-200", "bg-orange-200", "bg-rose-200", "bg-purple-200", "bg-pink-200",
+      "bg-gray-300", "bg-red-300", "bg-yellow-300", "bg-green-300", "bg-blue-300", "bg-indigo-300", "bg-orange-300", "bg-rose-300", "bg-purple-300", "bg-pink-300",
+      "bg-gray-400", "bg-red-400", "bg-yellow-400", "bg-green-400", "bg-blue-400", "bg-indigo-400", "bg-orange-400", "bg-rose-400", "bg-purple-400", "bg-pink-400",
+      "bg-gray-500", "bg-red-500", "bg-yellow-500", "bg-green-500", "bg-blue-500", "bg-indigo-500", "bg-orange-500", "bg-rose-500", "bg-purple-500", "bg-pink-500",
+      "bg-gray-600", "bg-red-600", "bg-yellow-600", "bg-green-600", "bg-blue-600", "bg-indigo-600", "bg-orange-600", "bg-rose-600", "bg-purple-600", "bg-pink-600",
+      "bg-gray-700", "bg-red-700", "bg-yellow-700", "bg-green-700", "bg-blue-700", "bg-indigo-700", "bg-orange-700", "bg-rose-700", "bg-purple-700", "bg-pink-700",
+      "bg-gray-800", "bg-red-800", "bg-yellow-800", "bg-green-800", "bg-blue-800", "bg-indigo-800", "bg-orange-800", "bg-rose-800", "bg-purple-800", "bg-pink-800",
+      "bg-gray-900", "bg-red-900", "bg-yellow-900", "bg-green-900", "bg-blue-900", "bg-indigo-900", "bg-orange-900", "bg-rose-900", "bg-purple-900", "bg-pink-900",
+    ]
+  },
+  plugins: [
+    require('@tailwindcss/ui'),
+    exposeColorsToCssVars],
   darkMode: 'class',
   darkMode: 'class',
   theme: {
   theme: {
     extend: {
     extend: {
+      fontSize: {
+        '2xs': ['0.625rem', '0.875rem']
+      },
       animation: {
       animation: {
         'spin-reverse': 'spin 2s linear infinite reverse',
         'spin-reverse': 'spin 2s linear infinite reverse',
       },
       },
@@ -48,7 +88,9 @@ module.exports = {
       red: colors.red,
       red: colors.red,
       yellow: colors.amber,
       yellow: colors.amber,
       orange: colors.orange,
       orange: colors.orange,
-      rose: colors.rose
+      rose: colors.rose,
+      purple: colors.purple,
+      pink: colors.pink
     }
     }
   }
   }
 }
 }

+ 11 - 4
yarn.lock

@@ -346,10 +346,12 @@
     global-agent "^3.0.0"
     global-agent "^3.0.0"
     global-tunnel-ng "^2.7.1"
     global-tunnel-ng "^2.7.1"
 
 
-"@excalidraw/[email protected]":
-  version "0.10.0"
-  resolved "https://registry.yarnpkg.com/@excalidraw/excalidraw/-/excalidraw-0.10.0.tgz#5329d6fb1b0cca068e2cd34da6d648dd6d0d3fec"
-  integrity sha512-0fHc/oX394dHAT7LEacwZ0vh8aeI179plYnfaeLeRHBtHARgmtlmvxcnxd2pxJ0Z1Uj/Cy76oK9MVw/y8P1HhQ==
+"@excalidraw/[email protected]":
+  version "0.12.0"
+  resolved "https://registry.yarnpkg.com/@excalidraw/excalidraw/-/excalidraw-0.12.0.tgz#0c281624f29aa7834a015035b21dfd928be861d4"
+  integrity sha512-xMPmKmOEgKij43k5m6Koaevb+SBw6La7MT9UDY8Iq7nQCMhA1HQwcUURfSkZ3ERibdQmMsAGtjSLbkX7hrA3+A==
+  dependencies:
+    dotenv "10.0.0"
 
 
 "@ionic/cli-framework-output@^2.2.1", "@ionic/cli-framework-output@^2.2.5":
 "@ionic/cli-framework-output@^2.2.1", "@ionic/cli-framework-output@^2.2.5":
   version "2.2.5"
   version "2.2.5"
@@ -2466,6 +2468,11 @@ domutils@^2.8.0:
     domelementtype "^2.2.0"
     domelementtype "^2.2.0"
     domhandler "^4.2.0"
     domhandler "^4.2.0"
 
 
[email protected]:
+  version "10.0.0"
+  resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81"
+  integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==
+
 duplexer3@^0.1.4:
 duplexer3@^0.1.4:
   version "0.1.5"
   version "0.1.5"
   resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.5.tgz#0b5e4d7bad5de8901ea4440624c8e1d20099217e"
   resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.5.tgz#0b5e4d7bad5de8901ea4440624c8e1d20099217e"

Some files were not shown because too many files changed in this diff