dsh-tiddlywiki 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -660,11 +660,11 @@ function timestampTitle(date = /* @__PURE__ */ new Date()) {
660
660
  * `isDraft && editTemplate`), and return the draft title so the client can
661
661
  * navigate the panel iframe to `#<draftTitle>`.
662
662
  */
663
- async function openInTwEditor(client, title, text, tag) {
663
+ async function openInTwEditor(client, title, text, tags) {
664
664
  if (text.trim().length > 0) await client.put({
665
665
  title,
666
666
  text,
667
- tags: [tag]
667
+ tags
668
668
  });
669
669
  let draftText = text;
670
670
  if (draftText.trim().length === 0) draftText = (await client.get(title))?.text ?? "";
@@ -689,6 +689,16 @@ async function openInTwEditor(client, title, text, tag) {
689
689
  draftTitle
690
690
  };
691
691
  }
692
+ /** Resolve note tags from the request body: `tags` array wins, then the
693
+ * legacy single `tag` string, then the configured default tag. */
694
+ function resolveTags(body, defaultTag) {
695
+ if (Array.isArray(body.tags)) {
696
+ const tags = body.tags.filter((t) => typeof t === "string" && t.trim().length > 0).map((t) => t.trim());
697
+ if (tags.length > 0) return tags;
698
+ }
699
+ if (typeof body.tag === "string" && body.tag.trim().length > 0) return body.tag.trim().split(/\s+/).filter(Boolean);
700
+ return [defaultTag];
701
+ }
692
702
  function registerRoutes(ctx, deps) {
693
703
  const handleStatus = async (_req, res) => {
694
704
  const view = deps.server.status();
@@ -702,7 +712,8 @@ function registerRoutes(ctx, deps) {
702
712
  ok: true,
703
713
  ...view,
704
714
  git: gitSummary,
705
- note: { tag: deps.noteDefaults().tag }
715
+ note: { tag: deps.noteDefaults().tag },
716
+ ui: deps.uiDefaults()
706
717
  });
707
718
  };
708
719
  const handleNote = async (req, res) => {
@@ -725,17 +736,18 @@ function registerRoutes(ctx, deps) {
725
736
  return;
726
737
  }
727
738
  const title = typeof body.title === "string" && body.title.trim().length > 0 ? body.title.trim() : timestampTitle();
728
- const tag = typeof body.tag === "string" && body.tag.trim().length > 0 ? body.tag.trim() : deps.noteDefaults().tag;
739
+ const tags = resolveTags(body, deps.noteDefaults().tag);
729
740
  await client.put({
730
741
  title,
731
742
  text,
732
- tags: [tag]
743
+ tags
733
744
  });
734
745
  deps.autoCommit();
735
746
  json$1(res, {
736
747
  ok: true,
737
748
  title,
738
- tag,
749
+ tag: tags.join(" "),
750
+ tags,
739
751
  text
740
752
  });
741
753
  } catch (err) {
@@ -757,8 +769,8 @@ function registerRoutes(ctx, deps) {
757
769
  return;
758
770
  }
759
771
  const title = typeof body.title === "string" && body.title.trim().length > 0 ? body.title.trim() : timestampTitle();
760
- const tag = typeof body.tag === "string" && body.tag.trim().length > 0 ? body.tag.trim() : deps.noteDefaults().tag;
761
- const result = await openInTwEditor(client, title, typeof body.text === "string" ? body.text : "", tag);
772
+ const tags = resolveTags(body, deps.noteDefaults().tag);
773
+ const result = await openInTwEditor(client, title, typeof body.text === "string" ? body.text : "", tags);
762
774
  deps.autoCommit();
763
775
  json$1(res, {
764
776
  ok: true,
@@ -772,6 +784,31 @@ function registerRoutes(ctx, deps) {
772
784
  }, 500);
773
785
  }
774
786
  };
787
+ /** Distinct non-system tags for the quick-note tag autocomplete. */
788
+ const handleTags = async (_req, res) => {
789
+ const client = deps.getClient();
790
+ if (client === void 0) {
791
+ json$1(res, {
792
+ ok: false,
793
+ error: "wiki service is not running"
794
+ }, 503);
795
+ return;
796
+ }
797
+ try {
798
+ const items = await client.list(void 0, false);
799
+ const tags = /* @__PURE__ */ new Set();
800
+ for (const item of items) for (const tag of item.tags ?? []) if (tag.length > 0 && !tag.startsWith("$:/")) tags.add(tag);
801
+ json$1(res, {
802
+ ok: true,
803
+ tags: [...tags].sort((a, b) => a.localeCompare(b, "zh"))
804
+ });
805
+ } catch (err) {
806
+ json$1(res, {
807
+ ok: false,
808
+ error: err instanceof Error ? err.message : String(err)
809
+ }, 500);
810
+ }
811
+ };
775
812
  const handleRestart = async (_req, res) => {
776
813
  try {
777
814
  await deps.server.restart();
@@ -845,6 +882,13 @@ function registerRoutes(ctx, deps) {
845
882
  handleEdit(req, res);
846
883
  }
847
884
  }),
885
+ ctx.webServer.register({
886
+ kind: "exact",
887
+ path: `${ROUTE_PREFIX}/tags`,
888
+ handler: (req, res) => {
889
+ handleTags(req, res);
890
+ }
891
+ }),
848
892
  ctx.webServer.register({
849
893
  kind: "exact",
850
894
  path: `${ROUTE_PREFIX}/restart`,
@@ -1303,6 +1347,8 @@ function registerAdminRoutes(ctx, deps) {
1303
1347
  const DOC_NOTE_TITLE = "dsh-tiddlywiki 插件说明";
1304
1348
  /** Tag that makes the note easy to find via `tiddlywiki_search tag=docs`. */
1305
1349
  const DOC_NOTE_TAG = "docs";
1350
+ /** One-time marker: its presence means "the note was offered once — hands off". */
1351
+ const SEED_MARKER_TITLE = "$:/plugins/dsh-tiddlywiki/seed-doc-note";
1306
1352
  /** The note body, TiddlyWiki wiki-text. */
1307
1353
  const DOC_NOTE_TEXT = `! dsh-tiddlywiki 插件说明
1308
1354
 
@@ -1312,9 +1358,9 @@ const DOC_NOTE_TEXT = `! dsh-tiddlywiki 插件说明
1312
1358
 
1313
1359
  * **5 个 agent 工具**:\`tiddlywiki_search\`(检索)/ \`tiddlywiki_get\`(读)/ \`tiddlywiki_put\`(写)/ \`tiddlywiki_delete\`(删)/ \`tiddlywiki_git_sync\`(git 同步)。
1314
1360
  * **TW 编辑器面板**:侧边栏「TiddlyWiki」按钮 → 在界面中央打开完整版 TW 编辑器。
1315
- * **快速笔记**:右下角悬浮「📝 快速笔记」写随手记,\`Ctrl+Enter\` 保存;点「✏️ 在 TW 中编辑」会弹出独立小窗用 TW 原生编辑器编辑。
1361
+ * **快速笔记**:右下角悬浮「📝 快速笔记」写随手记(可多选/自动补全 tag),\`Ctrl+Enter\` 保存;点「✏️ 在 TW 中编辑」会弹出独立小窗用 TW 原生编辑器编辑。
1316
1362
  * **git 同步**:写入自动防抖 commit(默认 60 秒);手动 \`tiddlywiki_git_sync action=sync\` 做 pull → commit → push。
1317
- * **设置页**:DSH 设置 → 「TiddlyWiki 知识库」管理插件/主题/语言与运行配置。
1363
+ * **设置页**:DSH 设置 → 「TiddlyWiki 知识库」管理插件/主题/语言与运行配置(含「快速笔记」显示开关、侧边栏移左 CSS 补丁开关)。
1318
1364
 
1319
1365
  !! 知识库纪律(三条)
1320
1366
 
@@ -1329,19 +1375,67 @@ const DOC_NOTE_TEXT = `! dsh-tiddlywiki 插件说明
1329
1375
 
1330
1376
  !! 说明
1331
1377
 
1332
- * 本笔记由插件在首次启动时自动写入 wiki(幂等:不存在才写)。删除后重启 dsh web 会重建;手动编辑过的内容不会被覆盖。
1378
+ * 本笔记由插件在**首次启动**时自动写入(一次性:只写一次)。删除后重启 dsh web **不会自动恢复**——它从此归你所有。
1379
+ * 「\`侧边移到左侧.css\`」是插件默认安装的界面补丁(把 TW 右侧栏移到左边);可在设置页关闭「侧边栏移左 CSS」开关来停用补丁还原。
1333
1380
  * 更多细节见插件仓库 README。`;
1334
1381
  /**
1335
- * Seed the doc note when it is absent. Returns whether a note was written.
1336
- * Never throws (missing wiki or note already present → no-op / false).
1382
+ * Seed the doc note exactly once per wiki. A marker tiddler records that the
1383
+ * note has been offered; from then on the note is user-owned and is never
1384
+ * re-created (deleting it survives restarts). Returns whether a note was
1385
+ * written this call. Never throws.
1337
1386
  */
1338
1387
  async function seedDocNote(client) {
1339
- if (await client.get("dsh-tiddlywiki 插件说明").catch(() => void 0) !== void 0) return false;
1388
+ if (await client.get("$:/plugins/dsh-tiddlywiki/seed-doc-note").catch(() => void 0) !== void 0) return false;
1389
+ const existing = await client.get(DOC_NOTE_TITLE).catch(() => void 0);
1390
+ let wrote = false;
1391
+ if (existing === void 0) {
1392
+ await client.put({
1393
+ title: DOC_NOTE_TITLE,
1394
+ text: DOC_NOTE_TEXT,
1395
+ type: "text/vnd.tiddlywiki",
1396
+ tags: [DOC_NOTE_TAG]
1397
+ });
1398
+ wrote = true;
1399
+ }
1340
1400
  await client.put({
1341
- title: DOC_NOTE_TITLE,
1342
- text: DOC_NOTE_TEXT,
1343
- type: "text/vnd.tiddlywiki",
1344
- tags: [DOC_NOTE_TAG]
1401
+ title: SEED_MARKER_TITLE,
1402
+ text: "seeded-once",
1403
+ type: "text/plain",
1404
+ tags: []
1405
+ }).catch(() => void 0);
1406
+ return wrote;
1407
+ }
1408
+ /** Sidebar-left CSS tiddler: moves TW's sidebar to the LEFT (mirror of the
1409
+ * `centralised` default), so dsh-better-sidebar's top-right toggle cluster
1410
+ * no longer sits over TW's own right sidebar. A normal tiddler (tag
1411
+ * `$:/tags/Stylesheet`, type text/css) — same pattern as hand-made
1412
+ * `编辑界面微调:小标题·大编辑器.css`, easy to find and delete. */
1413
+ const SIDEBAR_LEFT_CSS_TITLE = "侧边移到左侧.css";
1414
+ /** The stylesheet body. Hardcoded 960px breakpoint (vanilla default
1415
+ * sidebarbreakpoint) — plain text/css tiddlers don't process `{{...}}`. */
1416
+ const SIDEBAR_LEFT_CSS = `/* 由 dsh-tiddlywiki 插件默认安装:把 TW 右侧栏镜像到左侧(与 centralised 主题默认对称)。
1417
+ 删除后,只要设置页「侧边栏移左 CSS」补丁开关开着,插件会在下次启动时重建本 tiddler。 */
1418
+ @media (min-width: 960px) {
1419
+ html .tc-sidebar-scrollable {
1420
+ left: 0 !important;
1421
+ right: 50% !important;
1422
+ margin-left: 0 !important;
1423
+ margin-right: 343px !important;
1424
+ }
1425
+ }`;
1426
+ /**
1427
+ * Patch-repair the sidebar-left CSS while the option is enabled: write it
1428
+ * whenever it is missing (fresh installs and deleted-by-user both re-seed).
1429
+ * Returns whether it was written. Never throws.
1430
+ */
1431
+ async function seedSidebarLeftCss(client, enabled) {
1432
+ if (!enabled) return false;
1433
+ if (await client.get("侧边移到左侧.css").catch(() => void 0) !== void 0) return false;
1434
+ await client.put({
1435
+ title: SIDEBAR_LEFT_CSS_TITLE,
1436
+ text: SIDEBAR_LEFT_CSS,
1437
+ type: "text/css",
1438
+ tags: ["$:/tags/Stylesheet"]
1345
1439
  });
1346
1440
  return true;
1347
1441
  }
@@ -1935,6 +2029,10 @@ const DEFAULTS = {
1935
2029
  branch: "main"
1936
2030
  },
1937
2031
  note: { tag: "inbox" },
2032
+ ui: {
2033
+ showQuickNote: true,
2034
+ sidebarLeftCss: true
2035
+ },
1938
2036
  auth: {
1939
2037
  username: "",
1940
2038
  password: ""
@@ -2007,6 +2105,10 @@ function apply(ctx, rawConfig = {}) {
2007
2105
  ...DEFAULTS.note,
2008
2106
  ...rawConfig.note ?? {}
2009
2107
  },
2108
+ ui: {
2109
+ ...DEFAULTS.ui,
2110
+ ...rawConfig.ui ?? {}
2111
+ },
2010
2112
  auth: {
2011
2113
  ...DEFAULTS.auth,
2012
2114
  ...rawConfig.auth ?? {}
@@ -2016,13 +2118,18 @@ function apply(ctx, rawConfig = {}) {
2016
2118
  const git = new GitFace();
2017
2119
  const configStore = new ConfigStore({
2018
2120
  note: config.note,
2019
- git: config.git
2121
+ git: config.git,
2122
+ ui: config.ui
2020
2123
  });
2021
2124
  const eff = () => configStore.get();
2022
2125
  const effectiveNoteTag = () => {
2023
2126
  const tag = eff().note?.tag;
2024
2127
  return typeof tag === "string" && tag.trim().length > 0 ? tag : config.note.tag;
2025
2128
  };
2129
+ const effectiveUi = () => ({
2130
+ showQuickNote: eff().ui?.showQuickNote !== false,
2131
+ sidebarLeftCss: eff().ui?.sidebarLeftCss !== false
2132
+ });
2026
2133
  const disposers = [];
2027
2134
  const disposeAll = () => {
2028
2135
  for (const dispose of disposers.splice(0)) dispose();
@@ -2096,9 +2203,12 @@ function apply(ctx, rawConfig = {}) {
2096
2203
  await configStore.load(client());
2097
2204
  try {
2098
2205
  const seedClient = client();
2099
- if (seedClient !== void 0) await seedDocNote(seedClient);
2206
+ if (seedClient !== void 0) {
2207
+ await seedDocNote(seedClient);
2208
+ await seedSidebarLeftCss(seedClient, effectiveUi().sidebarLeftCss);
2209
+ }
2100
2210
  } catch (err) {
2101
- console.warn("[dsh-tiddlywiki] seeding doc note:", err);
2211
+ console.warn("[dsh-tiddlywiki] seeding doc note / css:", err);
2102
2212
  }
2103
2213
  const uiLang = eff().uiLanguage;
2104
2214
  if (typeof uiLang === "string" && uiLang.trim().length > 0) try {
@@ -2128,6 +2238,7 @@ function apply(ctx, rawConfig = {}) {
2128
2238
  git,
2129
2239
  autoCommit: () => committer?.touch(),
2130
2240
  noteDefaults: () => ({ tag: effectiveNoteTag() }),
2241
+ uiDefaults: () => effectiveUi(),
2131
2242
  getWikiPath: () => wikiPath
2132
2243
  });
2133
2244
  const disposeAdmin = registerAdminRoutes({ webServer: ws }, {
@@ -2148,6 +2259,6 @@ function apply(ctx, rawConfig = {}) {
2148
2259
  }, "dsh-tiddlywiki: host teardown");
2149
2260
  }
2150
2261
  //#endregion
2151
- export { AutoCommitter, ConfigStore, DOC_NOTE_TAG, DOC_NOTE_TEXT, DOC_NOTE_TITLE, GitFace, PATH_PREFIX, TiddlyWebClient, WikiServer, apply, bundledCatalog, deepMerge, defineTool, dshHomePath, ensureLanguage, inject, name, normalizeThemes, openInTwEditor, readWikiInfo, registerAdminRoutes, resolveTwRoot, seedDocNote, writeWikiInfo };
2262
+ export { AutoCommitter, ConfigStore, DOC_NOTE_TAG, DOC_NOTE_TEXT, DOC_NOTE_TITLE, GitFace, PATH_PREFIX, SIDEBAR_LEFT_CSS, SIDEBAR_LEFT_CSS_TITLE, TiddlyWebClient, WikiServer, apply, bundledCatalog, deepMerge, defineTool, dshHomePath, ensureLanguage, inject, name, normalizeThemes, openInTwEditor, readWikiInfo, registerAdminRoutes, resolveTwRoot, seedDocNote, seedSidebarLeftCss, writeWikiInfo };
2152
2263
 
2153
2264
  //# sourceMappingURL=index.js.map