@lemoncat7/dsh-ssh 1.5.3 → 1.7.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 (142) hide show
  1. package/ARCHITECTURE.md +10 -0
  2. package/README.md +22 -1
  3. package/docs/releases/1.6.0.md +12 -0
  4. package/docs/releases/1.7.0.md +11 -0
  5. package/lib/activity-panel.d.ts.map +1 -1
  6. package/lib/activity-panel.js +14 -8
  7. package/lib/activity-panel.js.map +1 -1
  8. package/lib/adaptive-workspace.d.ts.map +1 -1
  9. package/lib/adaptive-workspace.js +5 -2
  10. package/lib/adaptive-workspace.js.map +1 -1
  11. package/lib/api.d.ts.map +1 -1
  12. package/lib/api.js +24 -2
  13. package/lib/api.js.map +1 -1
  14. package/lib/client-api.d.ts +21 -6
  15. package/lib/client-api.d.ts.map +1 -1
  16. package/lib/client-api.js +21 -12
  17. package/lib/client-api.js.map +1 -1
  18. package/lib/client.d.ts.map +1 -1
  19. package/lib/client.js +360 -69
  20. package/lib/client.js.map +4 -4
  21. package/lib/commands-panel.d.ts.map +1 -1
  22. package/lib/commands-panel.js +11 -7
  23. package/lib/commands-panel.js.map +1 -1
  24. package/lib/docked-panel-compat.d.ts +1 -1
  25. package/lib/docked-panel-compat.d.ts.map +1 -1
  26. package/lib/docked-panel-compat.js +3 -2
  27. package/lib/docked-panel-compat.js.map +1 -1
  28. package/lib/file-entry-delete-dialog.d.ts.map +1 -1
  29. package/lib/file-entry-delete-dialog.js +5 -2
  30. package/lib/file-entry-delete-dialog.js.map +1 -1
  31. package/lib/file-transfer-workspace.d.ts.map +1 -1
  32. package/lib/file-transfer-workspace.js +48 -30
  33. package/lib/file-transfer-workspace.js.map +1 -1
  34. package/lib/ftp-profile-editor.d.ts.map +1 -1
  35. package/lib/ftp-profile-editor.js +13 -8
  36. package/lib/ftp-profile-editor.js.map +1 -1
  37. package/lib/html-file-preview.d.ts +8 -0
  38. package/lib/html-file-preview.d.ts.map +1 -0
  39. package/lib/html-file-preview.js +58 -0
  40. package/lib/html-file-preview.js.map +1 -0
  41. package/lib/i18n.d.ts +17 -0
  42. package/lib/i18n.d.ts.map +1 -0
  43. package/lib/i18n.js +36 -0
  44. package/lib/i18n.js.map +1 -0
  45. package/lib/local-markdown-file.d.ts +5 -0
  46. package/lib/local-markdown-file.d.ts.map +1 -0
  47. package/lib/local-markdown-file.js +63 -0
  48. package/lib/local-markdown-file.js.map +1 -0
  49. package/lib/local-workspace.d.ts +1 -0
  50. package/lib/local-workspace.d.ts.map +1 -1
  51. package/lib/local-workspace.js +3 -2
  52. package/lib/local-workspace.js.map +1 -1
  53. package/lib/locales/messages.d.ts +2702 -0
  54. package/lib/locales/messages.d.ts.map +1 -0
  55. package/lib/locales/messages.js +2683 -0
  56. package/lib/locales/messages.js.map +1 -0
  57. package/lib/markdown-file.d.ts +19 -0
  58. package/lib/markdown-file.d.ts.map +1 -0
  59. package/lib/markdown-file.js +52 -0
  60. package/lib/markdown-file.js.map +1 -0
  61. package/lib/markdown-preview-editor.d.ts +8 -0
  62. package/lib/markdown-preview-editor.d.ts.map +1 -0
  63. package/lib/markdown-preview-editor.js +62 -0
  64. package/lib/markdown-preview-editor.js.map +1 -0
  65. package/lib/native-directory-button.d.ts.map +1 -1
  66. package/lib/native-directory-button.js +6 -3
  67. package/lib/native-directory-button.js.map +1 -1
  68. package/lib/preview-refresh-control.d.ts +9 -0
  69. package/lib/preview-refresh-control.d.ts.map +1 -0
  70. package/lib/preview-refresh-control.js +9 -0
  71. package/lib/preview-refresh-control.js.map +1 -0
  72. package/lib/profile-editor.d.ts.map +1 -1
  73. package/lib/profile-editor.js +13 -8
  74. package/lib/profile-editor.js.map +1 -1
  75. package/lib/project-session-dialog.d.ts.map +1 -1
  76. package/lib/project-session-dialog.js +7 -4
  77. package/lib/project-session-dialog.js.map +1 -1
  78. package/lib/remote-path-input.d.ts.map +1 -1
  79. package/lib/remote-path-input.js +10 -7
  80. package/lib/remote-path-input.js.map +1 -1
  81. package/lib/remote-workspace-tree.d.ts.map +1 -1
  82. package/lib/remote-workspace-tree.js +16 -11
  83. package/lib/remote-workspace-tree.js.map +1 -1
  84. package/lib/resizable-split.d.ts.map +1 -1
  85. package/lib/resizable-split.js +4 -1
  86. package/lib/resizable-split.js.map +1 -1
  87. package/lib/sftp-client.d.ts +13 -2
  88. package/lib/sftp-client.d.ts.map +1 -1
  89. package/lib/sftp-client.js +101 -48
  90. package/lib/sftp-client.js.map +1 -1
  91. package/lib/sftp-markdown-file.d.ts +6 -0
  92. package/lib/sftp-markdown-file.d.ts.map +1 -0
  93. package/lib/sftp-markdown-file.js +72 -0
  94. package/lib/sftp-markdown-file.js.map +1 -0
  95. package/lib/sftp.d.ts +1 -0
  96. package/lib/sftp.d.ts.map +1 -1
  97. package/lib/sftp.js +3 -1
  98. package/lib/sftp.js.map +1 -1
  99. package/lib/ssh-locale-binding.d.ts +9 -0
  100. package/lib/ssh-locale-binding.d.ts.map +1 -0
  101. package/lib/ssh-locale-binding.js +29 -0
  102. package/lib/ssh-locale-binding.js.map +1 -0
  103. package/lib/terminal-bootstrap-echo.d.ts +15 -0
  104. package/lib/terminal-bootstrap-echo.d.ts.map +1 -0
  105. package/lib/terminal-bootstrap-echo.js +62 -0
  106. package/lib/terminal-bootstrap-echo.js.map +1 -0
  107. package/lib/terminal-directory.d.ts +3 -0
  108. package/lib/terminal-directory.d.ts.map +1 -0
  109. package/lib/terminal-directory.js +28 -0
  110. package/lib/terminal-directory.js.map +1 -0
  111. package/lib/terminal-session.d.ts +2 -1
  112. package/lib/terminal-session.d.ts.map +1 -1
  113. package/lib/terminal-session.js +22 -7
  114. package/lib/terminal-session.js.map +1 -1
  115. package/lib/terminal-shell-integration.d.ts +6 -0
  116. package/lib/terminal-shell-integration.d.ts.map +1 -0
  117. package/lib/terminal-shell-integration.js +53 -0
  118. package/lib/terminal-shell-integration.js.map +1 -0
  119. package/lib/terminal-workspace.d.ts +2 -1
  120. package/lib/terminal-workspace.d.ts.map +1 -1
  121. package/lib/terminal-workspace.js +10 -5
  122. package/lib/terminal-workspace.js.map +1 -1
  123. package/lib/terminal.d.ts +4 -0
  124. package/lib/terminal.d.ts.map +1 -1
  125. package/lib/terminal.js +31 -1
  126. package/lib/terminal.js.map +1 -1
  127. package/lib/ui-components.d.ts.map +1 -1
  128. package/lib/ui-components.js +14 -8
  129. package/lib/ui-components.js.map +1 -1
  130. package/lib/use-file-preview.d.ts +13 -0
  131. package/lib/use-file-preview.d.ts.map +1 -0
  132. package/lib/use-file-preview.js +101 -0
  133. package/lib/use-file-preview.js.map +1 -0
  134. package/lib/use-markdown-draft.d.ts +23 -0
  135. package/lib/use-markdown-draft.d.ts.map +1 -0
  136. package/lib/use-markdown-draft.js +115 -0
  137. package/lib/use-markdown-draft.js.map +1 -0
  138. package/lib/use-ssh-locale.d.ts +4 -0
  139. package/lib/use-ssh-locale.d.ts.map +1 -0
  140. package/lib/use-ssh-locale.js +7 -0
  141. package/lib/use-ssh-locale.js.map +1 -0
  142. package/package.json +10 -3
@@ -0,0 +1,101 @@
1
+ import { t } from './i18n.js';
2
+ import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
3
+ /** One request at a time; only the active preview owns its polling lifecycle. */
4
+ export function useFilePreview(path, load, stat, bodies) {
5
+ const [preview, setPreview] = useState();
6
+ const [error, setError] = useState();
7
+ const [busy, setBusy] = useState(false);
8
+ const [locked, setLocked] = useState(false);
9
+ const [revision, setRevision] = useState(0);
10
+ const generation = useRef(0);
11
+ const running = useRef(false);
12
+ const request = useRef();
13
+ const current = useRef();
14
+ const signature = useRef();
15
+ const containers = useRef(bodies);
16
+ containers.current = bodies;
17
+ const scroll = useRef([]);
18
+ const refresh = useCallback(async (automatic = false) => {
19
+ if (running.current)
20
+ return;
21
+ const epoch = generation.current;
22
+ running.current = true;
23
+ setBusy(true);
24
+ const controller = new AbortController();
25
+ request.current = controller;
26
+ const timeout = setTimeout(() => controller.abort(), 30000);
27
+ try {
28
+ const meta = await stat(path, controller.signal);
29
+ if (epoch !== generation.current)
30
+ return;
31
+ if (meta.kind === 'directory')
32
+ throw new Error(t("use-file-preview.thisFileIsNowADirectoryGoBackAnd"));
33
+ const stamp = `${meta.size}:${meta.modifiedAt}`;
34
+ if (automatic && current.current && stamp === signature.current) {
35
+ setError(undefined);
36
+ return;
37
+ }
38
+ const next = await load(path, controller.signal);
39
+ if (epoch !== generation.current)
40
+ return;
41
+ const previous = current.current;
42
+ const changed = !previous || next.kind !== previous.kind || next.text !== previous.text || next.size !== previous.size || next.mimeType !== previous.mimeType || next.truncated !== previous.truncated;
43
+ // Capture at response time so scrolling during the request is respected.
44
+ if (changed || next.kind !== 'text') {
45
+ scroll.current = containers.current.flatMap(ref => ref.current ? [ref.current, ...ref.current.querySelectorAll('pre')].map(node => ({ node, top: node.scrollTop, left: node.scrollLeft })) : []);
46
+ current.current = next;
47
+ setPreview(next);
48
+ setRevision(value => value + 1);
49
+ }
50
+ signature.current = stamp;
51
+ setError(undefined);
52
+ }
53
+ catch (reason) {
54
+ if (epoch === generation.current)
55
+ setError(reason instanceof Error ? reason.message : String(reason));
56
+ }
57
+ finally {
58
+ clearTimeout(timeout);
59
+ if (epoch === generation.current) {
60
+ running.current = false;
61
+ setBusy(false);
62
+ }
63
+ }
64
+ }, [path, load, stat]);
65
+ useEffect(() => {
66
+ generation.current++;
67
+ running.current = false;
68
+ current.current = undefined;
69
+ signature.current = undefined;
70
+ setPreview(undefined);
71
+ setError(undefined);
72
+ setLocked(false);
73
+ setRevision(0);
74
+ void refresh();
75
+ return () => { generation.current++; request.current?.abort(); };
76
+ }, [refresh]);
77
+ useLayoutEffect(() => {
78
+ for (const { node, top, left } of scroll.current) {
79
+ node.scrollTop = top;
80
+ node.scrollLeft = left;
81
+ }
82
+ scroll.current = [];
83
+ }, [revision]);
84
+ useEffect(() => {
85
+ if (!locked || preview?.kind === 'pdf')
86
+ return;
87
+ let stopped = false;
88
+ let timer;
89
+ const tick = async () => {
90
+ const visible = !document.hidden && containers.current.some(ref => ref.current && ref.current.getClientRects().length > 0);
91
+ if (visible)
92
+ await refresh(true);
93
+ if (!stopped)
94
+ timer = setTimeout(tick, 5000);
95
+ };
96
+ timer = setTimeout(tick, 5000);
97
+ return () => { stopped = true; clearTimeout(timer); };
98
+ }, [locked, refresh, preview?.kind]);
99
+ return { preview, error, busy, locked, setLocked, refresh, revision };
100
+ }
101
+ //# 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,CAAC,EAAE,MAAM,WAAW,CAAA;AAC7B,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,CAAC,CAAC,mDAAmD,CAAC,CAAC,CAAA;YACtG,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":"AAEA,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,115 @@
1
+ import { t } from './i18n.js';
2
+ import { useCallback, useEffect, useRef, useState } from 'react';
3
+ const drafts = new Map();
4
+ export function useMarkdownDraft(key, path, preview, saveFile, refresh) {
5
+ const [draft, setDraft] = useState();
6
+ const [saving, setSaving] = useState(false);
7
+ const [message, setMessage] = useState('');
8
+ const current = useRef(draft);
9
+ current.current = draft;
10
+ const busy = useRef(false);
11
+ const alive = useRef(true);
12
+ const dirty = Boolean(draft && draft.text !== draft.baseText);
13
+ const storageKey = `dsh-ssh:markdown-draft:${key}`;
14
+ useEffect(() => { alive.current = true; return () => { alive.current = false; }; }, []);
15
+ useEffect(() => {
16
+ if (!preview?.contentHash || preview.text === undefined)
17
+ return;
18
+ let cached = drafts.get(key);
19
+ if (!cached) {
20
+ try {
21
+ const value = JSON.parse(sessionStorage.getItem(storageKey) || 'null');
22
+ if (value && typeof value.text === 'string' && typeof value.baseText === 'string' && /^[a-f0-9]{64}$/.test(value.expectedHash))
23
+ cached = value;
24
+ }
25
+ catch { /* Storage can be disabled; keep in-memory drafts. */ }
26
+ }
27
+ if (cached) {
28
+ setDraft(cached);
29
+ setMessage(cached.expectedHash === preview.contentHash ? t("use-markdown-draft.unsavedDraftRestored") : t("use-markdown-draft.theFileHasChangedYourDraftIsKeptSaving"));
30
+ }
31
+ else
32
+ setDraft({ text: preview.text, baseText: preview.text, expectedHash: preview.contentHash });
33
+ }, [key, storageKey, preview]);
34
+ const persist = useCallback((value) => {
35
+ if (value && value.text !== value.baseText)
36
+ drafts.set(key, value);
37
+ else
38
+ drafts.delete(key);
39
+ try {
40
+ if (value && value.text !== value.baseText)
41
+ sessionStorage.setItem(storageKey, JSON.stringify(value));
42
+ else
43
+ sessionStorage.removeItem(storageKey);
44
+ }
45
+ catch {
46
+ setMessage(t("use-markdown-draft.theBrowserCannotPersistThisDraftKeepThisPage"));
47
+ }
48
+ }, [key, storageKey]);
49
+ const change = useCallback((text) => {
50
+ if (busy.current || !current.current)
51
+ return;
52
+ const original = current.current.baseText;
53
+ if (original.includes('\r\n'))
54
+ text = text.replace(/\r?\n/g, '\r\n');
55
+ if (original.startsWith('\uFEFF') && !text.startsWith('\uFEFF'))
56
+ text = '\uFEFF' + text;
57
+ if (original.endsWith('\n') && !text.endsWith('\n'))
58
+ text += original.endsWith('\r\n') ? '\r\n' : '\n';
59
+ const value = { ...current.current, text };
60
+ current.current = value;
61
+ setDraft(value);
62
+ setMessage('');
63
+ persist(value);
64
+ }, [persist]);
65
+ const save = useCallback(async () => {
66
+ const value = current.current;
67
+ if (!value || !saveFile || busy.current || value.text === value.baseText)
68
+ return;
69
+ busy.current = true;
70
+ setSaving(true);
71
+ setMessage(t("client.saving"));
72
+ try {
73
+ const result = await saveFile({ path, text: value.text, expectedHash: value.expectedHash });
74
+ if (drafts.get(key)?.text === value.text)
75
+ persist(undefined);
76
+ if (alive.current) {
77
+ const next = { text: value.text, baseText: value.text, expectedHash: result.contentHash };
78
+ current.current = next;
79
+ setDraft(next);
80
+ setMessage(t("sftp-client.saved"));
81
+ await refresh();
82
+ }
83
+ }
84
+ catch (error) {
85
+ if (alive.current)
86
+ setMessage(t("use-markdown-draft.saveFailedDraftKept", [error instanceof Error ? error.message : String(error)]));
87
+ }
88
+ finally {
89
+ busy.current = false;
90
+ if (alive.current)
91
+ setSaving(false);
92
+ }
93
+ }, [key, path, saveFile, persist, refresh]);
94
+ const discard = useCallback(async () => {
95
+ if (busy.current)
96
+ return;
97
+ persist(undefined);
98
+ setMessage('');
99
+ if (preview?.contentHash && preview.text !== undefined) {
100
+ const value = { text: preview.text, baseText: preview.text, expectedHash: preview.contentHash };
101
+ current.current = value;
102
+ setDraft(value);
103
+ }
104
+ await refresh();
105
+ }, [persist, preview, refresh]);
106
+ useEffect(() => {
107
+ if (!dirty)
108
+ return;
109
+ const beforeUnload = (event) => { event.preventDefault(); event.returnValue = ''; };
110
+ window.addEventListener('beforeunload', beforeUnload);
111
+ return () => window.removeEventListener('beforeunload', beforeUnload);
112
+ }, [dirty]);
113
+ return { text: draft?.text ?? preview?.text ?? '', dirty, saving, message, change, save, discard };
114
+ }
115
+ //# 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":"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"}
@@ -0,0 +1,4 @@
1
+ import { type SshLocale } from './i18n.js';
2
+ /** Subscribe without changing component keys: terminal connections and drafts stay mounted. */
3
+ export declare function useSshLocale(): SshLocale;
4
+ //# sourceMappingURL=use-ssh-locale.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-ssh-locale.d.ts","sourceRoot":"","sources":["../src/use-ssh-locale.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,WAAW,CAAA;AAErD,+FAA+F;AAC/F,wBAAgB,YAAY,IAAI,SAAS,CAExC"}
@@ -0,0 +1,7 @@
1
+ import { useSyncExternalStore } from 'react';
2
+ import { sshLocale } from './i18n.js';
3
+ /** Subscribe without changing component keys: terminal connections and drafts stay mounted. */
4
+ export function useSshLocale() {
5
+ return useSyncExternalStore(sshLocale.subscribe, sshLocale.getSnapshot, sshLocale.getSnapshot);
6
+ }
7
+ //# sourceMappingURL=use-ssh-locale.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-ssh-locale.js","sourceRoot":"","sources":["../src/use-ssh-locale.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAA;AAC5C,OAAO,EAAE,SAAS,EAAkB,MAAM,WAAW,CAAA;AAErD,+FAA+F;AAC/F,MAAM,UAAU,YAAY;IAC1B,OAAO,oBAAoB,CAAC,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC,WAAW,EAAE,SAAS,CAAC,WAAW,CAAC,CAAA;AAChG,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.7.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",