dsh-tiddlywiki 0.2.1 → 0.3.1
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/LICENSE +21 -21
- package/README.md +10 -4
- package/cordis.patch.yml +13 -13
- package/lib/client.bundle.js +542 -27
- package/lib/client.js +542 -27
- package/lib/index.js +232 -21
- package/lib/index.js.map +1 -1
- package/package.json +79 -79
- package/src/client/editor-popup.ts +121 -121
- package/src/client/index.ts +2 -0
- package/src/client/markdown.ts +137 -0
- package/src/client/note-widget.ts +515 -373
- package/src/client/settings-page.ts +31 -1
- package/src/client/sidebar-entry.ts +148 -148
- package/src/client/state.ts +39 -39
- package/src/client/styles.ts +107 -11
- package/src/client/sync-button.ts +188 -0
- package/src/client/toast.ts +22 -22
- package/src/host/admin.ts +408 -408
- package/src/host/config.ts +2 -0
- package/src/host/git.ts +13 -3
- package/src/host/routes.ts +167 -9
- package/src/host/tools.ts +28 -1
- package/src/host/tw-api.ts +157 -157
- package/src/host/wiki.ts +287 -287
- package/src/index.ts +12 -6
- package/src/sdk.ts +198 -198
package/lib/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { existsSync, watch } from "node:fs";
|
|
3
|
-
import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
|
|
4
|
-
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { access, mkdir, readFile, readdir, writeFile } from "node:fs/promises";
|
|
4
|
+
import { basename, dirname, extname, join, resolve } from "node:path";
|
|
5
5
|
import { execFile, spawn } from "node:child_process";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
7
|
import { createServer } from "node:net";
|
|
@@ -199,8 +199,16 @@ var GitFace = class {
|
|
|
199
199
|
...behind !== void 0 ? { behind } : {}
|
|
200
200
|
};
|
|
201
201
|
}
|
|
202
|
-
/** `git pull --rebase --autostash`; on conflict: abort + report files.
|
|
202
|
+
/** `git pull --rebase --autostash`; on conflict: abort + report files.
|
|
203
|
+
* On success, `changed: true` means HEAD actually moved (files came in /
|
|
204
|
+
* commits were replayed) — callers use it to decide whether a running TW
|
|
205
|
+
* child needs a restart to drop its stale in-memory snapshot. */
|
|
203
206
|
async pull(dir) {
|
|
207
|
+
const before = await this.exec(["rev-parse", "HEAD"], {
|
|
208
|
+
cwd: dir,
|
|
209
|
+
timeout: QUICK_TIMEOUT_MS
|
|
210
|
+
});
|
|
211
|
+
const beforeHead = before.ok ? before.stdout.trim() : "";
|
|
204
212
|
const r = await this.exec([
|
|
205
213
|
"pull",
|
|
206
214
|
"--rebase",
|
|
@@ -209,10 +217,19 @@ var GitFace = class {
|
|
|
209
217
|
cwd: dir,
|
|
210
218
|
timeout: HEAVY_TIMEOUT_MS
|
|
211
219
|
});
|
|
212
|
-
if (r.ok)
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
220
|
+
if (r.ok) {
|
|
221
|
+
const after = await this.exec(["rev-parse", "HEAD"], {
|
|
222
|
+
cwd: dir,
|
|
223
|
+
timeout: QUICK_TIMEOUT_MS
|
|
224
|
+
});
|
|
225
|
+
const afterHead = after.ok ? after.stdout.trim() : "";
|
|
226
|
+
const changed = beforeHead.length > 0 && beforeHead !== afterHead;
|
|
227
|
+
return {
|
|
228
|
+
ok: true,
|
|
229
|
+
message: r.stdout.trim() || "pull ok",
|
|
230
|
+
...changed ? { changed: true } : {}
|
|
231
|
+
};
|
|
232
|
+
}
|
|
216
233
|
const conflictFiles = await this.unmergedFiles(dir);
|
|
217
234
|
await this.exec(["rebase", "--abort"], {
|
|
218
235
|
cwd: dir,
|
|
@@ -621,6 +638,12 @@ const ROUTE_PREFIX = PATH_PREFIX;
|
|
|
621
638
|
const MAX_BODY_BYTES = 2 * 1024 * 1024;
|
|
622
639
|
/** Max passthrough body (tiddler content can be large). */
|
|
623
640
|
const MAX_PROXY_BODY_BYTES = 16 * 1024 * 1024;
|
|
641
|
+
/** Max uploaded file body. */
|
|
642
|
+
const MAX_UPLOAD_BYTES = 64 * 1024 * 1024;
|
|
643
|
+
/** Tiddler type for quick-notes: Markdown, so the uploaded images/links and
|
|
644
|
+
* any Markdown in the note actually render in TW (a type-less tiddler is
|
|
645
|
+
* treated as plain wiki text and shows raw `![..]`/`[..]` instead). */
|
|
646
|
+
const NOTE_TYPE = "text/markdown";
|
|
624
647
|
function readBody$1(req, limit = MAX_BODY_BYTES) {
|
|
625
648
|
return new Promise((resolveP, rejectP) => {
|
|
626
649
|
let size = 0;
|
|
@@ -638,6 +661,35 @@ function readBody$1(req, limit = MAX_BODY_BYTES) {
|
|
|
638
661
|
req.on("error", rejectP);
|
|
639
662
|
});
|
|
640
663
|
}
|
|
664
|
+
/** Read a raw (binary-safe) request body up to `limit` bytes. */
|
|
665
|
+
function readBodyBuffer(req, limit = MAX_UPLOAD_BYTES) {
|
|
666
|
+
return new Promise((resolveP, rejectP) => {
|
|
667
|
+
let size = 0;
|
|
668
|
+
const chunks = [];
|
|
669
|
+
req.on("data", (chunk) => {
|
|
670
|
+
size += chunk.length;
|
|
671
|
+
if (size > limit) {
|
|
672
|
+
rejectP(/* @__PURE__ */ new Error("file too large"));
|
|
673
|
+
req.destroy();
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
chunks.push(chunk);
|
|
677
|
+
});
|
|
678
|
+
req.on("end", () => resolveP(Buffer.concat(chunks)));
|
|
679
|
+
req.on("error", rejectP);
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Sanitize an uploaded filename into a safe bare name (no path separators,
|
|
684
|
+
* no `..`, no control characters). Returns '' when nothing usable remains.
|
|
685
|
+
*/
|
|
686
|
+
function sanitizeUploadName(input) {
|
|
687
|
+
if (typeof input !== "string") return "";
|
|
688
|
+
const name = basename(input.trim().replace(/[\\/]+/g, "/")).replace(/[\u0000-\u001f\u007f]/g, "").replace(/[<>:"|?*]/g, "_").replace(/^\.+/, "").trim();
|
|
689
|
+
if (name.length === 0 || name === "." || name === "..") return "";
|
|
690
|
+
if (name.length > 160) return name.slice(0, 160);
|
|
691
|
+
return name;
|
|
692
|
+
}
|
|
641
693
|
function json$1(res, payload, status = 200) {
|
|
642
694
|
const body = JSON.stringify(payload);
|
|
643
695
|
res.writeHead(status, {
|
|
@@ -655,16 +707,20 @@ function timestampTitle(date = /* @__PURE__ */ new Date()) {
|
|
|
655
707
|
}
|
|
656
708
|
/**
|
|
657
709
|
* Open a tiddler in TW's NATIVE editor: save the tiddler (when text is
|
|
658
|
-
* non-empty), reuse or create a DRAFT tiddler carrying
|
|
659
|
-
* (TW's story view renders drafts with the
|
|
660
|
-
* `isDraft && editTemplate`), and return the draft
|
|
661
|
-
* navigate the panel iframe to `#<draftTitle>`.
|
|
710
|
+
* non-empty) as Markdown, reuse or create a DRAFT tiddler carrying
|
|
711
|
+
* `draft.of`/`draft.title` (TW's story view renders drafts with the
|
|
712
|
+
* EditTemplate — list.js: `isDraft && editTemplate`), and return the draft
|
|
713
|
+
* title so the client can navigate the panel iframe to `#<draftTitle>`.
|
|
714
|
+
* The draft carries the same `text/markdown` type as the note so saving it in
|
|
715
|
+
* TW keeps Markdown (a draft without a matching type would overwrite the
|
|
716
|
+
* note's type back to plain wiki text).
|
|
662
717
|
*/
|
|
663
718
|
async function openInTwEditor(client, title, text, tags) {
|
|
664
719
|
if (text.trim().length > 0) await client.put({
|
|
665
720
|
title,
|
|
666
721
|
text,
|
|
667
|
-
tags
|
|
722
|
+
tags,
|
|
723
|
+
type: NOTE_TYPE
|
|
668
724
|
});
|
|
669
725
|
let draftText = text;
|
|
670
726
|
if (draftText.trim().length === 0) draftText = (await client.get(title))?.text ?? "";
|
|
@@ -682,7 +738,7 @@ async function openInTwEditor(client, title, text, tags) {
|
|
|
682
738
|
text: draftText,
|
|
683
739
|
"draft.of": title,
|
|
684
740
|
"draft.title": title,
|
|
685
|
-
type:
|
|
741
|
+
type: NOTE_TYPE
|
|
686
742
|
});
|
|
687
743
|
return {
|
|
688
744
|
title,
|
|
@@ -740,7 +796,8 @@ function registerRoutes(ctx, deps) {
|
|
|
740
796
|
await client.put({
|
|
741
797
|
title,
|
|
742
798
|
text,
|
|
743
|
-
tags
|
|
799
|
+
tags,
|
|
800
|
+
type: NOTE_TYPE
|
|
744
801
|
});
|
|
745
802
|
deps.autoCommit();
|
|
746
803
|
json$1(res, {
|
|
@@ -748,7 +805,8 @@ function registerRoutes(ctx, deps) {
|
|
|
748
805
|
title,
|
|
749
806
|
tag: tags.join(" "),
|
|
750
807
|
tags,
|
|
751
|
-
text
|
|
808
|
+
text,
|
|
809
|
+
type: NOTE_TYPE
|
|
752
810
|
});
|
|
753
811
|
} catch (err) {
|
|
754
812
|
json$1(res, {
|
|
@@ -823,6 +881,120 @@ function registerRoutes(ctx, deps) {
|
|
|
823
881
|
}, 500);
|
|
824
882
|
}
|
|
825
883
|
};
|
|
884
|
+
/** One-click git sync for the floating button / settings page: pull →
|
|
885
|
+
* commit → push, then return the fresh status. Mirrors the agent tool's
|
|
886
|
+
* `action=sync` (design doc §7 conflict policy — rebase conflict aborts).
|
|
887
|
+
* When the pull actually changed the working tree, the running TW child
|
|
888
|
+
* still holds the old in-memory snapshot — restart it (same port) so the
|
|
889
|
+
* UI reflects the pulled files instead of looking stale. */
|
|
890
|
+
const handleSync = async (_req, res) => {
|
|
891
|
+
const dir = deps.getWikiPath();
|
|
892
|
+
const status = async () => {
|
|
893
|
+
try {
|
|
894
|
+
return await deps.git.status(dir);
|
|
895
|
+
} catch {
|
|
896
|
+
return null;
|
|
897
|
+
}
|
|
898
|
+
};
|
|
899
|
+
try {
|
|
900
|
+
const pulled = await deps.git.pull(dir);
|
|
901
|
+
if (!pulled.ok) {
|
|
902
|
+
json$1(res, {
|
|
903
|
+
ok: false,
|
|
904
|
+
action: "sync",
|
|
905
|
+
message: pulled.message,
|
|
906
|
+
...pulled.conflictFiles !== void 0 ? { conflictFiles: pulled.conflictFiles } : {},
|
|
907
|
+
status: await status()
|
|
908
|
+
}, 409);
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
let restarted = false;
|
|
912
|
+
let restartError;
|
|
913
|
+
if (pulled.changed === true) try {
|
|
914
|
+
await deps.server.restart();
|
|
915
|
+
restarted = true;
|
|
916
|
+
} catch (err) {
|
|
917
|
+
restartError = err instanceof Error ? err.message : String(err);
|
|
918
|
+
}
|
|
919
|
+
const committed = await deps.git.commit(dir, `sync ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
920
|
+
const pushed = await deps.git.push(dir);
|
|
921
|
+
const fresh = await status();
|
|
922
|
+
json$1(res, {
|
|
923
|
+
ok: pushed.ok,
|
|
924
|
+
action: "sync",
|
|
925
|
+
message: pushed.ok ? "同步完成" : pushed.message,
|
|
926
|
+
pull: "ok",
|
|
927
|
+
...pulled.changed === true ? { changed: true } : {},
|
|
928
|
+
restarted,
|
|
929
|
+
...restartError !== void 0 ? { restartError } : {},
|
|
930
|
+
commit: committed.message,
|
|
931
|
+
push: pushed.message,
|
|
932
|
+
status: fresh,
|
|
933
|
+
lastSync: (/* @__PURE__ */ new Date()).toISOString()
|
|
934
|
+
}, pushed.ok ? 200 : 502);
|
|
935
|
+
} catch (err) {
|
|
936
|
+
json$1(res, {
|
|
937
|
+
ok: false,
|
|
938
|
+
error: err instanceof Error ? err.message : String(err)
|
|
939
|
+
}, 500);
|
|
940
|
+
}
|
|
941
|
+
};
|
|
942
|
+
/**
|
|
943
|
+
* Save an uploaded file into the wiki's `files/` folder (git-tracked; TW's
|
|
944
|
+
* core server serves it at `/files/<name>`, get-file.js — no restart
|
|
945
|
+
* needed). Body is the raw file; the name arrives in `X-Filename`. A
|
|
946
|
+
* collision appends `-1`, `-2`, … so nothing is ever overwritten.
|
|
947
|
+
*/
|
|
948
|
+
const handleUpload = async (req, res) => {
|
|
949
|
+
try {
|
|
950
|
+
const buf = await readBodyBuffer(req);
|
|
951
|
+
let name = sanitizeUploadName(new URL(req.url ?? "/", "http://127.0.0.1").searchParams.get("name") ?? "");
|
|
952
|
+
if (name.length === 0 && typeof req.headers["x-filename"] === "string") {
|
|
953
|
+
let decoded = "";
|
|
954
|
+
try {
|
|
955
|
+
decoded = decodeURIComponent(req.headers["x-filename"]);
|
|
956
|
+
} catch {
|
|
957
|
+
decoded = req.headers["x-filename"];
|
|
958
|
+
}
|
|
959
|
+
name = sanitizeUploadName(decoded);
|
|
960
|
+
}
|
|
961
|
+
if (name.length === 0) {
|
|
962
|
+
json$1(res, {
|
|
963
|
+
ok: false,
|
|
964
|
+
error: "missing or invalid filename"
|
|
965
|
+
}, 400);
|
|
966
|
+
return;
|
|
967
|
+
}
|
|
968
|
+
const filesDir = join(deps.getWikiPath(), "files");
|
|
969
|
+
await mkdir(filesDir, { recursive: true });
|
|
970
|
+
const ext = extname(name);
|
|
971
|
+
const stem = ext.length > 0 ? name.slice(0, -ext.length) : name;
|
|
972
|
+
let candidate = name;
|
|
973
|
+
for (let i = 1;; i++) {
|
|
974
|
+
try {
|
|
975
|
+
await access(join(filesDir, candidate));
|
|
976
|
+
} catch {
|
|
977
|
+
break;
|
|
978
|
+
}
|
|
979
|
+
candidate = `${stem}-${i}${ext}`;
|
|
980
|
+
}
|
|
981
|
+
await writeFile(join(filesDir, candidate), buf);
|
|
982
|
+
deps.autoCommit();
|
|
983
|
+
json$1(res, {
|
|
984
|
+
ok: true,
|
|
985
|
+
name: candidate,
|
|
986
|
+
path: `files/${candidate}`,
|
|
987
|
+
url: `/files/${encodeURIComponent(candidate)}`,
|
|
988
|
+
size: buf.length,
|
|
989
|
+
type: req.headers["content-type"] ?? "application/octet-stream"
|
|
990
|
+
});
|
|
991
|
+
} catch (err) {
|
|
992
|
+
json$1(res, {
|
|
993
|
+
ok: false,
|
|
994
|
+
error: err instanceof Error ? err.message : String(err)
|
|
995
|
+
}, err instanceof Error && /too large/.test(err.message) ? 413 : 500);
|
|
996
|
+
}
|
|
997
|
+
};
|
|
826
998
|
/** Passthrough /dsh-tiddlywiki/api/<rest> → TW root /<rest>. */
|
|
827
999
|
const handleApiProxy = async (req, res) => {
|
|
828
1000
|
if (deps.getClient() === void 0) {
|
|
@@ -889,6 +1061,20 @@ function registerRoutes(ctx, deps) {
|
|
|
889
1061
|
handleTags(req, res);
|
|
890
1062
|
}
|
|
891
1063
|
}),
|
|
1064
|
+
ctx.webServer.register({
|
|
1065
|
+
kind: "exact",
|
|
1066
|
+
path: `${ROUTE_PREFIX}/sync`,
|
|
1067
|
+
handler: (req, res) => {
|
|
1068
|
+
handleSync(req, res);
|
|
1069
|
+
}
|
|
1070
|
+
}),
|
|
1071
|
+
ctx.webServer.register({
|
|
1072
|
+
kind: "exact",
|
|
1073
|
+
path: `${ROUTE_PREFIX}/upload`,
|
|
1074
|
+
handler: (req, res) => {
|
|
1075
|
+
handleUpload(req, res);
|
|
1076
|
+
}
|
|
1077
|
+
}),
|
|
892
1078
|
ctx.webServer.register({
|
|
893
1079
|
kind: "exact",
|
|
894
1080
|
path: `${ROUTE_PREFIX}/restart`,
|
|
@@ -1888,14 +2074,27 @@ function registerTiddlywikiTools(ctx, deps) {
|
|
|
1888
2074
|
},
|
|
1889
2075
|
execute: async (args) => {
|
|
1890
2076
|
const dir = deps.wikiPath();
|
|
2077
|
+
/** Restart TW after a pull that changed the tree (stale snapshot drop). */
|
|
2078
|
+
const restartIfChanged = async (pulled) => {
|
|
2079
|
+
if (pulled.changed !== true || deps.restartWiki === void 0) return {};
|
|
2080
|
+
try {
|
|
2081
|
+
await deps.restartWiki();
|
|
2082
|
+
return { restarted: true };
|
|
2083
|
+
} catch (err) {
|
|
2084
|
+
return { restartError: err instanceof Error ? err.message : String(err) };
|
|
2085
|
+
}
|
|
2086
|
+
};
|
|
1891
2087
|
switch (args.action) {
|
|
1892
2088
|
case "pull": {
|
|
1893
2089
|
const r = await deps.git.pull(dir);
|
|
2090
|
+
const restart = await restartIfChanged(r);
|
|
1894
2091
|
return {
|
|
1895
2092
|
action: args.action,
|
|
1896
2093
|
ok: r.ok,
|
|
1897
2094
|
message: r.message,
|
|
1898
|
-
...r.conflictFiles !== void 0 ? { conflictFiles: r.conflictFiles } : {}
|
|
2095
|
+
...r.conflictFiles !== void 0 ? { conflictFiles: r.conflictFiles } : {},
|
|
2096
|
+
...r.changed === true ? { changed: true } : {},
|
|
2097
|
+
...restart
|
|
1899
2098
|
};
|
|
1900
2099
|
}
|
|
1901
2100
|
case "push": {
|
|
@@ -1914,6 +2113,7 @@ function registerTiddlywikiTools(ctx, deps) {
|
|
|
1914
2113
|
message: pulled.message,
|
|
1915
2114
|
...pulled.conflictFiles !== void 0 ? { conflictFiles: pulled.conflictFiles } : {}
|
|
1916
2115
|
};
|
|
2116
|
+
const restart = await restartIfChanged(pulled);
|
|
1917
2117
|
const committed = await deps.git.commit(dir, args.message ?? `sync ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
1918
2118
|
const pushed = await deps.git.push(dir);
|
|
1919
2119
|
const status = await deps.git.status(dir);
|
|
@@ -1922,6 +2122,8 @@ function registerTiddlywikiTools(ctx, deps) {
|
|
|
1922
2122
|
ok: pushed.ok,
|
|
1923
2123
|
message: pushed.ok ? "同步完成" : pushed.message,
|
|
1924
2124
|
pull: "ok",
|
|
2125
|
+
...pulled.changed === true ? { changed: true } : {},
|
|
2126
|
+
...restart,
|
|
1925
2127
|
commit: committed.message,
|
|
1926
2128
|
push: pushed.message,
|
|
1927
2129
|
status
|
|
@@ -1942,6 +2144,8 @@ function renderSync(value) {
|
|
|
1942
2144
|
}
|
|
1943
2145
|
if (value.commit !== void 0) lines.push(`本地 commit: ${value.commit}`);
|
|
1944
2146
|
if (value.push !== void 0) lines.push(`远端 push: ${value.push}`);
|
|
2147
|
+
if (value.changed === true) lines.push(value.restarted === true ? "本次 pull 拉取了新内容,TW 服务已自动重启(同端口),读取/搜索均为最新快照。" : "本次 pull 拉取了新内容,但 TW 服务未能自动重启(如需最新快照,请手动重启 TW)。");
|
|
2148
|
+
if (value.restartError !== void 0) lines.push(`TW 重启失败: ${value.restartError}`);
|
|
1945
2149
|
if (value.status !== void 0) {
|
|
1946
2150
|
const s = value.status;
|
|
1947
2151
|
const bits = [`分支 ${s.branch}`];
|
|
@@ -1996,7 +2200,8 @@ const DEFAULTS = {
|
|
|
1996
2200
|
note: { tag: "inbox" },
|
|
1997
2201
|
ui: {
|
|
1998
2202
|
showQuickNote: true,
|
|
1999
|
-
showPanelStatus: true
|
|
2203
|
+
showPanelStatus: true,
|
|
2204
|
+
showSyncButton: true
|
|
2000
2205
|
},
|
|
2001
2206
|
auth: {
|
|
2002
2207
|
username: "",
|
|
@@ -2051,7 +2256,9 @@ const PROMPT_TEXT = `## TiddlyWiki 持久知识库
|
|
|
2051
2256
|
2. 收工 commit + push:\`tiddlywiki_git_sync action=sync\`(pull → commit → push)。
|
|
2052
2257
|
3. 插件会自动防抖 commit(默认 60s),手动同步用上面的工具。
|
|
2053
2258
|
|
|
2054
|
-
把 wiki 当作长期记忆与知识沉淀的地方:会议纪要、决策记录、调研笔记、随手的想法都可存成独立 tiddler(tag 建议用 inbox/meeting/decision
|
|
2259
|
+
把 wiki 当作长期记忆与知识沉淀的地方:会议纪要、决策记录、调研笔记、随手的想法都可存成独立 tiddler(tag 建议用 inbox/meeting/decision 等便于检索)。
|
|
2260
|
+
|
|
2261
|
+
用本插件自动创建笔记时,除了业务性 tag 外,请把「当前工作区(项目)的名字」也作为标签之一加上去(例如 \`tiddlywiki_put\` 的 tags 里带上当前 workspace 名),这样笔记能按项目归集、检索。`;
|
|
2055
2262
|
/**
|
|
2056
2263
|
* Mount the host half.
|
|
2057
2264
|
* @param ctx - the plugin context (tools + systemPrompt injected).
|
|
@@ -2093,7 +2300,8 @@ function apply(ctx, rawConfig = {}) {
|
|
|
2093
2300
|
};
|
|
2094
2301
|
const effectiveUi = () => ({
|
|
2095
2302
|
showQuickNote: eff().ui?.showQuickNote !== false,
|
|
2096
|
-
showPanelStatus: eff().ui?.showPanelStatus !== false
|
|
2303
|
+
showPanelStatus: eff().ui?.showPanelStatus !== false,
|
|
2304
|
+
showSyncButton: eff().ui?.showSyncButton !== false
|
|
2097
2305
|
});
|
|
2098
2306
|
const disposers = [];
|
|
2099
2307
|
const disposeAll = () => {
|
|
@@ -2159,7 +2367,10 @@ function apply(ctx, rawConfig = {}) {
|
|
|
2159
2367
|
git,
|
|
2160
2368
|
wikiPath: () => wikiPath,
|
|
2161
2369
|
noteTag: effectiveNoteTag,
|
|
2162
|
-
autoCommit: () => committer?.touch()
|
|
2370
|
+
autoCommit: () => committer?.touch(),
|
|
2371
|
+
restartWiki: async () => {
|
|
2372
|
+
await server.restart();
|
|
2373
|
+
}
|
|
2163
2374
|
};
|
|
2164
2375
|
disposers.push(...registerTiddlywikiTools(ctx, toolsDeps));
|
|
2165
2376
|
(async () => {
|
|
@@ -2221,6 +2432,6 @@ function apply(ctx, rawConfig = {}) {
|
|
|
2221
2432
|
}, "dsh-tiddlywiki: host teardown");
|
|
2222
2433
|
}
|
|
2223
2434
|
//#endregion
|
|
2224
|
-
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 };
|
|
2435
|
+
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, registerRoutes, resolveTwRoot, seedDocNote, writeWikiInfo };
|
|
2225
2436
|
|
|
2226
2437
|
//# sourceMappingURL=index.js.map
|