@wonderwhy-er/desktop-commander 0.2.38 → 0.2.40

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 (263) hide show
  1. package/README.md +53 -2
  2. package/dist/handlers/filesystem-handlers.d.ts +5 -0
  3. package/dist/handlers/filesystem-handlers.js +14 -2
  4. package/dist/remote-device/desktop-commander-integration.js +1 -1
  5. package/dist/search-manager.js +31 -38
  6. package/dist/server.js +9 -4
  7. package/dist/terminal-manager.js +4 -2
  8. package/dist/tools/edit.js +34 -1
  9. package/dist/tools/filesystem.js +91 -3
  10. package/dist/tools/improved-process-tools.js +2 -1
  11. package/dist/ui/config-editor/config-editor-runtime.js +65 -14096
  12. package/dist/ui/config-editor/styles.css +2 -1
  13. package/dist/ui/file-preview/preview-runtime.js +435 -26533
  14. package/dist/ui/file-preview/shared/preview-file-types.d.ts +1 -1
  15. package/dist/ui/file-preview/src/app.d.ts +1 -5
  16. package/dist/ui/file-preview/src/app.js +384 -534
  17. package/dist/ui/file-preview/src/components/markdown-renderer.js +47 -9
  18. package/dist/ui/file-preview/src/directory-controller.d.ts +8 -0
  19. package/dist/ui/file-preview/src/directory-controller.js +233 -0
  20. package/dist/ui/file-preview/src/document-layout.d.ts +20 -0
  21. package/dist/ui/file-preview/src/document-layout.js +109 -0
  22. package/dist/ui/file-preview/src/document-outline.d.ts +17 -0
  23. package/dist/ui/file-preview/src/document-outline.js +97 -0
  24. package/dist/ui/file-preview/src/document-workspace.d.ts +19 -0
  25. package/dist/ui/file-preview/src/document-workspace.js +33 -0
  26. package/dist/ui/file-preview/src/file-type-handlers.d.ts +10 -0
  27. package/dist/ui/file-preview/src/file-type-handlers.js +98 -0
  28. package/dist/ui/file-preview/src/host/external-actions.d.ts +19 -0
  29. package/dist/ui/file-preview/src/host/external-actions.js +94 -0
  30. package/dist/ui/file-preview/src/host/selection-context.d.ts +9 -0
  31. package/dist/ui/file-preview/src/host/selection-context.js +106 -0
  32. package/dist/ui/file-preview/src/markdown/conflict-dialog.d.ts +40 -0
  33. package/dist/ui/file-preview/src/markdown/conflict-dialog.js +163 -0
  34. package/dist/ui/file-preview/src/markdown/controller.d.ts +44 -0
  35. package/dist/ui/file-preview/src/markdown/controller.js +1040 -0
  36. package/dist/ui/file-preview/src/markdown/editor.d.ts +131 -0
  37. package/dist/ui/file-preview/src/markdown/editor.js +1479 -0
  38. package/dist/ui/file-preview/src/markdown/linking.d.ts +16 -0
  39. package/dist/ui/file-preview/src/markdown/linking.js +228 -0
  40. package/dist/ui/file-preview/src/markdown/outline.d.ts +2 -0
  41. package/dist/ui/file-preview/src/markdown/outline.js +16 -0
  42. package/dist/ui/file-preview/src/markdown/parser.d.ts +30 -0
  43. package/dist/ui/file-preview/src/markdown/parser.js +38 -0
  44. package/dist/ui/file-preview/src/markdown/preview.d.ts +1 -0
  45. package/dist/ui/file-preview/src/markdown/preview.js +20 -0
  46. package/dist/ui/file-preview/src/markdown/slugify.d.ts +3 -0
  47. package/dist/ui/file-preview/src/markdown/slugify.js +31 -0
  48. package/dist/ui/file-preview/src/markdown/utils.d.ts +1 -0
  49. package/dist/ui/file-preview/src/markdown/utils.js +15 -0
  50. package/dist/ui/file-preview/src/model.d.ts +35 -0
  51. package/dist/ui/file-preview/src/panel-actions.d.ts +17 -0
  52. package/dist/ui/file-preview/src/panel-actions.js +182 -0
  53. package/dist/ui/file-preview/src/path-utils.d.ts +6 -0
  54. package/dist/ui/file-preview/src/path-utils.js +64 -0
  55. package/dist/ui/file-preview/src/payload-utils.d.ts +11 -0
  56. package/dist/ui/file-preview/src/payload-utils.js +94 -0
  57. package/dist/ui/file-preview/styles.css +1066 -233
  58. package/dist/ui/shared/widget-state.d.ts +6 -1
  59. package/dist/ui/shared/widget-state.js +102 -4
  60. package/dist/utils/capture.js +1 -1
  61. package/dist/utils/files/base.d.ts +2 -0
  62. package/dist/utils/open-browser.js +1 -1
  63. package/dist/utils/toolHistory.d.ts +13 -0
  64. package/dist/utils/toolHistory.js +65 -0
  65. package/dist/version.d.ts +1 -1
  66. package/dist/version.js +1 -1
  67. package/package.json +12 -1
  68. package/dist/data/spec-kit-prompts.json +0 -123
  69. package/dist/handlers/macos-control-handlers.d.ts +0 -16
  70. package/dist/handlers/macos-control-handlers.js +0 -81
  71. package/dist/handlers/node-handlers.d.ts +0 -6
  72. package/dist/handlers/node-handlers.js +0 -73
  73. package/dist/handlers/test-crash-handler.d.ts +0 -11
  74. package/dist/handlers/test-crash-handler.js +0 -26
  75. package/dist/http-index.d.ts +0 -45
  76. package/dist/http-index.js +0 -51
  77. package/dist/http-server-auto-tunnel.js +0 -667
  78. package/dist/http-server-named-tunnel.d.ts +0 -2
  79. package/dist/http-server-named-tunnel.js +0 -167
  80. package/dist/http-server-tunnel.d.ts +0 -2
  81. package/dist/http-server-tunnel.js +0 -111
  82. package/dist/http-server.d.ts +0 -2
  83. package/dist/http-server.js +0 -270
  84. package/dist/index-oauth.d.ts +0 -2
  85. package/dist/index-oauth.js +0 -201
  86. package/dist/lib.d.ts +0 -10
  87. package/dist/lib.js +0 -10
  88. package/dist/oauth/auth-middleware.d.ts +0 -20
  89. package/dist/oauth/auth-middleware.js +0 -62
  90. package/dist/oauth/index.d.ts +0 -3
  91. package/dist/oauth/index.js +0 -3
  92. package/dist/oauth/oauth-manager.d.ts +0 -80
  93. package/dist/oauth/oauth-manager.js +0 -179
  94. package/dist/oauth/oauth-routes.d.ts +0 -3
  95. package/dist/oauth/oauth-routes.js +0 -377
  96. package/dist/oauth/provider.d.ts +0 -22
  97. package/dist/oauth/provider.js +0 -124
  98. package/dist/oauth/server.d.ts +0 -18
  99. package/dist/oauth/server.js +0 -160
  100. package/dist/oauth/types.d.ts +0 -54
  101. package/dist/oauth/types.js +0 -2
  102. package/dist/remote-device/templates/auth-success.d.ts +0 -1
  103. package/dist/remote-device/templates/auth-success.js +0 -30
  104. package/dist/setup.log +0 -275
  105. package/dist/test-docx.d.ts +0 -1
  106. package/dist/test-setup.js +0 -14
  107. package/dist/tools/docx/builders/html-builder.d.ts +0 -17
  108. package/dist/tools/docx/builders/html-builder.js +0 -92
  109. package/dist/tools/docx/builders/image.d.ts +0 -14
  110. package/dist/tools/docx/builders/image.js +0 -84
  111. package/dist/tools/docx/builders/index.d.ts +0 -11
  112. package/dist/tools/docx/builders/index.js +0 -11
  113. package/dist/tools/docx/builders/markdown-builder.d.ts +0 -2
  114. package/dist/tools/docx/builders/markdown-builder.js +0 -260
  115. package/dist/tools/docx/builders/paragraph.d.ts +0 -12
  116. package/dist/tools/docx/builders/paragraph.js +0 -29
  117. package/dist/tools/docx/builders/table.d.ts +0 -10
  118. package/dist/tools/docx/builders/table.js +0 -138
  119. package/dist/tools/docx/builders/utils.d.ts +0 -5
  120. package/dist/tools/docx/builders/utils.js +0 -18
  121. package/dist/tools/docx/constants.d.ts +0 -32
  122. package/dist/tools/docx/constants.js +0 -61
  123. package/dist/tools/docx/converters/markdown-to-html.d.ts +0 -17
  124. package/dist/tools/docx/converters/markdown-to-html.js +0 -111
  125. package/dist/tools/docx/create.d.ts +0 -21
  126. package/dist/tools/docx/create.js +0 -386
  127. package/dist/tools/docx/dom.d.ts +0 -139
  128. package/dist/tools/docx/dom.js +0 -448
  129. package/dist/tools/docx/errors.d.ts +0 -28
  130. package/dist/tools/docx/errors.js +0 -48
  131. package/dist/tools/docx/extractors/images.d.ts +0 -14
  132. package/dist/tools/docx/extractors/images.js +0 -40
  133. package/dist/tools/docx/extractors/metadata.d.ts +0 -14
  134. package/dist/tools/docx/extractors/metadata.js +0 -64
  135. package/dist/tools/docx/extractors/sections.d.ts +0 -14
  136. package/dist/tools/docx/extractors/sections.js +0 -61
  137. package/dist/tools/docx/html.d.ts +0 -17
  138. package/dist/tools/docx/html.js +0 -111
  139. package/dist/tools/docx/index.d.ts +0 -10
  140. package/dist/tools/docx/index.js +0 -10
  141. package/dist/tools/docx/markdown.d.ts +0 -84
  142. package/dist/tools/docx/markdown.js +0 -507
  143. package/dist/tools/docx/modify.d.ts +0 -28
  144. package/dist/tools/docx/modify.js +0 -271
  145. package/dist/tools/docx/operations/handlers/index.d.ts +0 -39
  146. package/dist/tools/docx/operations/handlers/index.js +0 -152
  147. package/dist/tools/docx/operations/html-manipulator.d.ts +0 -24
  148. package/dist/tools/docx/operations/html-manipulator.js +0 -352
  149. package/dist/tools/docx/operations/index.d.ts +0 -14
  150. package/dist/tools/docx/operations/index.js +0 -61
  151. package/dist/tools/docx/operations/operation-handlers.d.ts +0 -3
  152. package/dist/tools/docx/operations/operation-handlers.js +0 -67
  153. package/dist/tools/docx/operations/preprocessor.d.ts +0 -14
  154. package/dist/tools/docx/operations/preprocessor.js +0 -44
  155. package/dist/tools/docx/operations/xml-replacer.d.ts +0 -9
  156. package/dist/tools/docx/operations/xml-replacer.js +0 -35
  157. package/dist/tools/docx/operations.d.ts +0 -13
  158. package/dist/tools/docx/operations.js +0 -13
  159. package/dist/tools/docx/ops/delete-paragraph-at-body-index.d.ts +0 -11
  160. package/dist/tools/docx/ops/delete-paragraph-at-body-index.js +0 -23
  161. package/dist/tools/docx/ops/header-replace-text-exact.d.ts +0 -13
  162. package/dist/tools/docx/ops/header-replace-text-exact.js +0 -55
  163. package/dist/tools/docx/ops/index.d.ts +0 -17
  164. package/dist/tools/docx/ops/index.js +0 -70
  165. package/dist/tools/docx/ops/insert-image-after-text.d.ts +0 -24
  166. package/dist/tools/docx/ops/insert-image-after-text.js +0 -128
  167. package/dist/tools/docx/ops/insert-paragraph-after-text.d.ts +0 -12
  168. package/dist/tools/docx/ops/insert-paragraph-after-text.js +0 -74
  169. package/dist/tools/docx/ops/insert-table-after-text.d.ts +0 -19
  170. package/dist/tools/docx/ops/insert-table-after-text.js +0 -57
  171. package/dist/tools/docx/ops/replace-hyperlink-url.d.ts +0 -12
  172. package/dist/tools/docx/ops/replace-hyperlink-url.js +0 -37
  173. package/dist/tools/docx/ops/replace-paragraph-at-body-index.d.ts +0 -9
  174. package/dist/tools/docx/ops/replace-paragraph-at-body-index.js +0 -25
  175. package/dist/tools/docx/ops/replace-paragraph-text-exact.d.ts +0 -21
  176. package/dist/tools/docx/ops/replace-paragraph-text-exact.js +0 -36
  177. package/dist/tools/docx/ops/replace-table-cell-text.d.ts +0 -25
  178. package/dist/tools/docx/ops/replace-table-cell-text.js +0 -85
  179. package/dist/tools/docx/ops/set-color-for-paragraph-exact.d.ts +0 -9
  180. package/dist/tools/docx/ops/set-color-for-paragraph-exact.js +0 -24
  181. package/dist/tools/docx/ops/set-color-for-style.d.ts +0 -13
  182. package/dist/tools/docx/ops/set-color-for-style.js +0 -31
  183. package/dist/tools/docx/ops/set-paragraph-style-at-body-index.d.ts +0 -8
  184. package/dist/tools/docx/ops/set-paragraph-style-at-body-index.js +0 -57
  185. package/dist/tools/docx/ops/table-set-cell-text.d.ts +0 -9
  186. package/dist/tools/docx/ops/table-set-cell-text.js +0 -40
  187. package/dist/tools/docx/parsers/image-extractor.d.ts +0 -18
  188. package/dist/tools/docx/parsers/image-extractor.js +0 -61
  189. package/dist/tools/docx/parsers/index.d.ts +0 -9
  190. package/dist/tools/docx/parsers/index.js +0 -9
  191. package/dist/tools/docx/parsers/paragraph-parser.d.ts +0 -2
  192. package/dist/tools/docx/parsers/paragraph-parser.js +0 -88
  193. package/dist/tools/docx/parsers/table-parser.d.ts +0 -9
  194. package/dist/tools/docx/parsers/table-parser.js +0 -72
  195. package/dist/tools/docx/parsers/xml-parser.d.ts +0 -25
  196. package/dist/tools/docx/parsers/xml-parser.js +0 -71
  197. package/dist/tools/docx/parsers/zip-reader.d.ts +0 -23
  198. package/dist/tools/docx/parsers/zip-reader.js +0 -52
  199. package/dist/tools/docx/read.d.ts +0 -27
  200. package/dist/tools/docx/read.js +0 -308
  201. package/dist/tools/docx/relationships.d.ts +0 -22
  202. package/dist/tools/docx/relationships.js +0 -76
  203. package/dist/tools/docx/structure.d.ts +0 -25
  204. package/dist/tools/docx/structure.js +0 -102
  205. package/dist/tools/docx/styled-html-parser.d.ts +0 -23
  206. package/dist/tools/docx/styled-html-parser.js +0 -1262
  207. package/dist/tools/docx/types.d.ts +0 -213
  208. package/dist/tools/docx/types.js +0 -5
  209. package/dist/tools/docx/utils/escaping.d.ts +0 -13
  210. package/dist/tools/docx/utils/escaping.js +0 -26
  211. package/dist/tools/docx/utils/images.d.ts +0 -9
  212. package/dist/tools/docx/utils/images.js +0 -26
  213. package/dist/tools/docx/utils/index.d.ts +0 -12
  214. package/dist/tools/docx/utils/index.js +0 -17
  215. package/dist/tools/docx/utils/markdown.d.ts +0 -13
  216. package/dist/tools/docx/utils/markdown.js +0 -32
  217. package/dist/tools/docx/utils/paths.d.ts +0 -15
  218. package/dist/tools/docx/utils/paths.js +0 -27
  219. package/dist/tools/docx/utils/versioning.d.ts +0 -25
  220. package/dist/tools/docx/utils/versioning.js +0 -55
  221. package/dist/tools/docx/utils.d.ts +0 -101
  222. package/dist/tools/docx/utils.js +0 -299
  223. package/dist/tools/docx/validate.d.ts +0 -33
  224. package/dist/tools/docx/validate.js +0 -49
  225. package/dist/tools/docx/validators.d.ts +0 -13
  226. package/dist/tools/docx/validators.js +0 -40
  227. package/dist/tools/docx/write.d.ts +0 -17
  228. package/dist/tools/docx/write.js +0 -88
  229. package/dist/tools/docx/xml-view-test.d.ts +0 -1
  230. package/dist/tools/docx/xml-view-test.js +0 -63
  231. package/dist/tools/docx/xml-view.d.ts +0 -56
  232. package/dist/tools/docx/xml-view.js +0 -169
  233. package/dist/tools/docx/zip.d.ts +0 -21
  234. package/dist/tools/docx/zip.js +0 -35
  235. package/dist/tools/macos-control/ax-adapter.d.ts +0 -55
  236. package/dist/tools/macos-control/ax-adapter.js +0 -438
  237. package/dist/tools/macos-control/cdp-adapter.d.ts +0 -23
  238. package/dist/tools/macos-control/cdp-adapter.js +0 -402
  239. package/dist/tools/macos-control/orchestrator.d.ts +0 -77
  240. package/dist/tools/macos-control/orchestrator.js +0 -136
  241. package/dist/tools/macos-control/role-aliases.d.ts +0 -5
  242. package/dist/tools/macos-control/role-aliases.js +0 -34
  243. package/dist/tools/macos-control/types.d.ts +0 -129
  244. package/dist/tools/macos-control/types.js +0 -1
  245. package/dist/tools/pdf-processor.d.ts +0 -1
  246. package/dist/tools/pdf-processor.js +0 -3
  247. package/dist/tools/search.d.ts +0 -32
  248. package/dist/tools/search.js +0 -202
  249. package/dist/ui/file-preview/src/components/toolbar.d.ts +0 -6
  250. package/dist/ui/file-preview/src/components/toolbar.js +0 -75
  251. package/dist/ui/shared/host-lifecycle.d.ts +0 -16
  252. package/dist/ui/shared/host-lifecycle.js +0 -35
  253. package/dist/ui/shared/rpc-client.d.ts +0 -14
  254. package/dist/ui/shared/rpc-client.js +0 -72
  255. package/dist/ui/shared/theme-adaptation.d.ts +0 -10
  256. package/dist/ui/shared/theme-adaptation.js +0 -118
  257. package/dist/ui/shared/tool-header.d.ts +0 -9
  258. package/dist/ui/shared/tool-header.js +0 -25
  259. package/dist/utils/crash-logger.d.ts +0 -18
  260. package/dist/utils/crash-logger.js +0 -44
  261. package/dist/utils/dedent.d.ts +0 -8
  262. package/dist/utils/dedent.js +0 -38
  263. /package/dist/{http-server-auto-tunnel.d.ts → ui/file-preview/src/model.js} +0 -0
@@ -0,0 +1,16 @@
1
+ export interface ResolvedMarkdownLink {
2
+ kind: 'external' | 'anchor' | 'file';
3
+ href: string;
4
+ url?: string;
5
+ targetPath?: string;
6
+ anchor?: string;
7
+ }
8
+ /**
9
+ * Invert `rewriteWikiLinks`: convert `[alias](href "mcp-wiki:ENCODED")` links
10
+ * back to their original `[[...]]` form. Used when serializing a WYSIWYG
11
+ * edit session back to markdown — the `mcp-wiki:` title prefix is the
12
+ * round-trip marker written by `rewriteWikiLinks`.
13
+ */
14
+ export declare function restoreWikiLinks(markdown: string): string;
15
+ export declare function rewriteWikiLinks(source: string): string;
16
+ export declare function resolveMarkdownLink(currentPath: string, rawHref: string): ResolvedMarkdownLink;
@@ -0,0 +1,228 @@
1
+ import { slugifyMarkdownHeading } from './slugify.js';
2
+ import { getParentDirectory, isWindowsAbsolutePath, normalizeFilePath, normalizePathSeparators } from '../path-utils.js';
3
+ const WIKI_LINK_PATTERN = /\[\[([^\]|#]*)(?:#([^\]|]+))?(?:\|([^\]]+))?\]\]/g;
4
+ const FENCE_PATTERN = /^(`{3,}|~{3,})/;
5
+ function encodeLinkPath(pathValue) {
6
+ return encodeURI(normalizePathSeparators(pathValue));
7
+ }
8
+ function safeDecodeURIComponent(value) {
9
+ try {
10
+ return decodeURIComponent(value);
11
+ }
12
+ catch {
13
+ return value;
14
+ }
15
+ }
16
+ function parseWikiLink(rawHref) {
17
+ const match = rawHref.match(/^\[\[([^\]|#]*)(?:#([^\]|]+))?(?:\|([^\]]+))?\]\]$/);
18
+ if (!match) {
19
+ return null;
20
+ }
21
+ return {
22
+ path: (match[1] ?? '').trim(),
23
+ anchor: match[2]?.trim(),
24
+ alias: match[3]?.trim(),
25
+ };
26
+ }
27
+ function buildWikiDisplayText(link) {
28
+ if (link.alias && link.alias.length > 0) {
29
+ return link.alias;
30
+ }
31
+ if (link.path && link.anchor) {
32
+ return `${link.path}#${link.anchor}`;
33
+ }
34
+ if (link.path) {
35
+ return link.path;
36
+ }
37
+ return link.anchor ?? '';
38
+ }
39
+ function appendMarkdownExtension(pathValue) {
40
+ if (/\.[A-Za-z0-9_-]+$/.test(pathValue)) {
41
+ return pathValue;
42
+ }
43
+ return `${pathValue}.md`;
44
+ }
45
+ function buildWikiHref(link) {
46
+ if (!link.path) {
47
+ if (!link.anchor) {
48
+ return '#';
49
+ }
50
+ return `#${slugifyMarkdownHeading(link.anchor)}`;
51
+ }
52
+ const normalizedPath = appendMarkdownExtension(normalizePathSeparators(link.path));
53
+ const prefixedPath = normalizedPath.startsWith('./')
54
+ || normalizedPath.startsWith('../')
55
+ || normalizedPath.startsWith('/')
56
+ || isWindowsAbsolutePath(normalizedPath)
57
+ ? normalizedPath
58
+ : `./${normalizedPath}`;
59
+ const encodedPath = encodeLinkPath(prefixedPath);
60
+ if (!link.anchor) {
61
+ return encodedPath;
62
+ }
63
+ return `${encodedPath}#${slugifyMarkdownHeading(link.anchor)}`;
64
+ }
65
+ function rewriteWikiLinksInPlainText(segment) {
66
+ return segment.replace(WIKI_LINK_PATTERN, (match) => {
67
+ const parsed = parseWikiLink(match);
68
+ if (!parsed) {
69
+ return match;
70
+ }
71
+ const displayText = buildWikiDisplayText(parsed);
72
+ const href = buildWikiHref(parsed);
73
+ return `[${displayText}](${href} "mcp-wiki:${encodeURIComponent(match)}")`;
74
+ });
75
+ }
76
+ function replaceWikiLinksOutsideInlineCode(line) {
77
+ let result = '';
78
+ let cursor = 0;
79
+ while (cursor < line.length) {
80
+ const codeStart = line.indexOf('`', cursor);
81
+ if (codeStart === -1) {
82
+ result += rewriteWikiLinksInPlainText(line.slice(cursor));
83
+ break;
84
+ }
85
+ result += rewriteWikiLinksInPlainText(line.slice(cursor, codeStart));
86
+ let delimiterEnd = codeStart;
87
+ while (delimiterEnd < line.length && line[delimiterEnd] === '`') {
88
+ delimiterEnd += 1;
89
+ }
90
+ const delimiter = line.slice(codeStart, delimiterEnd);
91
+ const codeEnd = line.indexOf(delimiter, delimiterEnd);
92
+ if (codeEnd === -1) {
93
+ result += line.slice(codeStart);
94
+ break;
95
+ }
96
+ result += line.slice(codeStart, codeEnd + delimiter.length);
97
+ cursor = codeEnd + delimiter.length;
98
+ }
99
+ return result;
100
+ }
101
+ function decodeAnchorFragment(fragment) {
102
+ if (!fragment || fragment.length === 0) {
103
+ return undefined;
104
+ }
105
+ return safeDecodeURIComponent(fragment);
106
+ }
107
+ function splitHref(rawHref) {
108
+ const hashIndex = rawHref.indexOf('#');
109
+ if (hashIndex === -1) {
110
+ return { pathPart: rawHref };
111
+ }
112
+ return {
113
+ pathPart: rawHref.slice(0, hashIndex),
114
+ anchorPart: rawHref.slice(hashIndex + 1),
115
+ };
116
+ }
117
+ function toDirectoryFileUrl(directoryPath) {
118
+ const normalized = normalizeFilePath(directoryPath);
119
+ const withTrailingSlash = normalized.endsWith('/') ? normalized : `${normalized}/`;
120
+ if (isWindowsAbsolutePath(withTrailingSlash)) {
121
+ return new URL(`file:///${encodeLinkPath(withTrailingSlash)}`);
122
+ }
123
+ if (withTrailingSlash.startsWith('/')) {
124
+ return new URL(`file://${encodeLinkPath(withTrailingSlash)}`);
125
+ }
126
+ return new URL(`file:///${encodeLinkPath(withTrailingSlash)}`);
127
+ }
128
+ function fromFileUrl(url) {
129
+ const decodedPath = safeDecodeURIComponent(url.pathname);
130
+ if (/^\/[A-Za-z]:\//.test(decodedPath)) {
131
+ return decodedPath.slice(1);
132
+ }
133
+ return decodedPath;
134
+ }
135
+ function isExternalHref(rawHref) {
136
+ return /^[A-Za-z][A-Za-z0-9+.-]*:/.test(rawHref) && !isWindowsAbsolutePath(rawHref);
137
+ }
138
+ function resolveFileTargetPath(currentPath, rawPath) {
139
+ const normalizedRawPath = normalizePathSeparators(safeDecodeURIComponent(rawPath));
140
+ if (normalizedRawPath.startsWith('/') || isWindowsAbsolutePath(normalizedRawPath)) {
141
+ return normalizeFilePath(normalizedRawPath);
142
+ }
143
+ const baseDirectory = getParentDirectory(currentPath);
144
+ if (baseDirectory === '.' && !normalizeFilePath(currentPath).includes('/')) {
145
+ return normalizeFilePath(normalizedRawPath);
146
+ }
147
+ const resolvedUrl = new URL(encodeURI(normalizedRawPath), toDirectoryFileUrl(baseDirectory));
148
+ return normalizeFilePath(fromFileUrl(resolvedUrl));
149
+ }
150
+ /**
151
+ * Invert `rewriteWikiLinks`: convert `[alias](href "mcp-wiki:ENCODED")` links
152
+ * back to their original `[[...]]` form. Used when serializing a WYSIWYG
153
+ * edit session back to markdown — the `mcp-wiki:` title prefix is the
154
+ * round-trip marker written by `rewriteWikiLinks`.
155
+ */
156
+ export function restoreWikiLinks(markdown) {
157
+ return markdown.replace(/\[([^\]]*)\]\(([^)\s]*)(?:\s+"mcp-wiki:([^"]+)")\)/g, (_, _alias, _href, encoded) => {
158
+ try {
159
+ return decodeURIComponent(encoded);
160
+ }
161
+ catch {
162
+ return `[[${encoded}]]`;
163
+ }
164
+ });
165
+ }
166
+ export function rewriteWikiLinks(source) {
167
+ const lines = source.split('\n');
168
+ let activeFence = null;
169
+ return lines.map((line) => {
170
+ const trimmedStart = line.trimStart();
171
+ const fenceMatch = trimmedStart.match(FENCE_PATTERN);
172
+ if (fenceMatch) {
173
+ const marker = fenceMatch[1];
174
+ if (!activeFence) {
175
+ activeFence = marker;
176
+ }
177
+ else if (marker[0] === activeFence[0] && marker.length >= activeFence.length) {
178
+ activeFence = null;
179
+ }
180
+ return line;
181
+ }
182
+ if (activeFence) {
183
+ return line;
184
+ }
185
+ return replaceWikiLinksOutsideInlineCode(line);
186
+ }).join('\n');
187
+ }
188
+ export function resolveMarkdownLink(currentPath, rawHref) {
189
+ const wikiLink = parseWikiLink(rawHref);
190
+ if (wikiLink) {
191
+ const href = buildWikiHref(wikiLink);
192
+ if (href.startsWith('#')) {
193
+ return {
194
+ kind: 'anchor',
195
+ href: rawHref,
196
+ anchor: decodeAnchorFragment(href.slice(1)),
197
+ };
198
+ }
199
+ const [pathPart, anchorPart] = href.split('#');
200
+ return {
201
+ kind: 'file',
202
+ href: rawHref,
203
+ targetPath: resolveFileTargetPath(currentPath, pathPart),
204
+ ...(anchorPart ? { anchor: decodeAnchorFragment(anchorPart) } : {}),
205
+ };
206
+ }
207
+ if (isExternalHref(rawHref)) {
208
+ return {
209
+ kind: 'external',
210
+ href: rawHref,
211
+ url: rawHref,
212
+ };
213
+ }
214
+ if (rawHref.startsWith('#')) {
215
+ return {
216
+ kind: 'anchor',
217
+ href: rawHref,
218
+ anchor: decodeAnchorFragment(rawHref.slice(1)),
219
+ };
220
+ }
221
+ const { pathPart, anchorPart } = splitHref(rawHref);
222
+ return {
223
+ kind: 'file',
224
+ href: rawHref,
225
+ targetPath: resolveFileTargetPath(currentPath, pathPart),
226
+ ...(anchorPart ? { anchor: decodeAnchorFragment(anchorPart) } : {}),
227
+ };
228
+ }
@@ -0,0 +1,2 @@
1
+ import type { DocumentOutlineItem } from '../document-outline.js';
2
+ export declare function extractMarkdownOutline(source: string): DocumentOutlineItem[];
@@ -0,0 +1,16 @@
1
+ import { createMarkdownIt, prepareMarkdownSource, readHeadingProjection } from './parser.js';
2
+ import { createSlugTracker } from './slugify.js';
3
+ const outlineParser = createMarkdownIt();
4
+ export function extractMarkdownOutline(source) {
5
+ const tokens = outlineParser.parse(prepareMarkdownSource(source), {});
6
+ const nextSlug = createSlugTracker();
7
+ const outline = [];
8
+ for (let index = 0; index < tokens.length; index += 1) {
9
+ const heading = readHeadingProjection(tokens, index, nextSlug);
10
+ if (!heading) {
11
+ continue;
12
+ }
13
+ outline.push(heading);
14
+ }
15
+ return outline;
16
+ }
@@ -0,0 +1,30 @@
1
+ import type { MarkdownSlugTracker } from './slugify.js';
2
+ export interface MarkdownToken {
3
+ type?: string;
4
+ tag?: string;
5
+ map?: number[];
6
+ children?: unknown;
7
+ content?: unknown;
8
+ attrSet?: (name: string, value: string) => void;
9
+ attrGet?: (name: string) => string | null;
10
+ attrs?: Array<[string, string]>;
11
+ }
12
+ interface MarkdownItInstance {
13
+ render: (source: string, env?: Record<string, unknown>) => string;
14
+ parse: (source: string, env?: Record<string, unknown>) => MarkdownToken[];
15
+ renderer: {
16
+ rules: Record<string, (...args: unknown[]) => string>;
17
+ };
18
+ }
19
+ export interface MarkdownHeadingProjection {
20
+ id: string;
21
+ text: string;
22
+ level: number;
23
+ line: number;
24
+ }
25
+ export declare function createMarkdownIt(options?: {
26
+ highlight?: (code: string, language: string) => string;
27
+ }): MarkdownItInstance;
28
+ export declare function prepareMarkdownSource(source: string): string;
29
+ export declare function readHeadingProjection(tokens: MarkdownToken[], index: number, nextSlug: MarkdownSlugTracker): MarkdownHeadingProjection | null;
30
+ export {};
@@ -0,0 +1,38 @@
1
+ // markdown-it is intentionally typed locally here to avoid maintaining ambient module declarations.
2
+ import MarkdownIt from 'markdown-it';
3
+ import { rewriteWikiLinks } from './linking.js';
4
+ import { extractInlineText } from './utils.js';
5
+ const MarkdownItCtor = MarkdownIt;
6
+ export function createMarkdownIt(options = {}) {
7
+ return new MarkdownItCtor({
8
+ html: false,
9
+ linkify: true,
10
+ typographer: false,
11
+ ...(options.highlight ? { highlight: options.highlight } : {}),
12
+ });
13
+ }
14
+ export function prepareMarkdownSource(source) {
15
+ return rewriteWikiLinks(source);
16
+ }
17
+ export function readHeadingProjection(tokens, index, nextSlug) {
18
+ const token = tokens[index];
19
+ if (token?.type !== 'heading_open' || typeof token.tag !== 'string') {
20
+ return null;
21
+ }
22
+ const level = Number.parseInt(token.tag.replace(/^h/i, ''), 10);
23
+ if (!Number.isFinite(level)) {
24
+ return null;
25
+ }
26
+ const inlineToken = tokens[index + 1];
27
+ const text = extractInlineText(inlineToken).trim();
28
+ if (!text) {
29
+ return null;
30
+ }
31
+ const lineMap = Array.isArray(token.map) ? token.map : undefined;
32
+ return {
33
+ id: nextSlug(text),
34
+ text,
35
+ level,
36
+ line: typeof lineMap?.[0] === 'number' ? lineMap[0] + 1 : index + 1,
37
+ };
38
+ }
@@ -0,0 +1 @@
1
+ export declare function getRenderedMarkdownCopyText(content: string): string;
@@ -0,0 +1,20 @@
1
+ import { renderMarkdown } from '../components/markdown-renderer.js';
2
+ export function getRenderedMarkdownCopyText(content) {
3
+ const html = renderMarkdown(content);
4
+ const normalizedHtml = html
5
+ .replace(/<\s*br\s*\/?>/gi, '\n')
6
+ .replace(/<\/p>/gi, '\n\n')
7
+ .replace(/<\/h[1-6]>/gi, '\n\n')
8
+ .replace(/<\/li>/gi, '\n')
9
+ .replace(/<li>/gi, '- ')
10
+ .replace(/<[^>]+>/g, '');
11
+ return normalizedHtml
12
+ .replace(/&nbsp;/g, ' ')
13
+ .replace(/&amp;/g, '&')
14
+ .replace(/&lt;/g, '<')
15
+ .replace(/&gt;/g, '>')
16
+ .replace(/&#39;/g, "'")
17
+ .replace(/&quot;/g, '"')
18
+ .replace(/\n{3,}/g, '\n\n')
19
+ .trim();
20
+ }
@@ -0,0 +1,3 @@
1
+ export type MarkdownSlugTracker = (text: string) => string;
2
+ export declare function slugifyMarkdownHeading(text: string): string;
3
+ export declare function createSlugTracker(): MarkdownSlugTracker;
@@ -0,0 +1,31 @@
1
+ function sanitizeSlugPart(text) {
2
+ const normalized = text
3
+ .normalize('NFKD')
4
+ .replace(/[\u0300-\u036f]/g, '')
5
+ .toLowerCase()
6
+ .trim()
7
+ .replace(/[^a-z0-9\s-]/g, ' ')
8
+ .replace(/\s+/g, '-')
9
+ .replace(/-+/g, '-')
10
+ .replace(/^-+|-+$/g, '');
11
+ return normalized.length > 0 ? normalized : 'section';
12
+ }
13
+ export function slugifyMarkdownHeading(text) {
14
+ return sanitizeSlugPart(text);
15
+ }
16
+ export function createSlugTracker() {
17
+ const counts = new Map();
18
+ const usedSlugs = new Set();
19
+ return (text) => {
20
+ const baseSlug = slugifyMarkdownHeading(text);
21
+ let nextCount = counts.get(baseSlug) ?? 1;
22
+ let nextSlug = nextCount === 1 ? baseSlug : `${baseSlug}-${nextCount}`;
23
+ while (usedSlugs.has(nextSlug)) {
24
+ nextCount += 1;
25
+ nextSlug = `${baseSlug}-${nextCount}`;
26
+ }
27
+ counts.set(baseSlug, nextCount + 1);
28
+ usedSlugs.add(nextSlug);
29
+ return nextSlug;
30
+ };
31
+ }
@@ -0,0 +1 @@
1
+ export declare function extractInlineText(token: Record<string, unknown> | undefined): string;
@@ -0,0 +1,15 @@
1
+ export function extractInlineText(token) {
2
+ if (!token) {
3
+ return '';
4
+ }
5
+ const children = Array.isArray(token.children) ? token.children : [];
6
+ if (children.length === 0) {
7
+ return typeof token.content === 'string' ? token.content : '';
8
+ }
9
+ return children.map((child) => {
10
+ if (typeof child.content === 'string') {
11
+ return child.content;
12
+ }
13
+ return '';
14
+ }).join('');
15
+ }
@@ -0,0 +1,35 @@
1
+ import type { DocumentOutlineItem } from './document-outline.js';
2
+ import type { FilePreviewStructuredContent } from '../../../types.js';
3
+ import type { MarkdownEditRange, MarkdownEditorView } from './markdown/editor.js';
4
+ export type RenderPayload = FilePreviewStructuredContent & {
5
+ content: string;
6
+ };
7
+ export interface MarkdownWorkspaceState {
8
+ filePath: string;
9
+ sourceContent: string;
10
+ fullDocumentContent: string;
11
+ draftContent: string;
12
+ outline: DocumentOutlineItem[];
13
+ mode: 'edit';
14
+ dirty: boolean;
15
+ dirtyLineRanges: MarkdownEditRange[];
16
+ activeHeadingId: string | null;
17
+ pendingAnchor: string | null;
18
+ notice: string | null;
19
+ error: string | null;
20
+ saving: boolean;
21
+ loadingDocument: boolean;
22
+ editorView: MarkdownEditorView;
23
+ editorScrollTop: number;
24
+ saveIndicator: 'idle' | 'saving' | 'saved';
25
+ fileDeleted: boolean;
26
+ }
27
+ export interface RenderBodyResult {
28
+ html: string;
29
+ notice?: string;
30
+ }
31
+ export interface FileTypeCapabilities {
32
+ supportsPreview: boolean;
33
+ canCopy: boolean;
34
+ canOpenInFolder: boolean;
35
+ }
@@ -0,0 +1,17 @@
1
+ import type { RenderPayload } from './model.js';
2
+ import type { MarkdownController } from './markdown/controller.js';
3
+ import type { HtmlPreviewMode } from './types.js';
4
+ export declare function attachPanelActions(options: {
5
+ container: HTMLElement;
6
+ payload: RenderPayload;
7
+ htmlMode: HtmlPreviewMode;
8
+ getIsExpanded: () => boolean;
9
+ callTool?: (name: string, args: Record<string, unknown>) => Promise<unknown | undefined>;
10
+ trackUiEvent?: (event: string, params?: Record<string, unknown>) => void;
11
+ getFileExtensionForAnalytics: (filePath: string) => string;
12
+ buildOpenInFolderCommand: (filePath: string) => string | undefined;
13
+ buildOpenInEditorCommand: (filePath: string) => string | undefined;
14
+ render: (payload?: RenderPayload, htmlMode?: HtmlPreviewMode, expandedState?: boolean) => void;
15
+ updateSaveStatus: (label: string, statusClass: string) => void;
16
+ markdownController: MarkdownController;
17
+ }): void;
@@ -0,0 +1,182 @@
1
+ import { parseReadRange, stripReadStatusLine } from './document-workspace.js';
2
+ import { extractToolText } from './payload-utils.js';
3
+ export function attachPanelActions(options) {
4
+ const queryById = (id) => (options.container.querySelector(`#${id}`));
5
+ const fallbackCopy = (text) => {
6
+ const textArea = document.createElement('textarea');
7
+ textArea.value = text;
8
+ textArea.setAttribute('readonly', '');
9
+ textArea.style.position = 'fixed';
10
+ textArea.style.top = '-9999px';
11
+ document.body.appendChild(textArea);
12
+ textArea.select();
13
+ const success = document.execCommand('copy');
14
+ document.body.removeChild(textArea);
15
+ return success;
16
+ };
17
+ const setButtonState = (button, label, fallbackLabel, revertMs) => {
18
+ button.setAttribute('title', label);
19
+ button.setAttribute('aria-label', label);
20
+ button.textContent = label;
21
+ if (revertMs) {
22
+ setTimeout(() => {
23
+ button.textContent = fallbackLabel;
24
+ button.setAttribute('title', fallbackLabel);
25
+ button.setAttribute('aria-label', fallbackLabel);
26
+ }, revertMs);
27
+ }
28
+ };
29
+ const setIconButtonState = (button, label, fallbackLabel, revertMs) => {
30
+ button.setAttribute('title', label);
31
+ button.setAttribute('aria-label', label);
32
+ button.dataset.status = label;
33
+ if (revertMs) {
34
+ setTimeout(() => {
35
+ button.setAttribute('title', fallbackLabel);
36
+ button.setAttribute('aria-label', fallbackLabel);
37
+ delete button.dataset.status;
38
+ }, revertMs);
39
+ }
40
+ };
41
+ const copyTextData = async (text) => {
42
+ try {
43
+ if (navigator.clipboard?.writeText) {
44
+ await navigator.clipboard.writeText(text);
45
+ return true;
46
+ }
47
+ return fallbackCopy(text);
48
+ }
49
+ catch {
50
+ return fallbackCopy(text);
51
+ }
52
+ };
53
+ const fileExtension = options.getFileExtensionForAnalytics(options.payload.filePath);
54
+ const copyButton = queryById('copy-source');
55
+ copyButton?.addEventListener('click', async () => {
56
+ options.trackUiEvent?.('copy_clicked', {
57
+ file_type: options.payload.fileType,
58
+ file_extension: fileExtension,
59
+ });
60
+ const copied = await copyTextData(stripReadStatusLine(options.payload.content));
61
+ setButtonState(copyButton, copied ? 'Copied!' : 'Copy failed', 'Copy', 1500);
62
+ });
63
+ const activeCopyButton = queryById('copy-active-markdown');
64
+ activeCopyButton?.addEventListener('click', async () => {
65
+ const textToCopy = options.markdownController.getCopyText(options.payload);
66
+ if (!textToCopy) {
67
+ return;
68
+ }
69
+ const copied = await copyTextData(textToCopy);
70
+ if (copied) {
71
+ options.updateSaveStatus('Copied', 'saved');
72
+ window.setTimeout(() => options.updateSaveStatus('', ''), 1500);
73
+ }
74
+ setIconButtonState(activeCopyButton, copied ? 'Copied!' : 'Copy failed', 'Copy', 1500);
75
+ });
76
+ const toggleButton = queryById('toggle-html-mode');
77
+ toggleButton?.addEventListener('click', () => {
78
+ const nextMode = options.htmlMode === 'rendered' ? 'source' : 'rendered';
79
+ options.trackUiEvent?.('html_view_toggled', {
80
+ file_type: options.payload.fileType,
81
+ file_extension: fileExtension,
82
+ });
83
+ options.render(options.payload, nextMode, options.getIsExpanded());
84
+ });
85
+ const openFolderButton = queryById('open-in-folder');
86
+ if (openFolderButton) {
87
+ const command = options.buildOpenInFolderCommand(options.payload.filePath);
88
+ if (!command) {
89
+ openFolderButton.disabled = true;
90
+ }
91
+ else {
92
+ openFolderButton.addEventListener('click', async () => {
93
+ options.trackUiEvent?.('open_in_folder', {
94
+ file_type: options.payload.fileType,
95
+ file_extension: fileExtension,
96
+ });
97
+ try {
98
+ await options.callTool?.('start_process', { command, timeout_ms: 12000 });
99
+ }
100
+ catch {
101
+ // Keep UI stable if opening folder fails.
102
+ }
103
+ });
104
+ }
105
+ }
106
+ const openEditorButton = queryById('open-in-editor');
107
+ if (openEditorButton) {
108
+ const command = options.buildOpenInEditorCommand(options.payload.filePath);
109
+ if (!command) {
110
+ openEditorButton.disabled = true;
111
+ }
112
+ else {
113
+ openEditorButton.addEventListener('click', async () => {
114
+ options.trackUiEvent?.('open_in_editor', {
115
+ file_type: options.payload.fileType,
116
+ file_extension: fileExtension,
117
+ });
118
+ try {
119
+ await options.callTool?.('start_process', { command, timeout_ms: 12000 });
120
+ }
121
+ catch {
122
+ // Keep UI stable if opening editor fails.
123
+ }
124
+ });
125
+ }
126
+ }
127
+ const beforeBtn = queryById('load-before');
128
+ const afterBtn = queryById('load-after');
129
+ if (!beforeBtn && !afterBtn) {
130
+ return;
131
+ }
132
+ const range = parseReadRange(options.payload.content);
133
+ if (!range?.isPartial) {
134
+ return;
135
+ }
136
+ const currentContent = stripReadStatusLine(options.payload.content);
137
+ const loadLines = async (button, direction) => {
138
+ const originalText = button.textContent;
139
+ button.textContent = 'Loading…';
140
+ button.disabled = true;
141
+ options.trackUiEvent?.(direction === 'before' ? 'load_lines_before' : 'load_lines_after', {
142
+ file_type: options.payload.fileType,
143
+ file_extension: fileExtension,
144
+ });
145
+ try {
146
+ const readArgs = direction === 'before'
147
+ ? { path: options.payload.filePath, offset: 0, length: range.fromLine - 1 }
148
+ : { path: options.payload.filePath, offset: range.toLine };
149
+ const result = await options.callTool?.('read_file', readArgs);
150
+ const newText = extractToolText(result);
151
+ if (newText && typeof newText === 'string') {
152
+ const cleanNew = stripReadStatusLine(newText);
153
+ const merged = direction === 'before'
154
+ ? `${cleanNew}${cleanNew.endsWith('\n') ? '' : '\n'}${currentContent}`
155
+ : `${currentContent}${currentContent.endsWith('\n') ? '' : '\n'}${cleanNew}`;
156
+ const newFrom = direction === 'before' ? 1 : range.fromLine;
157
+ const newTo = direction === 'after' ? range.totalLines : range.toLine;
158
+ const lineCount = newTo - newFrom + 1;
159
+ const remaining = range.totalLines - newTo;
160
+ const isStillPartial = newFrom > 1 || newTo < range.totalLines;
161
+ const statusLine = isStillPartial
162
+ ? `[Reading ${lineCount} lines from ${newFrom === 1 ? 'start' : `line ${newFrom}`} (total: ${range.totalLines} lines, ${remaining} remaining)]\n`
163
+ : '';
164
+ options.render({
165
+ ...options.payload,
166
+ content: statusLine + merged,
167
+ }, options.htmlMode, options.getIsExpanded());
168
+ return;
169
+ }
170
+ }
171
+ catch {
172
+ // Fall through to button reset.
173
+ }
174
+ button.textContent = 'Failed to load';
175
+ setTimeout(() => {
176
+ button.textContent = originalText;
177
+ button.disabled = false;
178
+ }, 2000);
179
+ };
180
+ beforeBtn?.addEventListener('click', () => void loadLines(beforeBtn, 'before'));
181
+ afterBtn?.addEventListener('click', () => void loadLines(afterBtn, 'after'));
182
+ }
@@ -0,0 +1,6 @@
1
+ export declare function isWindowsAbsolutePath(value: string): boolean;
2
+ export declare function normalizePathSeparators(value: string): string;
3
+ export declare function normalizeFilePath(value: string): string;
4
+ export declare function getParentDirectory(filePath: string): string;
5
+ export declare function getAncestorDirectories(filePath: string): string[];
6
+ export declare function toPosixRelativePath(fromDirectory: string, targetPath: string): string;