@lemoncat7/dsh-ssh 1.5.3 → 1.6.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.
Files changed (57) hide show
  1. package/ARCHITECTURE.md +3 -0
  2. package/README.md +10 -1
  3. package/docs/releases/1.6.0.md +12 -0
  4. package/lib/api.d.ts.map +1 -1
  5. package/lib/api.js +24 -2
  6. package/lib/api.js.map +1 -1
  7. package/lib/client-api.d.ts +21 -6
  8. package/lib/client-api.d.ts.map +1 -1
  9. package/lib/client-api.js +21 -12
  10. package/lib/client-api.js.map +1 -1
  11. package/lib/client.js +325 -59
  12. package/lib/client.js.map +4 -4
  13. package/lib/html-file-preview.d.ts +8 -0
  14. package/lib/html-file-preview.d.ts.map +1 -0
  15. package/lib/html-file-preview.js +55 -0
  16. package/lib/html-file-preview.js.map +1 -0
  17. package/lib/local-markdown-file.d.ts +5 -0
  18. package/lib/local-markdown-file.d.ts.map +1 -0
  19. package/lib/local-markdown-file.js +63 -0
  20. package/lib/local-markdown-file.js.map +1 -0
  21. package/lib/local-workspace.d.ts +1 -0
  22. package/lib/local-workspace.d.ts.map +1 -1
  23. package/lib/local-workspace.js +3 -2
  24. package/lib/local-workspace.js.map +1 -1
  25. package/lib/markdown-file.d.ts +19 -0
  26. package/lib/markdown-file.d.ts.map +1 -0
  27. package/lib/markdown-file.js +52 -0
  28. package/lib/markdown-file.js.map +1 -0
  29. package/lib/markdown-preview-editor.d.ts +8 -0
  30. package/lib/markdown-preview-editor.d.ts.map +1 -0
  31. package/lib/markdown-preview-editor.js +56 -0
  32. package/lib/markdown-preview-editor.js.map +1 -0
  33. package/lib/preview-refresh-control.d.ts +9 -0
  34. package/lib/preview-refresh-control.d.ts.map +1 -0
  35. package/lib/preview-refresh-control.js +6 -0
  36. package/lib/preview-refresh-control.js.map +1 -0
  37. package/lib/sftp-client.d.ts +11 -1
  38. package/lib/sftp-client.d.ts.map +1 -1
  39. package/lib/sftp-client.js +57 -28
  40. package/lib/sftp-client.js.map +1 -1
  41. package/lib/sftp-markdown-file.d.ts +6 -0
  42. package/lib/sftp-markdown-file.d.ts.map +1 -0
  43. package/lib/sftp-markdown-file.js +72 -0
  44. package/lib/sftp-markdown-file.js.map +1 -0
  45. package/lib/sftp.d.ts +1 -0
  46. package/lib/sftp.d.ts.map +1 -1
  47. package/lib/sftp.js +3 -1
  48. package/lib/sftp.js.map +1 -1
  49. package/lib/use-file-preview.d.ts +13 -0
  50. package/lib/use-file-preview.d.ts.map +1 -0
  51. package/lib/use-file-preview.js +100 -0
  52. package/lib/use-file-preview.js.map +1 -0
  53. package/lib/use-markdown-draft.d.ts +23 -0
  54. package/lib/use-markdown-draft.d.ts.map +1 -0
  55. package/lib/use-markdown-draft.js +114 -0
  56. package/lib/use-markdown-draft.js.map +1 -0
  57. package/package.json +10 -3
@@ -0,0 +1,13 @@
1
+ import { type RefObject } from 'react';
2
+ import type { SftpEntryView, SftpFilePreviewView } from './client-api.js';
3
+ /** One request at a time; only the active preview owns its polling lifecycle. */
4
+ export declare function useFilePreview(path: string, load: (path: string, signal?: AbortSignal) => Promise<SftpFilePreviewView>, stat: (path: string, signal?: AbortSignal) => Promise<SftpEntryView>, bodies: RefObject<HTMLDivElement>[]): {
5
+ preview: SftpFilePreviewView | undefined;
6
+ error: string | undefined;
7
+ busy: boolean;
8
+ locked: boolean;
9
+ setLocked: import("react").Dispatch<import("react").SetStateAction<boolean>>;
10
+ refresh: (automatic?: boolean) => Promise<void>;
11
+ revision: number;
12
+ };
13
+ //# sourceMappingURL=use-file-preview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-file-preview.d.ts","sourceRoot":"","sources":["../src/use-file-preview.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6D,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AACjG,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAEzE,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,mBAAmB,CAAC,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,cAAc,CAAC,EAAE;;;;;;;;EA6EjO"}
@@ -0,0 +1,100 @@
1
+ import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
2
+ /** One request at a time; only the active preview owns its polling lifecycle. */
3
+ export function useFilePreview(path, load, stat, bodies) {
4
+ const [preview, setPreview] = useState();
5
+ const [error, setError] = useState();
6
+ const [busy, setBusy] = useState(false);
7
+ const [locked, setLocked] = useState(false);
8
+ const [revision, setRevision] = useState(0);
9
+ const generation = useRef(0);
10
+ const running = useRef(false);
11
+ const request = useRef();
12
+ const current = useRef();
13
+ const signature = useRef();
14
+ const containers = useRef(bodies);
15
+ containers.current = bodies;
16
+ const scroll = useRef([]);
17
+ const refresh = useCallback(async (automatic = false) => {
18
+ if (running.current)
19
+ return;
20
+ const epoch = generation.current;
21
+ running.current = true;
22
+ setBusy(true);
23
+ const controller = new AbortController();
24
+ request.current = controller;
25
+ const timeout = setTimeout(() => controller.abort(), 30000);
26
+ try {
27
+ const meta = await stat(path, controller.signal);
28
+ if (epoch !== generation.current)
29
+ return;
30
+ if (meta.kind === 'directory')
31
+ throw new Error('文件已变为目录,请返回目录重新打开');
32
+ const stamp = `${meta.size}:${meta.modifiedAt}`;
33
+ if (automatic && current.current && stamp === signature.current) {
34
+ setError(undefined);
35
+ return;
36
+ }
37
+ const next = await load(path, controller.signal);
38
+ if (epoch !== generation.current)
39
+ return;
40
+ const previous = current.current;
41
+ const changed = !previous || next.kind !== previous.kind || next.text !== previous.text || next.size !== previous.size || next.mimeType !== previous.mimeType || next.truncated !== previous.truncated;
42
+ // Capture at response time so scrolling during the request is respected.
43
+ if (changed || next.kind !== 'text') {
44
+ scroll.current = containers.current.flatMap(ref => ref.current ? [ref.current, ...ref.current.querySelectorAll('pre')].map(node => ({ node, top: node.scrollTop, left: node.scrollLeft })) : []);
45
+ current.current = next;
46
+ setPreview(next);
47
+ setRevision(value => value + 1);
48
+ }
49
+ signature.current = stamp;
50
+ setError(undefined);
51
+ }
52
+ catch (reason) {
53
+ if (epoch === generation.current)
54
+ setError(reason instanceof Error ? reason.message : String(reason));
55
+ }
56
+ finally {
57
+ clearTimeout(timeout);
58
+ if (epoch === generation.current) {
59
+ running.current = false;
60
+ setBusy(false);
61
+ }
62
+ }
63
+ }, [path, load, stat]);
64
+ useEffect(() => {
65
+ generation.current++;
66
+ running.current = false;
67
+ current.current = undefined;
68
+ signature.current = undefined;
69
+ setPreview(undefined);
70
+ setError(undefined);
71
+ setLocked(false);
72
+ setRevision(0);
73
+ void refresh();
74
+ return () => { generation.current++; request.current?.abort(); };
75
+ }, [refresh]);
76
+ useLayoutEffect(() => {
77
+ for (const { node, top, left } of scroll.current) {
78
+ node.scrollTop = top;
79
+ node.scrollLeft = left;
80
+ }
81
+ scroll.current = [];
82
+ }, [revision]);
83
+ useEffect(() => {
84
+ if (!locked || preview?.kind === 'pdf')
85
+ return;
86
+ let stopped = false;
87
+ let timer;
88
+ const tick = async () => {
89
+ const visible = !document.hidden && containers.current.some(ref => ref.current && ref.current.getClientRects().length > 0);
90
+ if (visible)
91
+ await refresh(true);
92
+ if (!stopped)
93
+ timer = setTimeout(tick, 5000);
94
+ };
95
+ timer = setTimeout(tick, 5000);
96
+ return () => { stopped = true; clearTimeout(timer); };
97
+ }, [locked, refresh, preview?.kind]);
98
+ return { preview, error, busy, locked, setLocked, refresh, revision };
99
+ }
100
+ //# sourceMappingURL=use-file-preview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-file-preview.js","sourceRoot":"","sources":["../src/use-file-preview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,EAAE,QAAQ,EAAkB,MAAM,OAAO,CAAA;AAGjG,iFAAiF;AACjF,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,IAA0E,EAAE,IAAoE,EAAE,MAAmC;IAChO,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,EAAuB,CAAA;IAC7D,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,EAAU,CAAA;IAC5C,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IACvC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC3C,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IAC3C,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;IAC5B,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;IAC7B,MAAM,OAAO,GAAG,MAAM,EAAmB,CAAA;IACzC,MAAM,OAAO,GAAG,MAAM,EAAuB,CAAA;IAC7C,MAAM,SAAS,GAAG,MAAM,EAAU,CAAA;IAClC,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,CAAA;IACjC,UAAU,CAAC,OAAO,GAAG,MAAM,CAAA;IAC3B,MAAM,MAAM,GAAG,MAAM,CAA0D,EAAE,CAAC,CAAA;IAElF,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,KAAK,EAAE,EAAE;QACtD,IAAI,OAAO,CAAC,OAAO;YAAE,OAAM;QAC3B,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAA;QAChC,OAAO,CAAC,OAAO,GAAG,IAAI,CAAA;QACtB,OAAO,CAAC,IAAI,CAAC,CAAA;QACb,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;QACxC,OAAO,CAAC,OAAO,GAAG,UAAU,CAAA;QAC5B,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,KAAK,CAAC,CAAA;QAC3D,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,CAAA;YAChD,IAAI,KAAK,KAAK,UAAU,CAAC,OAAO;gBAAE,OAAM;YACxC,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW;gBAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAA;YACnE,MAAM,KAAK,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE,CAAA;YAC/C,IAAI,SAAS,IAAI,OAAO,CAAC,OAAO,IAAI,KAAK,KAAK,SAAS,CAAC,OAAO,EAAE,CAAC;gBAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;gBAAC,OAAM;YAAC,CAAC;YAChG,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,CAAA;YAChD,IAAI,KAAK,KAAK,UAAU,CAAC,OAAO;gBAAE,OAAM;YACxC,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAA;YAChC,MAAM,OAAO,GAAG,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,KAAK,QAAQ,CAAC,QAAQ,IAAI,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC,SAAS,CAAA;YACtM,yEAAyE;YACzE,IAAI,OAAO,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBACpC,MAAM,CAAC,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAc,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;gBAC7M,OAAO,CAAC,OAAO,GAAG,IAAI,CAAA;gBACtB,UAAU,CAAC,IAAI,CAAC,CAAA;gBAChB,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC,CAAA;YACjC,CAAC;YACD,SAAS,CAAC,OAAO,GAAG,KAAK,CAAA;YACzB,QAAQ,CAAC,SAAS,CAAC,CAAA;QACrB,CAAC;QAAC,OAAO,MAAM,EAAE,CAAC;YAChB,IAAI,KAAK,KAAK,UAAU,CAAC,OAAO;gBAAE,QAAQ,CAAC,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAA;QACvG,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,OAAO,CAAC,CAAA;YACrB,IAAI,KAAK,KAAK,UAAU,CAAC,OAAO,EAAE,CAAC;gBAAC,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC;gBAAC,OAAO,CAAC,KAAK,CAAC,CAAA;YAAC,CAAC;QAC/E,CAAC;IACH,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAA;IAEtB,SAAS,CAAC,GAAG,EAAE;QACb,UAAU,CAAC,OAAO,EAAE,CAAA;QACpB,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC;QAAC,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;QAAC,SAAS,CAAC,OAAO,GAAG,SAAS,CAAA;QACnF,UAAU,CAAC,SAAS,CAAC,CAAC;QAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAAC,WAAW,CAAC,CAAC,CAAC,CAAA;QAC5E,KAAK,OAAO,EAAE,CAAA;QACd,OAAO,GAAG,EAAE,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,CAAA,CAAC,CAAC,CAAA;IACjE,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAA;IAEb,eAAe,CAAC,GAAG,EAAE;QACnB,KAAK,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YAAC,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC;YAAC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAA;QAAC,CAAC;QAClG,MAAM,CAAC,OAAO,GAAG,EAAE,CAAA;IACrB,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAA;IAEd,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,MAAM,IAAI,OAAO,EAAE,IAAI,KAAK,KAAK;YAAE,OAAM;QAC9C,IAAI,OAAO,GAAG,KAAK,CAAA;QACnB,IAAI,KAAoC,CAAA;QACxC,MAAM,IAAI,GAAG,KAAK,IAAI,EAAE;YACtB,MAAM,OAAO,GAAG,CAAC,QAAQ,CAAC,MAAM,IAAI,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;YAC1H,IAAI,OAAO;gBAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;YAChC,IAAI,CAAC,OAAO;gBAAE,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QAC9C,CAAC,CAAA;QACD,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QAC9B,OAAO,GAAG,EAAE,GAAG,OAAO,GAAG,IAAI,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAA,CAAC,CAAC,CAAA;IACtD,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,CAAA;IAEpC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAA;AACvE,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { SftpFilePreviewView } from './client-api.js';
2
+ export interface MarkdownDraft {
3
+ text: string;
4
+ baseText: string;
5
+ expectedHash: string;
6
+ }
7
+ export type SaveMarkdown = (input: {
8
+ path: string;
9
+ text: string;
10
+ expectedHash: string;
11
+ }) => Promise<{
12
+ contentHash: string;
13
+ }>;
14
+ export declare function useMarkdownDraft(key: string, path: string, preview: SftpFilePreviewView | undefined, saveFile: SaveMarkdown | undefined, refresh: () => Promise<void>): {
15
+ text: string;
16
+ dirty: boolean;
17
+ saving: boolean;
18
+ message: string;
19
+ change: (text: string) => void;
20
+ save: () => Promise<void>;
21
+ discard: () => Promise<void>;
22
+ };
23
+ //# sourceMappingURL=use-markdown-draft.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-markdown-draft.d.ts","sourceRoot":"","sources":["../src/use-markdown-draft.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAE1D,MAAM,WAAW,aAAa;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE;AACvF,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,KAAK,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAE5H,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,GAAG,SAAS,EAAE,QAAQ,EAAE,YAAY,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC;;;;;mBA8BlI,MAAM;;;EAuCzC"}
@@ -0,0 +1,114 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+ const drafts = new Map();
3
+ export function useMarkdownDraft(key, path, preview, saveFile, refresh) {
4
+ const [draft, setDraft] = useState();
5
+ const [saving, setSaving] = useState(false);
6
+ const [message, setMessage] = useState('');
7
+ const current = useRef(draft);
8
+ current.current = draft;
9
+ const busy = useRef(false);
10
+ const alive = useRef(true);
11
+ const dirty = Boolean(draft && draft.text !== draft.baseText);
12
+ const storageKey = `dsh-ssh:markdown-draft:${key}`;
13
+ useEffect(() => { alive.current = true; return () => { alive.current = false; }; }, []);
14
+ useEffect(() => {
15
+ if (!preview?.contentHash || preview.text === undefined)
16
+ return;
17
+ let cached = drafts.get(key);
18
+ if (!cached) {
19
+ try {
20
+ const value = JSON.parse(sessionStorage.getItem(storageKey) || 'null');
21
+ if (value && typeof value.text === 'string' && typeof value.baseText === 'string' && /^[a-f0-9]{64}$/.test(value.expectedHash))
22
+ cached = value;
23
+ }
24
+ catch { /* Storage can be disabled; keep in-memory drafts. */ }
25
+ }
26
+ if (cached) {
27
+ setDraft(cached);
28
+ setMessage(cached.expectedHash === preview.contentHash ? '已恢复未保存草稿' : '文件版本已变化,草稿已保留;保存时会检查冲突');
29
+ }
30
+ else
31
+ setDraft({ text: preview.text, baseText: preview.text, expectedHash: preview.contentHash });
32
+ }, [key, storageKey, preview]);
33
+ const persist = useCallback((value) => {
34
+ if (value && value.text !== value.baseText)
35
+ drafts.set(key, value);
36
+ else
37
+ drafts.delete(key);
38
+ try {
39
+ if (value && value.text !== value.baseText)
40
+ sessionStorage.setItem(storageKey, JSON.stringify(value));
41
+ else
42
+ sessionStorage.removeItem(storageKey);
43
+ }
44
+ catch {
45
+ setMessage('浏览器无法持久保存草稿,请保持此页面打开并及时保存');
46
+ }
47
+ }, [key, storageKey]);
48
+ const change = useCallback((text) => {
49
+ if (busy.current || !current.current)
50
+ return;
51
+ const original = current.current.baseText;
52
+ if (original.includes('\r\n'))
53
+ text = text.replace(/\r?\n/g, '\r\n');
54
+ if (original.startsWith('\uFEFF') && !text.startsWith('\uFEFF'))
55
+ text = '\uFEFF' + text;
56
+ if (original.endsWith('\n') && !text.endsWith('\n'))
57
+ text += original.endsWith('\r\n') ? '\r\n' : '\n';
58
+ const value = { ...current.current, text };
59
+ current.current = value;
60
+ setDraft(value);
61
+ setMessage('');
62
+ persist(value);
63
+ }, [persist]);
64
+ const save = useCallback(async () => {
65
+ const value = current.current;
66
+ if (!value || !saveFile || busy.current || value.text === value.baseText)
67
+ return;
68
+ busy.current = true;
69
+ setSaving(true);
70
+ setMessage('正在保存…');
71
+ try {
72
+ const result = await saveFile({ path, text: value.text, expectedHash: value.expectedHash });
73
+ if (drafts.get(key)?.text === value.text)
74
+ persist(undefined);
75
+ if (alive.current) {
76
+ const next = { text: value.text, baseText: value.text, expectedHash: result.contentHash };
77
+ current.current = next;
78
+ setDraft(next);
79
+ setMessage('已保存');
80
+ await refresh();
81
+ }
82
+ }
83
+ catch (error) {
84
+ if (alive.current)
85
+ setMessage(`保存失败,草稿仍保留:${error instanceof Error ? error.message : String(error)}`);
86
+ }
87
+ finally {
88
+ busy.current = false;
89
+ if (alive.current)
90
+ setSaving(false);
91
+ }
92
+ }, [key, path, saveFile, persist, refresh]);
93
+ const discard = useCallback(async () => {
94
+ if (busy.current)
95
+ return;
96
+ persist(undefined);
97
+ setMessage('');
98
+ if (preview?.contentHash && preview.text !== undefined) {
99
+ const value = { text: preview.text, baseText: preview.text, expectedHash: preview.contentHash };
100
+ current.current = value;
101
+ setDraft(value);
102
+ }
103
+ await refresh();
104
+ }, [persist, preview, refresh]);
105
+ useEffect(() => {
106
+ if (!dirty)
107
+ return;
108
+ const beforeUnload = (event) => { event.preventDefault(); event.returnValue = ''; };
109
+ window.addEventListener('beforeunload', beforeUnload);
110
+ return () => window.removeEventListener('beforeunload', beforeUnload);
111
+ }, [dirty]);
112
+ return { text: draft?.text ?? preview?.text ?? '', dirty, saving, message, change, save, discard };
113
+ }
114
+ //# sourceMappingURL=use-markdown-draft.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-markdown-draft.js","sourceRoot":"","sources":["../src/use-markdown-draft.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAKhE,MAAM,MAAM,GAAG,IAAI,GAAG,EAAyB,CAAA;AAC/C,MAAM,UAAU,gBAAgB,CAAC,GAAW,EAAE,IAAY,EAAE,OAAwC,EAAE,QAAkC,EAAE,OAA4B;IACpK,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,EAAiB,CAAA;IACnD,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC3C,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAA;IAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAAC,OAAO,CAAC,OAAO,GAAG,KAAK,CAAA;IACtD,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;IAC1B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAA;IAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,QAAQ,CAAC,CAAA;IAC7D,MAAM,UAAU,GAAG,0BAA0B,GAAG,EAAE,CAAA;IAClD,SAAS,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC,OAAO,GAAG,EAAE,GAAG,KAAK,CAAC,OAAO,GAAG,KAAK,CAAA,CAAC,CAAC,CAAA,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IACrF,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,OAAO,EAAE,WAAW,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS;YAAE,OAAM;QAC/D,IAAI,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAC5B,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,CAAA;gBACtE,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC;oBAAE,MAAM,GAAG,KAAK,CAAA;YAChJ,CAAC;YAAC,MAAM,CAAC,CAAC,qDAAqD,CAAC,CAAC;QACnE,CAAC;QACD,IAAI,MAAM,EAAE,CAAC;YAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAAC,UAAU,CAAC,MAAM,CAAC,YAAY,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAA;QAAC,CAAC;;YAC5H,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,CAAA;IAClG,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC,CAAA;IAC9B,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,KAAgC,EAAE,EAAE;QAC/D,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,QAAQ;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;;YAC7D,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QACvB,IAAI,CAAC;YACH,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,QAAQ;gBAAE,cAAc,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAA;;gBAChG,cAAc,CAAC,UAAU,CAAC,UAAU,CAAC,CAAA;QAC5C,CAAC;QAAC,MAAM,CAAC;YAAC,UAAU,CAAC,2BAA2B,CAAC,CAAA;QAAC,CAAC;IACrD,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAA;IACrB,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,IAAY,EAAE,EAAE;QAC1C,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO;YAAE,OAAM;QAC5C,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAA;QACzC,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QACpE,IAAI,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,IAAI,GAAG,QAAQ,GAAG,IAAI,CAAA;QACvF,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,IAAI,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA;QACtG,MAAM,KAAK,GAAG,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,CAAA;QAC1C,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IAC1E,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAA;IACb,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QAClC,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAA;QAC7B,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,QAAQ;YAAE,OAAM;QAChF,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAAC,UAAU,CAAC,OAAO,CAAC,CAAA;QACzD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC,CAAA;YAC3F,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,IAAI,KAAK,KAAK,CAAC,IAAI;gBAAE,OAAO,CAAC,SAAS,CAAC,CAAA;YAC5D,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;gBAClB,MAAM,IAAI,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,CAAC,WAAW,EAAE,CAAA;gBACzF,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;gBAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;gBAAC,UAAU,CAAC,KAAK,CAAC,CAAC;gBAAC,MAAM,OAAO,EAAE,CAAA;YAC5E,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAAC,IAAI,KAAK,CAAC,OAAO;gBAAE,UAAU,CAAC,cAAc,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QAAC,CAAC;gBACjH,CAAC;YAAC,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;YAAC,IAAI,KAAK,CAAC,OAAO;gBAAE,SAAS,CAAC,KAAK,CAAC,CAAA;QAAC,CAAC;IACvE,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAA;IAC3C,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACrC,IAAI,IAAI,CAAC,OAAO;YAAE,OAAM;QACxB,OAAO,CAAC,SAAS,CAAC,CAAC;QAAC,UAAU,CAAC,EAAE,CAAC,CAAA;QAClC,IAAI,OAAO,EAAE,WAAW,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACvD,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,CAAC,WAAW,EAAE,CAAA;YAC/F,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC;YAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;QAC1C,CAAC;QACD,MAAM,OAAO,EAAE,CAAA;IACjB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAA;IAC/B,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,KAAK;YAAE,OAAM;QAClB,MAAM,YAAY,GAAG,CAAC,KAAwB,EAAE,EAAE,GAAG,KAAK,CAAC,cAAc,EAAE,CAAC,CAAC,KAAK,CAAC,WAAW,GAAG,EAAE,CAAA,CAAC,CAAC,CAAA;QACrG,MAAM,CAAC,gBAAgB,CAAC,cAAc,EAAE,YAAY,CAAC,CAAA;QACrD,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,cAAc,EAAE,YAAY,CAAC,CAAA;IACvE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAA;IACX,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,IAAI,OAAO,EAAE,IAAI,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAA;AACpG,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lemoncat7/dsh-ssh",
3
- "version": "1.5.3",
3
+ "version": "1.6.0",
4
4
  "description": "Secure SSH, FTP/FTPS/SFTP file transfer, browser terminals, proxies, port forwarding, and session-scoped AI access for DeepSeek Harness",
5
5
  "keywords": [
6
6
  "deepseek-harness",
@@ -90,7 +90,6 @@
90
90
  "devDependencies": {
91
91
  "@deepseek-ai/cordis": "^4.0.1",
92
92
  "@deepseek-ai/dsh-agent": "0.1.2-rc.1",
93
- "@deepseek-ai/dsh-code-runtime": "0.1.2-rc.1",
94
93
  "@deepseek-ai/dsh-api-session-controller": "0.1.2-rc.1",
95
94
  "@deepseek-ai/dsh-api-workspace-controller": "0.1.2-rc.1",
96
95
  "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-rc.1",
@@ -101,15 +100,23 @@
101
100
  "@deepseek-ai/dsh-client-ui-sidebar": "0.1.2-rc.1",
102
101
  "@deepseek-ai/dsh-client-ui-slots": "0.1.2-rc.1",
103
102
  "@deepseek-ai/dsh-client-ui-theme": "0.1.2-rc.1",
103
+ "@deepseek-ai/dsh-code-runtime": "0.1.2-rc.1",
104
104
  "@deepseek-ai/dsh-credentials": "0.1.2-rc.1",
105
105
  "@deepseek-ai/dsh-invariants": "0.1.2-rc.1",
106
106
  "@deepseek-ai/dsh-llm": "0.1.2-rc.1",
107
107
  "@deepseek-ai/dsh-scope": "0.1.2-rc.1",
108
+ "@deepseek-ai/dsh-session": "0.1.2-rc.1",
108
109
  "@deepseek-ai/dsh-system-prompt": "0.1.2-rc.1",
109
110
  "@deepseek-ai/dsh-terminal": "0.1.2-rc.1",
110
- "@deepseek-ai/dsh-session": "0.1.2-rc.1",
111
111
  "@deepseek-ai/dsh-tools": "0.1.2-rc.1",
112
112
  "@deepseek-ai/dsh-user-approval": "0.1.2-rc.1",
113
+ "@tiptap/core": "^3.30.5",
114
+ "@tiptap/extension-image": "^3.30.5",
115
+ "@tiptap/extension-table": "^3.30.5",
116
+ "@tiptap/extension-task-item": "^3.30.5",
117
+ "@tiptap/extension-task-list": "^3.30.5",
118
+ "@tiptap/markdown": "^3.30.5",
119
+ "@tiptap/starter-kit": "^3.30.5",
113
120
  "@types/node": "^24.0.0",
114
121
  "@types/react": "18.3.28",
115
122
  "@types/ssh2": "^1.15.5",