@nextclaw/ui 0.14.4 → 0.15.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 (194) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/assets/api-DLtsiBKm.js +13 -0
  3. package/dist/assets/appearance-settings-page-CtkeYXux.js +1 -0
  4. package/dist/assets/book-open-BQwWBDKb.js +1 -0
  5. package/dist/assets/channels-list-page-Cv8o9Hxg.js +8 -0
  6. package/dist/assets/chat-page-BtA7H1Ea.js +1 -0
  7. package/dist/assets/{config-split-page-CRhlZ_pH.js → config-split-page-EidRBpP2.js} +1 -1
  8. package/dist/assets/confirm-dialog-iIJFFp2T.js +5 -0
  9. package/dist/assets/desktop-update-config-C6n_TB1r.js +1 -0
  10. package/dist/assets/{dist-CikkhOlq.js → dist-CBxWOazz.js} +1 -1
  11. package/dist/assets/{dist-PVA_xL1D.js → dist-D2eZC3OR.js} +1 -1
  12. package/dist/assets/doc-browser-B76XOXmW.js +1 -0
  13. package/dist/assets/doc-browser-DBnj9UMB.js +1 -0
  14. package/dist/assets/{doc-browser-context-MTw4EvKd.js → doc-browser-context-Sv-044Cl.js} +1 -1
  15. package/dist/assets/{ellipsis-y6vDtq4M.js → ellipsis-2uzhypw5.js} +1 -1
  16. package/dist/assets/es2015-DhQo_oS6.js +41 -0
  17. package/dist/assets/{external-link-DVJIQf_G.js → external-link-CM5dnUI6.js} +1 -1
  18. package/dist/assets/host-capabilities-9bB_998Z.js +1 -0
  19. package/dist/assets/i18n-provider-di1waTGX.js +1 -0
  20. package/dist/assets/index-CBRP-7-a.css +1 -0
  21. package/dist/assets/index-Z1fYpbNd.js +104 -0
  22. package/dist/assets/loader-circle-BjmN5DeJ.js +1 -0
  23. package/dist/assets/mcp-marketplace-page-C_MVQUbT.js +9 -0
  24. package/dist/assets/mcp-marketplace-page-CoZIiD6G.js +1 -0
  25. package/dist/assets/model-config-page-Cdr9P6TQ.js +1 -0
  26. package/dist/assets/plus-3ztRZD_l.js +1 -0
  27. package/dist/assets/provider-scoped-model-input-g1WkGiJ8.js +1 -0
  28. package/dist/assets/providers-config-page-CWlO7J4A.js +1 -0
  29. package/dist/assets/{react-VNqT5Zp7.js → react-DjpzoZXB.js} +1 -1
  30. package/dist/assets/refresh-cw-DaTv3zxE.js +1 -0
  31. package/dist/assets/remote-CKWZfVVB.js +1 -0
  32. package/dist/assets/rotate-cw-CHcaRUDz.js +1 -0
  33. package/dist/assets/runtime-config-page-CfTqh1nv.js +1 -0
  34. package/dist/assets/{save-BOJ477H-.js → save-BZof1uie.js} +1 -1
  35. package/dist/assets/{search-BEti5beA.js → search-MxAarS_g.js} +1 -1
  36. package/dist/assets/search-config-page-Dm8daYny.js +1 -0
  37. package/dist/assets/{secrets-config-page--IzrIOid.js → secrets-config-page-D8AbcR05.js} +2 -2
  38. package/dist/assets/security-config-Ds0LsJmo.js +1 -0
  39. package/dist/assets/select-D7GcNP0z.js +1 -0
  40. package/dist/assets/setting-row-CuozlnJd.js +1 -0
  41. package/dist/assets/side-dock-BcLau5S_.js +3 -0
  42. package/dist/assets/skeleton-shfmXuYp.js +1 -0
  43. package/dist/assets/switch-C1BWMU5c.js +1 -0
  44. package/dist/assets/tag-chip-DCUrQUCP.js +1 -0
  45. package/dist/assets/tooltip-DfAHHA76.js +1 -0
  46. package/dist/assets/use-config-DAdbT5QJ.js +1 -0
  47. package/dist/assets/use-confirm-dialog-eWcJ6V-s.js +1 -0
  48. package/dist/assets/x-DqL1mYLC.js +1 -0
  49. package/dist/index.html +53 -17
  50. package/index.html +25 -2
  51. package/package.json +9 -9
  52. package/src/app/components/layout/__tests__/app-layout.test.tsx +27 -0
  53. package/src/app/components/layout/__tests__/settings-entry-page.test.tsx +9 -1
  54. package/src/app/components/layout/__tests__/sidebar.layout.test.tsx +6 -3
  55. package/src/app/components/layout/app-layout.tsx +10 -3
  56. package/src/app/components/layout/sidebar-items.tsx +8 -8
  57. package/src/app/components/layout/sidebar-rail.styles.ts +4 -2
  58. package/src/app/components/layout/sidebar.tsx +21 -28
  59. package/src/app/configs/app-navigation.config.ts +11 -5
  60. package/src/app/index.tsx +8 -0
  61. package/src/app/managers/__tests__/viewport-layout.manager.test.ts +59 -0
  62. package/src/app/managers/viewport-layout.manager.ts +39 -0
  63. package/src/app/presenters/app.presenter.ts +25 -2
  64. package/src/app/styles/design-system.css +91 -4
  65. package/src/features/chat/components/conversation/__tests__/chat-conversation-content.test.tsx +86 -0
  66. package/src/features/chat/components/conversation/chat-conversation-content.tsx +47 -17
  67. package/src/features/chat/components/layout/__tests__/chat-sidebar-read-state.test.tsx +7 -0
  68. package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +9 -5
  69. package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +5 -5
  70. package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +51 -2
  71. package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +170 -1
  72. package/src/features/chat/features/conversation/components/session-conversation-area.tsx +23 -1
  73. package/src/features/chat/features/conversation/components/session-conversation-input.tsx +24 -7
  74. package/src/features/chat/features/conversation/components/session-queued-input-rows.tsx +96 -0
  75. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-controller.test.tsx +181 -0
  76. package/src/features/chat/features/conversation/hooks/use-session-conversation-controller.ts +351 -89
  77. package/src/features/chat/features/input/hooks/__tests__/use-chat-input-surface-state.test.tsx +29 -4
  78. package/src/features/chat/features/input/hooks/use-chat-input-surface-state.ts +28 -3
  79. package/src/features/chat/features/input/input-surface-plugins/__tests__/slash-command-plugin.test.ts +108 -8
  80. package/src/features/chat/features/input/input-surface-plugins/panel-app-reference-plugin.utils.ts +50 -14
  81. package/src/features/chat/features/input/input-surface-plugins/slash-command-plugin.utils.ts +77 -6
  82. package/src/features/chat/features/message/components/__tests__/chat-inline-panel-app-card.test.tsx +21 -12
  83. package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +166 -0
  84. package/src/features/chat/features/message/components/chat-inline-panel-app-card.tsx +19 -11
  85. package/src/features/chat/features/message/components/chat-message-list.container.tsx +38 -64
  86. package/src/features/chat/features/message/utils/__tests__/chat-message-process-summary.utils.test.ts +59 -0
  87. package/src/features/chat/features/message/utils/__tests__/chat-message-show-content-tool-card.utils.test.ts +56 -1
  88. package/src/features/chat/features/message/utils/chat-inline-panel-app-card.utils.ts +0 -2
  89. package/src/features/chat/features/message/utils/chat-message-process-summary.utils.ts +80 -0
  90. package/src/features/chat/features/message/utils/chat-message-show-content-tool-card.utils.ts +12 -3
  91. package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +60 -0
  92. package/src/features/chat/features/message/utils/chat-message.utils.ts +3 -0
  93. package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-agent-runtime.test.tsx +39 -0
  94. package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +2 -2
  95. package/src/features/chat/features/session/components/chat-sidebar-session-item.tsx +1 -1
  96. package/src/features/chat/features/session/components/session-header/__tests__/chat-session-header-actions.test.tsx +3 -1
  97. package/src/features/chat/features/session/components/session-header/chat-session-metadata-dialog.tsx +1 -1
  98. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.test.tsx +270 -115
  99. package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumb-browser.tsx +121 -0
  100. package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumbs.tsx +75 -20
  101. package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +70 -5
  102. package/src/features/chat/features/workspace/utils/chat-workspace-file-tab-persistence.utils.ts +83 -0
  103. package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +79 -1
  104. package/src/features/chat/managers/__tests__/chat-ui.manager.test.ts +2 -0
  105. package/src/features/chat/managers/chat-thread.manager.ts +9 -3
  106. package/src/features/chat/managers/chat-ui.manager.ts +5 -1
  107. package/src/features/chat/presenters/chat.presenter.ts +2 -1
  108. package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +4 -0
  109. package/src/features/chat/stores/chat-thread.store.ts +6 -64
  110. package/src/features/chat/types/chat-message.types.ts +4 -0
  111. package/src/features/marketplace/components/__tests__/marketplace-page-detail.test.tsx +33 -26
  112. package/src/features/marketplace/components/__tests__/marketplace-page.test.tsx +1 -0
  113. package/src/features/marketplace/components/curated-shelves/__tests__/marketplace-curated-scene-route.test.tsx +1 -0
  114. package/src/features/marketplace/components/detail-doc/__tests__/marketplace-detail-doc.test.tsx +115 -0
  115. package/src/features/marketplace/components/marketplace-detail-doc.tsx +441 -0
  116. package/src/features/marketplace/components/mcp/__tests__/mcp-marketplace-page.test.tsx +3 -3
  117. package/src/features/marketplace/components/mcp/mcp-marketplace-doc.ts +0 -60
  118. package/src/features/marketplace/components/mcp/mcp-marketplace-page.tsx +64 -29
  119. package/src/features/marketplace/hooks/use-marketplace-item-detail.ts +42 -31
  120. package/src/features/marketplace/index.ts +3 -0
  121. package/src/features/marketplace/stores/marketplace-detail-doc.store.ts +38 -0
  122. package/src/features/panel-apps/components/__tests__/panel-apps-list.test.tsx +32 -2
  123. package/src/features/pwa/managers/__tests__/pwa-shell-theme.manager.test.ts +11 -0
  124. package/src/features/pwa/managers/pwa-shell-theme.manager.ts +8 -3
  125. package/src/features/settings/pages/__tests__/appearance-settings-page.test.tsx +25 -0
  126. package/src/features/settings/pages/appearance-settings-page.tsx +40 -0
  127. package/src/features/side-dock/components/__tests__/side-dock.test.tsx +44 -2
  128. package/src/features/side-dock/components/side-dock.tsx +100 -13
  129. package/src/features/side-dock/configs/side-dock-built-in-items.config.ts +10 -0
  130. package/src/features/side-dock/managers/__tests__/side-dock.manager.test.ts +32 -2
  131. package/src/features/side-dock/managers/side-dock.manager.ts +17 -7
  132. package/src/features/side-dock/stores/side-dock.store.ts +9 -0
  133. package/src/features/side-dock/types/side-dock.types.ts +10 -3
  134. package/src/features/side-dock/utils/side-dock-item.utils.ts +15 -2
  135. package/src/index.css +501 -27
  136. package/src/platforms/desktop/components/desktop-window-chrome.tsx +1 -1
  137. package/src/shared/components/doc-browser/__tests__/doc-browser-context.test.tsx +30 -1
  138. package/src/shared/components/doc-browser/__tests__/doc-browser.test.tsx +206 -38
  139. package/src/shared/components/doc-browser/doc-browser-panel-parts.tsx +34 -11
  140. package/src/shared/components/doc-browser/doc-browser-renderer.types.ts +1 -1
  141. package/src/shared/components/doc-browser/doc-browser-tab-strip.tsx +23 -5
  142. package/src/shared/components/doc-browser/doc-browser.tsx +128 -75
  143. package/src/shared/components/doc-browser/managers/doc-browser.manager.ts +9 -1
  144. package/src/shared/components/doc-browser/utils/doc-browser-floating-panel.utils.ts +83 -0
  145. package/src/shared/components/ui/__tests__/dialog.test.tsx +27 -0
  146. package/src/shared/components/ui/actions/icon-action-button.tsx +14 -2
  147. package/src/shared/components/ui/dialog.tsx +2 -2
  148. package/src/shared/components/ui/popover.tsx +1 -1
  149. package/src/shared/components/ui/select.tsx +1 -1
  150. package/src/shared/components/ui/switch.tsx +4 -3
  151. package/src/shared/components/ui/tab-strip/__tests__/compact-tab-strip.test.tsx +54 -0
  152. package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +4 -5
  153. package/src/shared/components/ui/tooltip.tsx +1 -1
  154. package/src/shared/lib/api/utils/server-path.utils.ts +27 -1
  155. package/src/shared/lib/i18n/locales/en-US/chat.json +15 -6
  156. package/src/shared/lib/i18n/locales/en-US/core.json +2 -0
  157. package/src/shared/lib/i18n/locales/en-US/doc-browser.json +11 -0
  158. package/src/shared/lib/i18n/locales/en-US/marketplace.json +5 -0
  159. package/src/shared/lib/i18n/locales/zh-CN/chat.json +15 -6
  160. package/src/shared/lib/i18n/locales/zh-CN/core.json +2 -0
  161. package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +11 -0
  162. package/src/shared/lib/i18n/locales/zh-CN/marketplace.json +5 -0
  163. package/src/shared/lib/session-project/__tests__/workspace-file-breadcrumb.test.ts +14 -0
  164. package/src/shared/lib/session-project/index.ts +1 -1
  165. package/src/shared/lib/session-project/{workspace-file-breadcrumb.ts → workspace-file-breadcrumb.utils.ts} +62 -8
  166. package/src/shared/lib/theme/index.ts +51 -13
  167. package/dist/assets/api-BP_8IaxB.js +0 -13
  168. package/dist/assets/channels-list-page-DEh5Jjqx.js +0 -8
  169. package/dist/assets/chat-page-BRB0N5WW.js +0 -105
  170. package/dist/assets/confirm-dialog-DsivFJ4r.js +0 -5
  171. package/dist/assets/desktop-update-config-B0w9p2ep.js +0 -1
  172. package/dist/assets/doc-browser-BoNJ7-tE.js +0 -1
  173. package/dist/assets/doc-browser-CdJ_d4ik.js +0 -1
  174. package/dist/assets/index-BiE-K8-q.css +0 -1
  175. package/dist/assets/index-D9BAE_L_.js +0 -105
  176. package/dist/assets/mcp-marketplace-page-B1SZNY5m.js +0 -40
  177. package/dist/assets/mcp-marketplace-page-Cq_Tw42T.js +0 -1
  178. package/dist/assets/model-config-page-DotaTUHb.js +0 -1
  179. package/dist/assets/plus-BbZqquJe.js +0 -1
  180. package/dist/assets/provider-scoped-model-input-DsFRuWJH.js +0 -1
  181. package/dist/assets/providers-config-page-DW4tQSLN.js +0 -1
  182. package/dist/assets/remote-B8TiUfwW.js +0 -1
  183. package/dist/assets/rotate-cw-DjHDTkCv.js +0 -1
  184. package/dist/assets/runtime-config-page-S6ErRHR8.js +0 -1
  185. package/dist/assets/search-config-page-DGj5am03.js +0 -1
  186. package/dist/assets/select-hoj8dK4K.js +0 -41
  187. package/dist/assets/tag-chip-T8-h-die.js +0 -1
  188. package/dist/assets/use-config-CjRV2hwL.js +0 -1
  189. package/dist/assets/x-D9PXs_xP.js +0 -1
  190. package/src/features/marketplace/components/detail-doc/__tests__/marketplace-detail-doc.test.ts +0 -40
  191. package/src/features/marketplace/components/detail-doc/marketplace-detail-doc-renderer.ts +0 -201
  192. package/src/features/marketplace/components/marketplace-detail-doc.ts +0 -142
  193. /package/dist/assets/{config-hints-BkYBc7z0.js → config-hints-fGnUjDe9.js} +0 -0
  194. /package/dist/assets/{middleware-CkFT3R4v.js → middleware-BILWbJE4.js} +0 -0
@@ -1,201 +0,0 @@
1
- export function escapeHtml(text: string): string {
2
- return text
3
- .replace(/&/g, "&")
4
- .replace(/</g, "&lt;")
5
- .replace(/>/g, "&gt;")
6
- .replace(/"/g, "&quot;")
7
- .replace(/'/g, "&#39;");
8
- }
9
-
10
- function stripYamlFrontmatter(text: string): string {
11
- const lines = text.replace(/\r\n/g, "\n").split("\n");
12
- if (lines[0]?.trim() !== "---") {
13
- return text;
14
- }
15
- const closingIndex = lines.findIndex((line, index) => index > 0 && line.trim() === "---");
16
- return closingIndex > 0 ? lines.slice(closingIndex + 1).join("\n").trim() : text;
17
- }
18
-
19
- function renderInlineMarkdown(text: string): string {
20
- return escapeHtml(text)
21
- .replace(/`([^`]+)`/g, "<code>$1</code>")
22
- .replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>")
23
- .replace(/\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>');
24
- }
25
-
26
- function isMarkdownBlockStart(line: string): boolean {
27
- return /^(#{1,4})\s+/.test(line)
28
- || /^([-*])\s+/.test(line)
29
- || /^\d+\.\s+/.test(line)
30
- || /^>\s?/.test(line)
31
- || /^```/.test(line);
32
- }
33
-
34
- type MarkdownRenderResult = {
35
- html: string;
36
- nextIndex: number;
37
- };
38
-
39
- function renderCodeBlock(lines: string[], index: number, fenceMatch: RegExpMatchArray): MarkdownRenderResult {
40
- const codeLines: string[] = [];
41
- let nextIndex = index + 1;
42
- while (nextIndex < lines.length && !lines[nextIndex]?.trim().startsWith("```")) {
43
- codeLines.push(lines[nextIndex] ?? "");
44
- nextIndex += 1;
45
- }
46
- const language = fenceMatch[1] ? `<span class="code-language">${escapeHtml(fenceMatch[1])}</span>` : "";
47
- return {
48
- html: `<div class="code-block">${language}<pre><code>${escapeHtml(codeLines.join("\n"))}</code></pre></div>`,
49
- nextIndex: nextIndex + 1,
50
- };
51
- }
52
-
53
- function renderHeadingBlock(headingMatch: RegExpMatchArray, index: number): MarkdownRenderResult {
54
- const level = Math.min(4, headingMatch[1]?.length ?? 2);
55
- return {
56
- html: `<h${level}>${renderInlineMarkdown(headingMatch[2] ?? "")}</h${level}>`,
57
- nextIndex: index + 1,
58
- };
59
- }
60
-
61
- function renderListBlock(lines: string[], index: number, ordered: boolean): MarkdownRenderResult {
62
- const items: string[] = [];
63
- let nextIndex = index;
64
- const matcher = ordered ? /^\d+\.\s+/ : /^[-*]\s+/;
65
- while (nextIndex < lines.length && matcher.test(lines[nextIndex]?.trim() ?? "")) {
66
- items.push(`<li>${renderInlineMarkdown((lines[nextIndex] ?? "").trim().replace(matcher, ""))}</li>`);
67
- nextIndex += 1;
68
- }
69
- return {
70
- html: ordered ? `<ol>${items.join("")}</ol>` : `<ul>${items.join("")}</ul>`,
71
- nextIndex,
72
- };
73
- }
74
-
75
- function renderQuoteBlock(lines: string[], index: number): MarkdownRenderResult {
76
- const quotes: string[] = [];
77
- let nextIndex = index;
78
- while (nextIndex < lines.length && /^>\s?/.test(lines[nextIndex]?.trim() ?? "")) {
79
- quotes.push((lines[nextIndex] ?? "").trim().replace(/^>\s?/, ""));
80
- nextIndex += 1;
81
- }
82
- return {
83
- html: `<blockquote>${renderInlineMarkdown(quotes.join(" "))}</blockquote>`,
84
- nextIndex,
85
- };
86
- }
87
-
88
- function renderParagraphBlock(lines: string[], index: number): MarkdownRenderResult {
89
- const paragraphLines: string[] = [];
90
- let nextIndex = index;
91
- while (nextIndex < lines.length) {
92
- const paragraphLine = lines[nextIndex]?.trim() ?? "";
93
- if (!paragraphLine || isMarkdownBlockStart(paragraphLine)) {
94
- break;
95
- }
96
- paragraphLines.push(paragraphLine);
97
- nextIndex += 1;
98
- }
99
- return {
100
- html: `<p>${renderInlineMarkdown(paragraphLines.join(" "))}</p>`,
101
- nextIndex,
102
- };
103
- }
104
-
105
- function renderMarkdownBlock(lines: string[], index: number): MarkdownRenderResult {
106
- const trimmed = lines[index]?.trim() ?? "";
107
- const fenceMatch = trimmed.match(/^```(\S*)/);
108
- if (fenceMatch) {
109
- return renderCodeBlock(lines, index, fenceMatch);
110
- }
111
-
112
- const headingMatch = trimmed.match(/^(#{1,4})\s+(.+)$/);
113
- if (headingMatch) {
114
- return renderHeadingBlock(headingMatch, index);
115
- }
116
-
117
- if (/^[-*]\s+/.test(trimmed)) {
118
- return renderListBlock(lines, index, false);
119
- }
120
- if (/^\d+\.\s+/.test(trimmed)) {
121
- return renderListBlock(lines, index, true);
122
- }
123
- if (/^>\s?/.test(trimmed)) {
124
- return renderQuoteBlock(lines, index);
125
- }
126
- return renderParagraphBlock(lines, index);
127
- }
128
-
129
- export function renderDetailMarkdown(markdown: string): string {
130
- const lines = stripYamlFrontmatter(markdown).replace(/\r\n/g, "\n").split("\n");
131
- const blocks: string[] = [];
132
- let index = 0;
133
-
134
- while (index < lines.length) {
135
- if (!(lines[index] ?? "").trim()) {
136
- index += 1;
137
- continue;
138
- }
139
-
140
- const result = renderMarkdownBlock(lines, index);
141
- blocks.push(result.html);
142
- index = result.nextIndex;
143
- }
144
-
145
- return blocks.join("");
146
- }
147
-
148
- type MetadataEntry = {
149
- key: string;
150
- value: string;
151
- };
152
-
153
- function stringifyMetadataValue(value: unknown): string {
154
- if (Array.isArray(value)) {
155
- return value.map((entry) => stringifyMetadataValue(entry)).join(", ");
156
- }
157
- if (value && typeof value === "object") {
158
- return JSON.stringify(value);
159
- }
160
- return String(value ?? "");
161
- }
162
-
163
- function readJsonMetadata(raw: string): MetadataEntry[] {
164
- try {
165
- const parsed = JSON.parse(raw) as unknown;
166
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
167
- return [];
168
- }
169
- return Object.entries(parsed)
170
- .map(([key, value]) => ({ key, value: stringifyMetadataValue(value) }))
171
- .filter((entry) => entry.value.trim().length > 0);
172
- } catch {
173
- return [];
174
- }
175
- }
176
-
177
- function readYamlLikeMetadata(raw: string): MetadataEntry[] {
178
- return raw
179
- .replace(/\r\n/g, "\n")
180
- .split("\n")
181
- .map((line) => line.match(/^([A-Za-z0-9_.-]+):\s*(.+)$/))
182
- .filter((match): match is RegExpMatchArray => Boolean(match))
183
- .map((match) => ({
184
- key: match[1] ?? "",
185
- value: match[2]?.replace(/^["']|["']$/g, "") ?? "",
186
- }))
187
- .filter((entry) => entry.key && entry.value.trim().length > 0);
188
- }
189
-
190
- export function renderDetailMetadata(raw: string): string {
191
- const entries = readJsonMetadata(raw);
192
- const metadataEntries = entries.length > 0 ? entries : readYamlLikeMetadata(raw);
193
- if (metadataEntries.length === 0) {
194
- return `<pre class="code">${escapeHtml(raw)}</pre>`;
195
- }
196
- return `<dl class="metadata-list">${metadataEntries
197
- .map(
198
- (entry) => `<div><dt>${escapeHtml(entry.key)}</dt><dd>${escapeHtml(entry.value)}</dd></div>`,
199
- )
200
- .join("")}</dl>`;
201
- }
@@ -1,142 +0,0 @@
1
- import {
2
- escapeHtml,
3
- renderDetailMarkdown,
4
- renderDetailMetadata,
5
- } from "@/features/marketplace/components/detail-doc/marketplace-detail-doc-renderer";
6
-
7
- export function buildGenericDetailDataUrl(params: {
8
- title: string;
9
- typeLabel: string;
10
- spec: string;
11
- summary?: string;
12
- description?: string;
13
- metadataRaw?: string;
14
- contentRaw?: string;
15
- sourceUrl?: string;
16
- sourceLabel?: string;
17
- tags?: string[];
18
- author?: string;
19
- loading?: boolean;
20
- }): string {
21
- const {
22
- title,
23
- typeLabel,
24
- spec,
25
- summary: rawSummary,
26
- description: rawDescription,
27
- metadataRaw,
28
- contentRaw,
29
- sourceUrl,
30
- sourceLabel,
31
- tags,
32
- author,
33
- loading,
34
- } = params;
35
- const metadata = metadataRaw?.trim() || "-";
36
- const content = contentRaw?.trim() || "-";
37
- const summary = rawSummary?.trim();
38
- const description = rawDescription?.trim();
39
- const shouldShowDescription = Boolean(description) && description !== summary;
40
- const renderedMetadata = renderDetailMetadata(metadata);
41
- const renderedContent = renderDetailMarkdown(content);
42
-
43
- const html = `<!doctype html>
44
- <html>
45
- <head>
46
- <meta charset="utf-8" />
47
- <meta name="viewport" content="width=device-width, initial-scale=1" />
48
- <title>${escapeHtml(title)}</title>
49
- <style>
50
- :root { color-scheme: light; }
51
- body { margin: 0; background: #f9f8f5; color: #2f2212; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }
52
- .wrap { max-width: 940px; margin: 0 auto; padding: 24px 20px 36px; }
53
- .hero { border: 1px solid #f0e2c8; border-radius: 14px; background: linear-gradient(180deg, #fff9f1 0%, #ffffff 28%); padding: 18px; box-shadow: 0 1px 3px rgba(30, 20, 10, 0.05); }
54
- .hero h1 { margin: 0; font-size: 22px; line-height: 1.2; letter-spacing: 0; }
55
- .meta { margin-top: 7px; color: #78644d; font-size: 12px; overflow-wrap: anywhere; word-break: break-word; }
56
- .summary { margin: 12px 0 0; font-size: 13px; line-height: 1.65; color: #5f5142; }
57
- .grid { display: grid; grid-template-columns: minmax(220px, 0.42fr) minmax(0, 1fr); gap: 12px; margin-top: 12px; }
58
- .card { border: 1px solid #eee3d1; background: #fffdf9; border-radius: 12px; overflow: hidden; box-shadow: 0 1px 2px rgba(30, 20, 10, 0.035); }
59
- .card h2 { margin: 0; padding: 11px 13px; font-size: 12px; font-weight: 650; color: #3f472f; border-bottom: 1px solid #f1e7d4; background: #fffaf2; }
60
- .card .body { padding: 12px 13px; font-size: 12px; color: #4e463d; line-height: 1.65; }
61
- .code { white-space: pre-wrap; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px; line-height: 1.55; margin: 0; }
62
- .metadata-list { margin: 0; }
63
- .metadata-list div { display: grid; grid-template-columns: minmax(72px, 0.36fr) minmax(0, 1fr); gap: 10px; padding: 8px 0; border-bottom: 1px solid #f2eadc; }
64
- .metadata-list div:last-child { border-bottom: 0; }
65
- .metadata-list dt { color: #7a5a24; font-weight: 650; overflow-wrap: anywhere; }
66
- .metadata-list dd { margin: 0; color: #4e463d; overflow-wrap: anywhere; }
67
- .markdown { font-size: 13px; line-height: 1.68; }
68
- .markdown > *:first-child { margin-top: 0; }
69
- .markdown > *:last-child { margin-bottom: 0; }
70
- .markdown h1, .markdown h2, .markdown h3, .markdown h4 { margin: 18px 0 8px; color: #2f2212; line-height: 1.25; letter-spacing: 0; }
71
- .markdown h1 { font-size: 20px; }
72
- .markdown h2 { font-size: 17px; }
73
- .markdown h3 { font-size: 15px; }
74
- .markdown h4 { font-size: 13px; }
75
- .markdown p { margin: 10px 0; }
76
- .markdown ul, .markdown ol { margin: 10px 0; padding-left: 20px; }
77
- .markdown li { margin: 5px 0; }
78
- .markdown blockquote { margin: 12px 0; padding: 8px 12px; border-left: 3px solid #d9b56f; border-radius: 8px; background: #fff7ea; color: #6d5841; }
79
- .markdown code { border: 1px solid #eadcc6; border-radius: 5px; background: #fff7ea; padding: 1px 4px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px; color: #6b4b16; }
80
- .markdown a { color: #5f6b45; text-decoration: none; font-weight: 600; }
81
- .markdown a:hover { text-decoration: underline; }
82
- .code-block { position: relative; margin: 12px 0; overflow: hidden; border: 1px solid #eadcc6; border-radius: 10px; background: #2f2a24; }
83
- .code-block pre { margin: 0; overflow-x: auto; padding: 13px; }
84
- .code-block code { border: 0; border-radius: 0; background: transparent; padding: 0; color: #f7efe3; }
85
- .code-language { position: absolute; right: 10px; top: 8px; color: #d8c3a0; font-size: 10px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
86
- .tags { margin-top: 10px; }
87
- .tag { display: inline-block; margin: 0 6px 6px 0; padding: 4px 8px; border: 1px solid #ecd9b5; border-radius: 999px; background: #fff7ea; color: #7a5a24; font-size: 11px; }
88
- .source { color: #5f6b45; text-decoration: none; overflow-wrap: anywhere; word-break: break-all; }
89
- .source:hover { text-decoration: underline; }
90
- .skeleton { display: block; border-radius: 8px; background: linear-gradient(90deg, #f0e6d6 0%, #fffaf2 42%, #f0e6d6 78%); background-size: 220% 100%; animation: shimmer 1.35s ease-in-out infinite; }
91
- .detail-skeleton .hero { padding: 18px; }
92
- .sk-title { width: 52%; height: 24px; }
93
- .sk-meta { width: 78%; height: 12px; margin-top: 12px; }
94
- .sk-line { height: 12px; margin-top: 12px; }
95
- .sk-line.short { width: 62%; }
96
- .sk-line.mid { width: 82%; }
97
- .sk-body { height: 220px; margin: 13px; }
98
- @keyframes shimmer { 0% { background-position: 120% 0; } 100% { background-position: -120% 0; } }
99
- @media (max-width: 860px) { .grid { grid-template-columns: 1fr; } }
100
- </style>
101
- </head>
102
- <body>
103
- <main class="wrap${loading ? " detail-skeleton" : ""}"${loading ? ' aria-busy="true"' : ""}>
104
- ${loading ? `
105
- <section class="hero">
106
- <span class="skeleton sk-title"></span>
107
- <span class="skeleton sk-meta"></span>
108
- <span class="skeleton sk-line mid"></span>
109
- <span class="skeleton sk-line"></span>
110
- <span class="skeleton sk-line short"></span>
111
- </section>
112
- <section class="grid">
113
- <article class="card"><span class="skeleton sk-body"></span></article>
114
- <article class="card"><span class="skeleton sk-body"></span></article>
115
- </section>
116
- ` : `
117
- <section class="hero">
118
- <h1>${escapeHtml(title)}</h1>
119
- <div class="meta">${escapeHtml(typeLabel)} · ${escapeHtml(spec)}${author ? ` · ${escapeHtml(author)}` : ""}</div>
120
- ${summary ? `<p class="summary">${escapeHtml(summary)}</p>` : ""}
121
- ${shouldShowDescription ? `<p class="summary">${escapeHtml(description as string)}</p>` : ""}
122
- ${tags && tags.length > 0 ? `<div class="tags">${tags.map((tag) => `<span class="tag">${escapeHtml(tag)}</span>`).join("")}</div>` : ""}
123
- ${sourceUrl ? `<p class="meta" style="margin-top:12px;">${escapeHtml(sourceLabel ?? "Source")}: <a class="source" href="${escapeHtml(sourceUrl)}" target="_blank" rel="noopener noreferrer">${escapeHtml(sourceUrl)}</a></p>` : ""}
124
- </section>
125
-
126
- <section class="grid">
127
- <article class="card">
128
- <h2>Metadata</h2>
129
- <div class="body">${renderedMetadata}</div>
130
- </article>
131
- <article class="card">
132
- <h2>Content</h2>
133
- <div class="body markdown">${renderedContent}</div>
134
- </article>
135
- </section>
136
- `}
137
- </main>
138
- </body>
139
- </html>`;
140
-
141
- return `data:text/html;charset=utf-8,${encodeURIComponent(html)}`;
142
- }