diff --git a/package.json b/package.json index 9ea1060..9ae1622 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sy-bookmark-plus", - "version": "1.4.1", + "version": "1.5.0", "type": "module", "description": "A more powerful bookmark", "repository": "", @@ -33,6 +33,7 @@ "vite-plugin-zip-pack": "^1.2.2" }, "dependencies": { + "@frostime/siyuan-plugin-kits": "^1.3.6", "@frostime/solid-signal-ref": "^2.0.0", "solid-transition-group": "^0.2.3" } diff --git a/plugin.json b/plugin.json index d1a4fba..0bf1d8f 100644 --- a/plugin.json +++ b/plugin.json @@ -2,7 +2,7 @@ "name": "sy-bookmark-plus", "author": "frostime", "url": "https://github.com/frostime/sy-bookmark-plus", - "version": "1.4.1", + "version": "1.5.0", "minAppVersion": "3.0.12", "backends": [ "all" diff --git a/public/i18n/en_US.yaml b/public/i18n/en_US.yaml index 9e03fe1..23aa046 100644 --- a/public/i18n/en_US.yaml +++ b/public/i18n/en_US.yaml @@ -1,130 +1,141 @@ ---- -msg: - ruleFailed: 'Failed to correctly parse the configuration rule: {0}' - ruleInvalid: 'The format of the group rule input is incorrect: {0}' - itemHasInGroup: The item is already in the target group - itemNotFoundInGroup: The item was not found in the source group - groupNameEmpty: The name of the bookmark group cannot be empty! -itemErr: - closed: 'The notebook "{0}" is already closed' - deleted: 'Content block not found, it may have been deleted! Old block content: [{0}]' -setting: - replaceDefault: - title: Replace Built-in Bookmarks - description: 'If enabled, the default bookmarks will be blocked, and bookmark shortcut keys (default is Alt+3) will be bound;
Takes effect after restart; Notice: not appliable in mobile devices!' - viewMode: - title: Display Mode - description: Select the style of the bookmark bar display - hideClosed: - title: Hide Closed Items - description: When enabled, hide items from closed notebooks - hideDeleted: - title: Hide Invalid Items - description: When enabled, hide items that have been deleted - grouplist: - title: Bookmark Groups - description: Set up bookmark groups - autoRefreshOnExpand: - title: Auto Refresh on Expand - description: Automatically refreshes the items within a dynamic group when it is expanded from a collapsed state. - ariaLabel: - title: Display Hover Box - description: Display full title of the bookmark item in a hover box when the mouse hovers -viewMode: - bookmark: Bookmark Style - card: Card Style bookmark: - new: Create New Bookmark Group + cache: Cache Current Bookmarks delete: - title: Delete the bookmark group {0}[{1}]? desc: ⚠️ Cannot be recovered after deletion! Are you sure you want to delete? - cache: Cache Current Bookmarks + title: Delete the bookmark group {0}[{1}]? logo: - name: Bookmark - setting: Settings add: Add Bookmark Group - refresh: Refresh - expand: Expand Ctrl+↓ collapse: Collapse Ctrl+↑ + expand: Expand Ctrl+↓ min: Minimize Ctrl+W + name: Bookmark + refresh: Refresh + setting: Settings + new: Create New Bookmark Group +bookmarktype: + composed: Composite Bookmark + dynamic: Dynamic Bookmark + normal: Normal Bookmark group: - msgexist: 'Cannot add: Block with ID [{0}] already exists in the bookmark group' - msg404: 'Cannot add: Block with ID [{0}] not found' - refresh: Refresh - copyref: Copy as Reference List + bottom: Move to Bottom copylink: Copy as Link List - msgcopy: Copy Successful + copyref: Copy as Reference List + currentdoc: Add Current Document Block + delete: Delete Bookmark Group docflow: Document Flow - rename: Rename Bookmark Group + down: Move Down edit: Edit Rule Value - delete: Delete Bookmark Group + fromclipboard: Insert Block from Clipboard move: Move + msg404: 'Cannot add: Block with ID [{0}] not found' + msgcopy: Copy Successful + msgdelconfirm: + - Delete bookmark item {0}? + - ⚠️ Cannot be recovered after deletion! Are you sure you want to delete? + msgexist: 'Cannot add: Block with ID [{0}] already exists in the bookmark group' + msgparse: Unable to parse block from [{0}] + refresh: Refresh + rename: Rename Bookmark Group top: Move to Top up: Move Up - down: Move Down - bottom: Move to Bottom - fromclipboard: Insert Block from Clipboard - msgparse: Unable to parse block from [{0}] - currentdoc: Add Current Document Block - msgdelconfirm: - - Delete bookmark item {0}? - - ⚠️ Cannot be recovered after deletion! Are you sure you want to delete? item: - copyref: Copy as Reference + bottom: Move to Bottom + checkerritem: Check detail + copyitem: Copy to copylink: Copy as Link + copyref: Copy as Reference + del: Delete Bookmark + move: Move msgcopy: Copy Successful style: Style - transfer: Move to - move: Move top: Move to Top - bottom: Move to Bottom - del: Delete Bookmark - checkerritem: Check detail - copyitem: Copy to + transfer: Move to +itemErr: + closed: The notebook "{0}" is already closed + deleted: 'Content block not found, it may have been deleted! Old block content: + [{0}]' +msg: + groupNameEmpty: The name of the bookmark group cannot be empty! + itemHasInGroup: The item is already in the target group + itemNotFoundInGroup: The item was not found in the source group + ruleFailed: 'Failed to correctly parse the configuration rule: {0}' + ruleInvalid: 'The format of the group rule input is incorrect: {0}' newgroup: - name: - - Bookmark Group Name - - Please enter the name of the new bookmark group - type: - - Bookmark Group Type - - Select Bookmark Group Type - rtype: - - Rule Type - - Select Dynamic Rule Type - rinput: Rule Value + choosetemplate: Template desc: - sql: Please enter the SQL query below + attr: Please enter the attribute to search for; You can fill in [attribute name], + or [attribute name][= or like][attribute value] backlinks: Please enter the block ID of the backlink - attr: Please enter the attribute to search for; You can fill in [attribute name], or [attribute name][= or like][attribute value] - choosetemplate: Template + sql: Please enter the SQL query below + icondesc: Select the icon for the group; if not set, the plugin will assign a default + icon based on the group type. + icontitle: Group Icon + name: + - Bookmark Group Name + - Please enter the name of the new bookmark group postprocess: - ariaLabel: 'First child of container: Display the full container block when the referenced block is the first child of a list item or quote block.
Display as document block: Display the document containing the referenced block, rather than the referenced block itself.
See README for details' + ariaLabel: 'First child of container: Display the full container block + when the referenced block is the first child of a list item or quote block.
Display + as document block: Display the document containing the referenced block, + rather than the referenced block itself.
See README for details' + b2doc: Display as document block + fb2p: First child of container name: Post-process omit: No Process - fb2p: First child of container - b2doc: Display as document block - icontitle: Group Icon - # icondesc: 可以手动选择分组的图标;如果不设置,插件会根据分组类型分配默认的图标。 - icondesc: Select the icon for the group; if not set, the plugin will assign a default icon based on the group type. - -bookmarktype: - normal: 'Normal Bookmark' - dynamic: 'Dynamic Bookmark' - composed: 'Composite Bookmark' + rinput: Rule Value + rtype: + - Rule Type + - Select Dynamic Rule Type + type: + - Bookmark Group Type + - Select Bookmark Group Type ruletype: - sql: SQL - backlinks: Backlinks attr: Block Attributes + backlinks: Backlinks js: Javascript + sql: SQL +selecticon: + h3: Please select an icon + reset: Reset + title: Change Group Icon +setting: + ariaLabel: + description: Display full title of the bookmark item in a hover box when the mouse + hovers + title: Display Hover Box + autoRefreshOnExpand: + description: Automatically refreshes the items within a dynamic group when it + is expanded from a collapsed state. + title: Auto Refresh on Expand + grouplist: + description: Set up bookmark groups + title: Bookmark Groups + hideClosed: + description: When enabled, hide items from closed notebooks + title: Hide Closed Items + hideDeleted: + description: When enabled, hide items that have been deleted + title: Hide Invalid Items + replaceDefault: + description: 'If enabled, the default bookmarks will be blocked, and bookmark + shortcut keys (default is Alt+3) will be bound;
Takes effect after restart; + Notice: not appliable in mobile devices!' + title: Replace Built-in Bookmarks + viewMode: + description: Select the style of the bookmark bar display + title: Display Mode + zoomInWhenClick: + description: When clicking on the bookmark item and opening the content block + in the tab, automatically enter the focus mode + title: Enter focus mode after jumping to the block template: + attr: + dailynote: Daily Note sql: random: Random Roam - updated: Recently Updated thisday: On this day todo: Unsolved Task - attr: - dailynote: Daily Note -selecticon: - title: Change Group Icon - h3: Please select an icon - reset: Reset + updated: Recently Updated +viewMode: + bookmark: Bookmark Style + card: Card Style diff --git a/public/i18n/zh_CN.yaml b/public/i18n/zh_CN.yaml index 3b438e8..4799265 100644 --- a/public/i18n/zh_CN.yaml +++ b/public/i18n/zh_CN.yaml @@ -30,6 +30,9 @@ setting: ariaLabel: title: 显示悬浮框 description: 鼠标悬停在书签项目上时,在悬浮框中显示书签项目的完整标题 + zoomInWhenClick: + title: 跳转到块后进入聚焦 + description: 当点击书签项目,在标签页中打开内容块的时候,自动进入聚焦模式 viewMode: bookmark: 书签样式 card: 卡片样式 diff --git a/src/components/group.tsx b/src/components/group.tsx index 4cfdf92..cf527f8 100644 --- a/src/components/group.tsx +++ b/src/components/group.tsx @@ -105,7 +105,9 @@ const Group: Component = (props) => { if (configs['autoRefreshOnExpand'] && isDynamicGroup() && expand === false) { // console.log('auto refresh', props.group.name); - model.updateDynamicGroup(props.group); + model.updateDynamicGroup(props.group).then(() => { + model.updateGroupStaticItemsDebounced(props.group); + }); } setGroups((g) => g.id === props.group.id, 'expand', expand); @@ -167,16 +169,17 @@ const Group: Component = (props) => { const showGroupContextMenu = (e: MouseEvent) => { e.stopPropagation(); const menu = new Menu(); - if (props.group.type === 'dynamic') { - menu.addItem({ - label: i18n_.refresh, - icon: 'iconRefresh', - click: () => { - model.updateDynamicGroup(props.group); + menu.addItem({ + label: i18n_.refresh, + icon: 'iconRefresh', + click: async () => { + if (props.group.type === 'dynamic') { + await model.updateDynamicGroup(props.group); } - }); - menu.addSeparator(); - } + await model.updateGroupStaticItemsDebounced(props.group); + } + }); + menu.addSeparator(); menu.addItem({ label: i18n_.copyref, icon: "iconRef", @@ -498,7 +501,9 @@ const Group: Component = (props) => { class="b3-list-item__action" onClick={(e) => { e.stopPropagation(); - model.updateDynamicGroup(props.group); + model.updateDynamicGroup(props.group).then(() => { + model.updateGroupStaticItemsDebounced(props.group); + }); }} > diff --git a/src/components/item.tsx b/src/components/item.tsx index c33d903..9a8115e 100644 --- a/src/components/item.tsx +++ b/src/components/item.tsx @@ -3,14 +3,16 @@ import { render } from "solid-js/web"; import { Menu, openTab, showMessage, openMobileFileById } from "siyuan"; import { buildItemDetail } from "../libs/dom"; -import { itemInfo, setGroups, groupMap, configs } from "../model"; +import { itemInfo, setGroups, groupMap, configs, configRef } from "../model"; import { BookmarkContext, itemMoving, setItemMoving } from "./context"; import { i18n, renderI18n } from "@/utils/i18n"; -import { simpleDialog } from "@/libs/dialog"; +// import { simpleDialog } from "@/libs/dialog"; +import { simpleDialog } from "@frostime/siyuan-plugin-kits"; import Typography from "@/libs/components/typography"; import { getNotebook, isMobile } from "@/utils"; +import { getBlockByID } from "@/api"; interface IProps { group: TBookmarkGroupId; @@ -267,11 +269,20 @@ const Item: Component = (props) => { if (isMobile()) { openMobileFileById(plugin.app, item().id); } else { + let zoomIn = configRef().zoomInWhenClick; + // if (item().type === 'd') { + // zoomIn = false; + // } else { + // let block = await getBlockByID(item().id); + // let root_id = block.root_id; + // let protyle = document.querySelector(`.protyle-title[data-node-id="${root_id}"]`); + // if (protyle) zoomIn = false; //如果文档已经打开了,就直接调整不聚焦 + // } openTab({ app: plugin.app, doc: { id: item().id, - zoomIn: item().type === 'd' ? false : true, + zoomIn: zoomIn, }, }); } diff --git a/src/components/select-icon.tsx b/src/components/select-icon.tsx index 4ee999a..ae37a6e 100644 --- a/src/components/select-icon.tsx +++ b/src/components/select-icon.tsx @@ -1,6 +1,6 @@ import { createSignal, For, Show } from "solid-js"; import Icon from "./icon"; -import { inject } from "@/libs/inject"; +// import { inject } from "@/libs/inject"; import { solidDialog } from "@/libs/dialog"; import { i18n } from "@/utils/i18n"; @@ -20,7 +20,7 @@ const SelectIcons = (props: IProps) => { const [activeTab, setActiveTab] = createSignal<'symbols' | 'emojis'>('symbols'); const symbols = Array.from(document.querySelectorAll('symbol')); - const i18n = inject('i18n'); + // const i18n = inject('i18n'); const ALLOWED_EMOJI_GROUP = ['activity', 'travel', 'people', 'nature', 'food', 'symbols', 'object', 'flags']; let EmojisGroups = (window.siyuan.emojis as ImojiGroup[]).filter(emoji => ALLOWED_EMOJI_GROUP.includes(emoji.id)); diff --git a/src/components/setting/group-list.tsx b/src/components/setting/group-list.tsx index 7c8c617..868981c 100644 --- a/src/components/setting/group-list.tsx +++ b/src/components/setting/group-list.tsx @@ -56,12 +56,13 @@ const App = () => { return (
diff --git a/src/components/setting/index.tsx b/src/components/setting/index.tsx index 6720c1d..ff7b3e0 100644 --- a/src/components/setting/index.tsx +++ b/src/components/setting/index.tsx @@ -1,108 +1,162 @@ -import { FormWrap as SettingItemWrap, FormInput as InputItem } from '@/libs/components/Form'; +import Form from "@/libs/components/Form"; import GroupList from './group-list'; import { configs, setConfigs } from "@/model"; import { i18n } from "@/utils/i18n"; +import { children, Component, For, JSXElement } from "solid-js"; +import { useSignalRef } from "@frostime/solid-signal-ref"; +import { Dynamic } from "solid-js/web"; + +interface SettingPanelProps { + group: string; + settingItems?: ISettingItem[]; + // display: boolean; + onChanged?: (e: { group: string, key: string, value: any }) => void; + children?: JSXElement +} + +const SettingPanel: Component = (props) => { + // const fn__none = createMemo(() => props.display === true ? "" : "fn__none"); + const useChildren = children(() => props.children); + + return ( +
+ + {(item) => ( + + props.onChanged({ group: props.group, key: item.key, value: v })} + /> + + )} + + {useChildren()} +
+ ); +}; const App = () => { const i18n_ = i18n.setting; - return ( -
- - { - setConfigs('replaceDefault', v); - }} - /> - - - { - setConfigs('viewMode', v); - }} - options={{ - 'bookmark': i18n.viewMode.bookmark, - 'card': i18n.viewMode.card - }} - /> - - - { - setConfigs('autoRefreshOnExpand', v); - }} - /> - - - { - setConfigs('hideClosed', v); - }} - /> - - - { - setConfigs('hideDeleted', v); - }} - /> - - - { - setConfigs('ariaLabel', v); - }} - /> - - { + const settingItems: ISettingItem[] = [ + { + key: 'replaceDefault', + type: 'checkbox', + title: i18n_.replaceDefault.title, + description: i18n_.replaceDefault.description, + value: configs['replaceDefault'] + }, + { + key: 'viewMode', + type: 'select', + title: i18n_.viewMode.title, + description: i18n_.viewMode.description, + value: configs['viewMode'], + options: { + 'bookmark': i18n.viewMode.bookmark, + 'card': i18n.viewMode.card + } + }, + { + key: 'autoRefreshOnExpand', + type: 'checkbox', + title: i18n_.autoRefreshOnExpand.title, + description: i18n_.autoRefreshOnExpand.title, + value: configs['autoRefreshOnExpand'] + }, + { + key: 'hideClosed', + type: 'checkbox', + title: i18n_.hideClosed.title, + description: i18n_.hideClosed.description, + value: configs['hideClosed'] + }, + { + key: 'hideDeleted', + type: 'checkbox', + title: i18n_.hideDeleted.title, + description: i18n_.hideDeleted.description, + value: configs['hideDeleted'] + }, + { + key: 'ariaLabel', + type: 'checkbox', + title: i18n_.ariaLabel.title, + description: i18n_.ariaLabel.description, + value: configs['ariaLabel'] + }, + { + key: 'zoomInWhenClick', + type: 'checkbox', + title: i18n_.zoomInWhenClick.title, + description: i18n_.zoomInWhenClick.description, + value: configs['zoomInWhenClick'] + } + ]; + + return ( + { + //@ts-ignore + setConfigs(key, value); + }} + /> + ); + } + + const PanelGroupList = () => { + return ( + - - + + + ); + } + + const groups = { + 'Basic': PanelBasic, + 'GroupList': PanelGroupList + } + + + return ( +
+
    + + {(group, i) => ( +
  • focused(i())} + onKeyDown={() => { }} + style={{ 'padding-left': "1rem" }} + > + {group} +
  • + )} +
    +
+
+ +
- ) -} + ); +}; export default App; diff --git a/src/index.ts b/src/index.ts index 34eed23..52326bc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,7 +3,7 @@ * @Author : frostime * @Date : 2024-06-12 19:48:53 * @FilePath : /src/index.ts - * @LastEditTime : 2024-12-15 20:04:24 + * @LastEditTime : 2024-12-29 20:35:01 * @Description : */ import { @@ -12,7 +12,7 @@ import { import { render } from "solid-js/web"; -import { simpleDialog } from "./libs/dialog"; +import { solidDialog } from "./libs/dialog"; import { getModel, rmModel, type BookmarkDataModel } from "./model"; import { configs } from "./model"; @@ -26,9 +26,11 @@ import { setI18n } from "@/utils/i18n"; import "@/index.scss"; import { isMobile } from "./utils"; -import { provide, purge } from "./libs/inject"; + import { loadSdk, unloadSdk } from "./sdk"; +import { registerPlugin } from "@frostime/siyuan-plugin-kits"; + let model: BookmarkDataModel; const initBookmark = async (ele: HTMLElement, plugin: PluginBookmarkPlus) => { @@ -67,9 +69,9 @@ export default class PluginBookmarkPlus extends Plugin { } async onload() { + //@ts-ignore + registerPlugin(this); setI18n(this.i18n as I18n); - provide('i18n', this.i18n as I18n); - provide('plugin', this); let svgs = Object.values(Svg); this.addIcons(svgs.join('')); @@ -127,19 +129,15 @@ export default class PluginBookmarkPlus extends Plugin { onunload(): void { unloadSdk(); - purge(); destroyBookmark(); bookmarkKeymap.custom = bookmarkKeymap.default; // this.commands = this.commands.filter((command) => command.langKey !== 'F-Misc::Bookmark'); } openSetting(): void { - let container = document.createElement("div") as HTMLDivElement; - container.classList.add("fn__flex-1", "fn__flex"); - render(() => Setting(), container); let size = { - width: '700px', - height: '700px' + width: '900px', + height: '600px' } if (isMobile()) { size = { @@ -147,14 +145,11 @@ export default class PluginBookmarkPlus extends Plugin { height: '90%' } } - simpleDialog({ + solidDialog({ title: window.siyuan.languages.config, - ele: container, - callback: () => { - model.save(); - }, + loader: () => Setting(), ...size - }) + }); } } diff --git a/src/libs/dialog.ts b/src/libs/dialog.ts index c023ba5..8329d55 100644 --- a/src/libs/dialog.ts +++ b/src/libs/dialog.ts @@ -3,28 +3,14 @@ * @Author : frostime * @Date : 2024-03-23 21:37:33 * @FilePath : /src/libs/dialog.ts - * @LastEditTime : 2024-10-03 16:44:41 + * @LastEditTime : 2024-12-29 20:30:34 * @Description : 对话框相关工具 */ import { Dialog } from "siyuan"; import { JSXElement } from "solid-js"; import { render } from "solid-js/web"; -export const simpleDialog = (args: { - title: string, ele: HTMLElement | DocumentFragment, - width?: string, height?: string, - callback?: () => void; -}) => { - const dialog = new Dialog({ - title: args.title, - content: `
`, - width: args.width, - height: args.height, - destroyCallback: args.callback - }); - dialog.element.querySelector(".dialog-content").appendChild(args.ele); - return dialog; -} +import { simpleDialog } from "@frostime/siyuan-plugin-kits"; export const solidDialog = (args: { title: string, loader: () => JSXElement, @@ -34,13 +20,13 @@ export const solidDialog = (args: { let container = document.createElement('div') container.style.display = 'contents'; let disposer = render(args.loader, container); - const dialog = simpleDialog({...args, ele: container, callback: () => { + const {dialog, close} = simpleDialog({...args, ele: container, callback: () => { disposer(); if (args.callback) args.callback();; }}); return { dialog, - close: () => dialog.destroy(), + close, container } } diff --git a/src/libs/inject.ts b/src/libs/inject.ts deleted file mode 100644 index 6867186..0000000 --- a/src/libs/inject.ts +++ /dev/null @@ -1,19 +0,0 @@ -const Storage = {}; - -export const provide = (key: string, value: T) => { - Storage[key] = value; -} - -export const inject = (key: string): T => { - return Storage[key]; -} - -export const purge = (key?: string) => { - if (key) { - delete Storage[key]; - } else { - for (const key in Storage) { - delete Storage[key]; - } - } -} diff --git a/src/libs/data.ts b/src/model/data.ts similarity index 87% rename from src/libs/data.ts rename to src/model/data.ts index 823c2db..a011c11 100644 --- a/src/libs/data.ts +++ b/src/model/data.ts @@ -2,8 +2,8 @@ * Copyright (c) 2024 by frostime. All Rights Reserved. * @Author : frostime * @Date : 2024-06-13 14:09:40 - * @FilePath : /src/func/bookmarks/libs/data.ts - * @LastEditTime : 2024-06-20 15:29:04 + * @FilePath : /src/model/data.ts + * @LastEditTime : 2024-12-29 20:58:27 * @Description : */ import { sql, request } from "@/api"; @@ -20,8 +20,9 @@ export { * @param ids * @returns */ -const getBlocks = async (...ids: BlockId[]) => { - const fmt = `select * from blocks where id in (${ids.map((b) => `'${b}'`).join(',')})`; +const getBlocks = async (ids: BlockId[], limit = 64) => { + let fmt = `select * from blocks where id in (${ids.map((b) => `'${b}'`).join(',')})`; + fmt += `limit ${limit}`; let blocks = await sql(fmt); let results: { [key: BlockId]: Block | null } = {}; for (let id of ids) { diff --git a/src/model/index.ts b/src/model/index.ts index 6b8a174..786c955 100644 --- a/src/model/index.ts +++ b/src/model/index.ts @@ -2,12 +2,12 @@ import { unwrap } from "solid-js/store"; import type PluginBookmarkPlus from "@/index"; -import { getBlocks, getDocInfos } from "../libs/data"; +import { getBlocks, getDocInfos } from "./data"; import { rmItem, insertItem, moveItem } from "../libs/op"; import { showMessage } from "siyuan"; import { batch } from "solid-js"; -import { debounce } from '@/utils'; +import { debounce, PromiseLimitPool } from "@frostime/siyuan-plugin-kits"; import { i18n, renderI18n } from "@/utils/i18n"; @@ -98,7 +98,8 @@ export class BookmarkDataModel { await this.plugin.saveData(StorageFileItemSnapshot, itemInfo); } - save = debounce(this.saveCore.bind(this), 1000); + // save = debounce(this.saveCore.bind(this), 1000); + save = debounce(this.saveCore.bind(this) as typeof this.saveCore, 1000); setGroups(gid: TBookmarkGroupId, key: keyof IBookmarkGroup, value: IBookmarkGroup[keyof IBookmarkGroup]) { setGroups((gs) => gs.id === gid, key, value); @@ -131,12 +132,21 @@ export class BookmarkDataModel { toUpdated.push(this.updateDynamicGroup(group)); } }); + // 由于是 async 的,所以很遗憾没法使用 batch 更新 await Promise.all(toUpdated); - await this.updateStaticItems(); + // await this.updateStaticItems(); + let groupsToUpdate = groups.filter(g => g.hidden !== true); + let allIdsSet = new Set(); + groupsToUpdate.forEach(group => { + group.items.forEach(item => { + allIdsSet.add(item.id); + }); + }); + await this.updateStaticItems(allIdsSet); } /** - * 查询动态规则中的块 + * 查询动态规则中的块, 只查询,不更改已有的 item 的信息 * @param group * @returns */ @@ -219,20 +229,39 @@ export class BookmarkDataModel { } - async updateStaticItems() { - console.debug('Update all Bookmark items'); - //1. 获取 block 的最新内容 - let allIds = []; - //一般调用 updateItems 之前会已经调用过 update dynamic group; 如果再次更新就有些冗余了 - //不这么做似乎会造成 item 404 的 bug - const staticGroups = groups.filter(g => g.type !== 'dynamic' && g.hidden !== true); - staticGroups.forEach(g => { - allIds = allIds.concat(g.items.map(it => it.id)); - }) - let allIdsSet = new Set(allIds); - allIds = Array.from(allIdsSet); + async updateGroupStaticItems(group: IBookmarkGroup) { + let allIdsSet = new Set(); + group.items.forEach(item => { + allIdsSet.add(item.id); + }); + await this.updateStaticItems(allIdsSet); + } - let blocks = await getBlocks(...allIds); + // updateGroupStaticItemsDebounced = debounce(this.updateGroupStaticItems.bind(this), 1000); + updateGroupStaticItemsDebounced = debounce( + this.updateGroupStaticItems.bind(this) as typeof this.updateGroupStaticItems, + 1000 + ); + + private async updateStaticItems(allIdsSet: Set) { + let allIds = Array.from(allIdsSet); + let blocks: Awaited> = {}; + const PAGE_SIZE = 128; + if (allIds.length <= PAGE_SIZE) { + let result = await getBlocks(allIds, PAGE_SIZE); + blocks = result; + } else { + const pool = new PromiseLimitPool(PAGE_SIZE); + for (let i = 0; i < allIds.length; i += PAGE_SIZE) { + let ids = allIds.slice(i, i + PAGE_SIZE); + pool.add(async () => { + let result = await getBlocks(ids, PAGE_SIZE); + blocks = { ...blocks, ...result }; + return result; + }); + } + await pool.awaitAll(); + } //2. 更新文档块的 logo let docsItem: DocumentId[] = []; @@ -300,7 +329,7 @@ export class BookmarkDataModel { } // ItemInfoStore[id].set({ ...item }); }); - console.debug('更新所有 Bookmark items 完成'); + // console.debug('更新所有 Bookmark items 完成'); //batch 更新 batch(() => { @@ -402,6 +431,7 @@ export class BookmarkDataModel { }) setItemInfo(item.id, 'ref', (ref) => ref + 1); this.save(); + // this.updateStaticItems(new Set([item.id])); return true; } else { return false; diff --git a/src/model/stores.ts b/src/model/stores.ts index 29967ea..c3e86a9 100644 --- a/src/model/stores.ts +++ b/src/model/stores.ts @@ -3,12 +3,13 @@ * @Author : frostime * @Date : 2024-07-07 14:44:03 * @FilePath : /src/model/stores.ts - * @LastEditTime : 2024-07-15 18:43:34 + * @LastEditTime : 2024-12-28 01:22:56 * @Description : */ import { createStore } from "solid-js/store"; import { createMemo } from "solid-js"; +import { wrapStoreRef } from "@frostime/solid-signal-ref"; export const [itemInfo, setItemInfo] = createStore<{ [key: BlockId]: IBookmarkItemInfo }>({}); @@ -24,6 +25,7 @@ interface IConfig { replaceDefault: boolean; autoRefreshOnExpand: boolean; ariaLabel: boolean; + zoomInWhenClick: boolean; } export const [configs, setConfigs] = createStore({ @@ -33,4 +35,6 @@ export const [configs, setConfigs] = createStore({ replaceDefault: true, autoRefreshOnExpand: false, ariaLabel: false, + zoomInWhenClick: true }); +export const configRef = wrapStoreRef(configs, setConfigs); diff --git a/src/sdk.ts b/src/sdk.ts index 610422f..a414246 100644 --- a/src/sdk.ts +++ b/src/sdk.ts @@ -1,3 +1,11 @@ +/* + * Copyright (c) 2024 by frostime. All Rights Reserved. + * @Author : frostime + * @Date : 2024-12-15 23:01:47 + * @FilePath : /src/sdk.ts + * @LastEditTime : 2024-12-28 12:48:12 + * @Description : + */ // import type PluginBookmarkPlus from "."; import { groups, itemInfo } from "./model/stores"; // import { getModel } from "./model"; @@ -15,6 +23,9 @@ export const loadSdk = () => { type: group.type, })); }, + findGroup: (name: string) => { + return groups.find(group => group.name === name); + }, listItems: (groupId: string) => { if (!groupId || typeof groupId !== 'string') { return []; diff --git a/src/types/i18n.d.ts b/src/types/i18n.d.ts index c92ba1f..293faf5 100644 --- a/src/types/i18n.d.ts +++ b/src/types/i18n.d.ts @@ -39,6 +39,10 @@ interface I18n { title: string; description: string; }; + zoomInWhenClick: { + title: string; + description: string; + }; }; viewMode: { bookmark: string; @@ -125,6 +129,7 @@ interface I18n { sql: string; backlinks: string; attr: string; + js: string; }; template: { sql: {