quasar-ui-danx 0.5.1 → 0.5.3

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 (38) hide show
  1. package/.claude/settings.local.json +8 -0
  2. package/dist/danx.es.js +13869 -12976
  3. package/dist/danx.es.js.map +1 -1
  4. package/dist/danx.umd.js +159 -151
  5. package/dist/danx.umd.js.map +1 -1
  6. package/dist/style.css +1 -1
  7. package/package.json +1 -1
  8. package/src/components/Utility/Buttons/ActionButton.vue +15 -5
  9. package/src/components/Utility/Code/CodeViewer.vue +10 -2
  10. package/src/components/Utility/Code/CodeViewerFooter.vue +2 -0
  11. package/src/components/Utility/Code/MarkdownContent.vue +31 -163
  12. package/src/components/Utility/Files/FilePreview.vue +2 -2
  13. package/src/components/Utility/Markdown/MarkdownEditor.vue +7 -2
  14. package/src/components/Utility/Markdown/MarkdownEditorContent.vue +69 -8
  15. package/src/components/Utility/Widgets/LabelPillWidget.vue +20 -0
  16. package/src/composables/markdown/features/useCodeBlocks.spec.ts +59 -33
  17. package/src/composables/markdown/features/useLinks.spec.ts +29 -10
  18. package/src/composables/markdown/useMarkdownEditor.ts +16 -7
  19. package/src/composables/useCodeFormat.ts +17 -10
  20. package/src/composables/useCodeViewerCollapse.ts +7 -0
  21. package/src/composables/useFileNavigation.ts +5 -1
  22. package/src/helpers/formats/highlightCSS.ts +236 -0
  23. package/src/helpers/formats/highlightHTML.ts +483 -0
  24. package/src/helpers/formats/highlightJavaScript.ts +346 -0
  25. package/src/helpers/formats/highlightSyntax.ts +15 -4
  26. package/src/helpers/formats/index.ts +3 -0
  27. package/src/helpers/formats/markdown/htmlToMarkdown/index.ts +42 -4
  28. package/src/helpers/formats/markdown/linePatterns.spec.ts +7 -4
  29. package/src/helpers/formats/markdown/parseInline.ts +26 -13
  30. package/src/helpers/routes.ts +3 -1
  31. package/src/styles/danx.scss +3 -3
  32. package/src/styles/index.scss +5 -5
  33. package/src/styles/themes/danx/code.scss +257 -1
  34. package/src/styles/themes/danx/index.scss +10 -10
  35. package/src/styles/themes/danx/markdown.scss +81 -0
  36. package/src/test/highlighters.test.ts +153 -0
  37. package/src/types/widgets.d.ts +2 -2
  38. package/vite.config.js +5 -1
@@ -26,6 +26,29 @@ describe("useCodeBlocks", () => {
26
26
  });
27
27
  }
28
28
 
29
+ /**
30
+ * Helper to verify code block wrapper structure
31
+ * The new implementation creates wrapper divs with mount points instead of <pre><code>
32
+ */
33
+ function expectCodeBlockWrapper(content: string, language = "") {
34
+ const wrapper = editor.container.querySelector(".code-block-wrapper");
35
+ expect(wrapper).not.toBeNull();
36
+ expect(wrapper?.getAttribute("contenteditable")).toBe("false");
37
+ expect(wrapper?.hasAttribute("data-code-block-id")).toBe(true);
38
+
39
+ const mountPoint = wrapper?.querySelector(".code-viewer-mount-point");
40
+ expect(mountPoint).not.toBeNull();
41
+ expect(mountPoint?.getAttribute("data-content")).toBe(content);
42
+ expect(mountPoint?.getAttribute("data-language")).toBe(language);
43
+ }
44
+
45
+ /**
46
+ * Helper to check if a code block wrapper exists
47
+ */
48
+ function hasCodeBlockWrapper(): boolean {
49
+ return editor.container.querySelector(".code-block-wrapper") !== null;
50
+ }
51
+
29
52
  describe("toggleCodeBlock", () => {
30
53
  beforeEach(() => {
31
54
  onContentChange = vi.fn();
@@ -38,7 +61,7 @@ describe("useCodeBlocks", () => {
38
61
 
39
62
  codeBlocks.toggleCodeBlock();
40
63
 
41
- expect(editor.getHtml()).toBe("<pre><code>Hello world</code></pre>");
64
+ expectCodeBlockWrapper("Hello world");
42
65
  expect(onContentChange).toHaveBeenCalled();
43
66
  });
44
67
 
@@ -49,7 +72,7 @@ describe("useCodeBlocks", () => {
49
72
 
50
73
  codeBlocks.toggleCodeBlock();
51
74
 
52
- expect(editor.getHtml()).toBe("<pre><code>Hello world</code></pre>");
75
+ expectCodeBlockWrapper("Hello world");
53
76
  expect(onContentChange).toHaveBeenCalled();
54
77
  });
55
78
 
@@ -60,7 +83,7 @@ describe("useCodeBlocks", () => {
60
83
 
61
84
  codeBlocks.toggleCodeBlock();
62
85
 
63
- expect(editor.getHtml()).toBe("<pre><code>Hello world</code></pre>");
86
+ expectCodeBlockWrapper("Hello world");
64
87
  expect(onContentChange).toHaveBeenCalled();
65
88
  });
66
89
 
@@ -71,7 +94,7 @@ describe("useCodeBlocks", () => {
71
94
 
72
95
  codeBlocks.toggleCodeBlock();
73
96
 
74
- expect(editor.getHtml()).toBe("<pre><code>Hello world</code></pre>");
97
+ expectCodeBlockWrapper("Hello world");
75
98
  expect(onContentChange).toHaveBeenCalled();
76
99
  });
77
100
 
@@ -97,7 +120,7 @@ describe("useCodeBlocks", () => {
97
120
 
98
121
  codeBlocks.toggleCodeBlock();
99
122
 
100
- expect(editor.getHtml()).toBe("<pre><code>const x = 1;</code></pre>");
123
+ expectCodeBlockWrapper("const x = 1;");
101
124
  });
102
125
 
103
126
  it("preserves content when toggling from code block", () => {
@@ -128,7 +151,7 @@ describe("useCodeBlocks", () => {
128
151
 
129
152
  codeBlocks.toggleCodeBlock();
130
153
 
131
- expect(editor.getHtml()).toBe("<pre><code></code></pre>");
154
+ expectCodeBlockWrapper("");
132
155
  });
133
156
 
134
157
  it("does not convert list items directly", () => {
@@ -152,17 +175,18 @@ describe("useCodeBlocks", () => {
152
175
  onContentChange = vi.fn();
153
176
  });
154
177
 
155
- it("converts ``` to code block with cursor anchor", () => {
178
+ it("does not convert just ``` without language (requires language identifier)", () => {
179
+ // Implementation intentionally requires at least one character in language identifier
180
+ // to avoid triggering on just "```" before user finishes typing the language
156
181
  editor = createTestEditor("<p>```</p>");
157
182
  const codeBlocks = createCodeBlocks();
158
183
  editor.setCursorInBlock(0, 3);
159
184
 
160
185
  const result = codeBlocks.checkAndConvertCodeBlockPattern();
161
186
 
162
- expect(result).toBe(true);
163
- // Code element contains a zero-width space as cursor anchor
164
- expect(editor.getHtml()).toBe(`<pre><code>${CURSOR_ANCHOR}</code></pre>`);
165
- expect(onContentChange).toHaveBeenCalled();
187
+ expect(result).toBe(false);
188
+ expect(editor.getHtml()).toBe("<p>```</p>");
189
+ expect(onContentChange).not.toHaveBeenCalled();
166
190
  });
167
191
 
168
192
  it("converts ```javascript to code block with language", () => {
@@ -173,7 +197,7 @@ describe("useCodeBlocks", () => {
173
197
  const result = codeBlocks.checkAndConvertCodeBlockPattern();
174
198
 
175
199
  expect(result).toBe(true);
176
- expect(editor.getHtml()).toBe(`<pre><code class="language-javascript">${CURSOR_ANCHOR}</code></pre>`);
200
+ expectCodeBlockWrapper("", "javascript");
177
201
  expect(onContentChange).toHaveBeenCalled();
178
202
  });
179
203
 
@@ -185,7 +209,7 @@ describe("useCodeBlocks", () => {
185
209
  const result = codeBlocks.checkAndConvertCodeBlockPattern();
186
210
 
187
211
  expect(result).toBe(true);
188
- expect(editor.getHtml()).toBe(`<pre><code class="language-python">${CURSOR_ANCHOR}</code></pre>`);
212
+ expectCodeBlockWrapper("", "python");
189
213
  });
190
214
 
191
215
  it("converts ```typescript to code block with language", () => {
@@ -196,7 +220,7 @@ describe("useCodeBlocks", () => {
196
220
  const result = codeBlocks.checkAndConvertCodeBlockPattern();
197
221
 
198
222
  expect(result).toBe(true);
199
- expect(editor.getHtml()).toBe(`<pre><code class="language-typescript">${CURSOR_ANCHOR}</code></pre>`);
223
+ expectCodeBlockWrapper("", "typescript");
200
224
  });
201
225
 
202
226
  it("returns false when no pattern is present", () => {
@@ -237,7 +261,7 @@ describe("useCodeBlocks", () => {
237
261
  expect(onContentChange).not.toHaveBeenCalled();
238
262
  });
239
263
 
240
- it("does not convert if already a heading", () => {
264
+ it("converts heading with code fence pattern", () => {
241
265
  editor = createTestEditor("<h1>```javascript</h1>");
242
266
  const codeBlocks = createCodeBlocks();
243
267
  editor.setCursorInBlock(0, 13);
@@ -246,7 +270,7 @@ describe("useCodeBlocks", () => {
246
270
 
247
271
  // Headings ARE convertible blocks, so this should convert
248
272
  expect(result).toBe(true);
249
- expect(editor.getHtml()).toBe(`<pre><code class="language-javascript">${CURSOR_ANCHOR}</code></pre>`);
273
+ expectCodeBlockWrapper("", "javascript");
250
274
  });
251
275
 
252
276
  it("converts DIV elements (browser default)", () => {
@@ -257,7 +281,7 @@ describe("useCodeBlocks", () => {
257
281
  const result = codeBlocks.checkAndConvertCodeBlockPattern();
258
282
 
259
283
  expect(result).toBe(true);
260
- expect(editor.getHtml()).toBe(`<pre><code class="language-rust">${CURSOR_ANCHOR}</code></pre>`);
284
+ expectCodeBlockWrapper("", "rust");
261
285
  });
262
286
 
263
287
  it("does not convert list items", () => {
@@ -275,25 +299,19 @@ describe("useCodeBlocks", () => {
275
299
  expect(onContentChange).not.toHaveBeenCalled();
276
300
  });
277
301
 
278
- it("positions cursor inside code element after conversion", () => {
302
+ it("creates wrapper structure after conversion", () => {
279
303
  editor = createTestEditor("<p>```javascript</p>");
280
304
  const codeBlocks = createCodeBlocks();
281
305
  editor.setCursorInBlock(0, 13);
282
306
 
283
307
  codeBlocks.checkAndConvertCodeBlockPattern();
284
308
 
285
- // Verify cursor is positioned inside the code element
286
- const sel = window.getSelection();
287
- expect(sel).not.toBeNull();
288
- expect(sel?.rangeCount).toBe(1);
289
-
290
- const range = sel?.getRangeAt(0);
291
- const code = editor.container.querySelector("code");
309
+ // Verify wrapper structure is created
310
+ expectCodeBlockWrapper("", "javascript");
292
311
 
293
- // The cursor should be in the code element's text node (the cursor anchor)
294
- expect(range?.startContainer.parentElement).toBe(code);
295
- // Position should be at the end of the cursor anchor (after the zero-width space)
296
- expect(range?.startOffset).toBe(1);
312
+ // Verify the wrapper has an id that can be used for mounting
313
+ const wrapper = editor.container.querySelector(".code-block-wrapper");
314
+ expect(wrapper?.getAttribute("data-code-block-id")).toBeTruthy();
297
315
  });
298
316
 
299
317
  it("cursor anchor is stripped during markdown conversion", () => {
@@ -761,7 +779,15 @@ describe("useCodeBlocks", () => {
761
779
 
762
780
  codeBlocks.toggleCodeBlock();
763
781
 
764
- expect(editor.getHtml()).toBe("<p>First</p><pre><code>Second</code></pre><p>Third</p>");
782
+ // Verify only the second block was converted
783
+ const html = editor.getHtml();
784
+ expect(html).toContain("<p>First</p>");
785
+ expect(html).toContain("<p>Third</p>");
786
+ expect(html).toContain("code-block-wrapper");
787
+
788
+ // Verify the wrapper has the correct content
789
+ const mountPoint = editor.container.querySelector(".code-viewer-mount-point");
790
+ expect(mountPoint?.getAttribute("data-content")).toBe("Second");
765
791
  });
766
792
 
767
793
  it("handles multiline content in code block", () => {
@@ -771,9 +797,9 @@ describe("useCodeBlocks", () => {
771
797
 
772
798
  codeBlocks.toggleCodeBlock();
773
799
 
774
- // Content should be preserved as-is
775
- const code = editor.container.querySelector("code");
776
- expect(code?.textContent).toBe("Line 1\nLine 2\nLine 3");
800
+ // Content should be preserved in the data-content attribute
801
+ const mountPoint = editor.container.querySelector(".code-viewer-mount-point");
802
+ expect(mountPoint?.getAttribute("data-content")).toBe("Line 1\nLine 2\nLine 3");
777
803
  });
778
804
  });
779
805
  });
@@ -2,6 +2,19 @@ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
2
  import { useLinks } from "./useLinks";
3
3
  import { createTestEditor, TestEditorResult } from "../../../test/helpers/editorTestUtils";
4
4
 
5
+ // Mock getBoundingClientRect for Range since jsdom doesn't implement it
6
+ Range.prototype.getBoundingClientRect = vi.fn(() => ({
7
+ x: 0,
8
+ y: 0,
9
+ width: 100,
10
+ height: 20,
11
+ top: 0,
12
+ right: 100,
13
+ bottom: 20,
14
+ left: 0,
15
+ toJSON: () => ({})
16
+ }));
17
+
5
18
  describe("useLinks", () => {
6
19
  let editor: TestEditorResult;
7
20
  let onContentChange: ReturnType<typeof vi.fn>;
@@ -34,7 +47,7 @@ describe("useLinks", () => {
34
47
 
35
48
  links.insertLink();
36
49
 
37
- expect(editor.getHtml()).toBe('<p><a href="https://example.com">Hello</a> world</p>');
50
+ expect(editor.getHtml()).toBe('<p><a href="https://example.com" target="_blank" rel="noopener noreferrer">Hello</a> world</p>');
38
51
  expect(onContentChange).toHaveBeenCalled();
39
52
  });
40
53
 
@@ -46,7 +59,7 @@ describe("useLinks", () => {
46
59
 
47
60
  links.insertLink();
48
61
 
49
- expect(editor.getHtml()).toBe('<p>Hello <a href="https://test.com">beautiful</a> world</p>');
62
+ expect(editor.getHtml()).toBe('<p>Hello <a href="https://test.com" target="_blank" rel="noopener noreferrer">beautiful</a> world</p>');
50
63
  });
51
64
 
52
65
  it("trims whitespace from URL", () => {
@@ -57,7 +70,7 @@ describe("useLinks", () => {
57
70
 
58
71
  links.insertLink();
59
72
 
60
- expect(editor.getHtml()).toBe('<p><a href="https://example.com">Hello</a> world</p>');
73
+ expect(editor.getHtml()).toBe('<p><a href="https://example.com" target="_blank" rel="noopener noreferrer">Hello</a> world</p>');
61
74
  });
62
75
 
63
76
  it("does nothing when user cancels prompt", () => {
@@ -96,7 +109,7 @@ describe("useLinks", () => {
96
109
  expect(onContentChange).not.toHaveBeenCalled();
97
110
  });
98
111
 
99
- it("selects the wrapped content after wrapping", () => {
112
+ it("wraps content and link is properly created", () => {
100
113
  editor = createTestEditor("<p>Hello world</p>");
101
114
  vi.spyOn(window, "prompt").mockReturnValue("https://example.com");
102
115
  const links = createLinks();
@@ -104,8 +117,11 @@ describe("useLinks", () => {
104
117
 
105
118
  links.insertLink();
106
119
 
107
- const selection = window.getSelection();
108
- expect(selection?.toString()).toBe("Hello");
120
+ // Verify the link was created with correct text
121
+ const linkEl = editor.container.querySelector("a");
122
+ expect(linkEl).not.toBeNull();
123
+ expect(linkEl?.textContent).toBe("Hello");
124
+ expect(linkEl?.getAttribute("href")).toBe("https://example.com");
109
125
  });
110
126
  });
111
127
 
@@ -122,7 +138,7 @@ describe("useLinks", () => {
122
138
 
123
139
  links.insertLink();
124
140
 
125
- expect(editor.getHtml()).toContain('<a href="https://example.com">https://example.com</a>');
141
+ expect(editor.getHtml()).toContain('<a href="https://example.com" target="_blank" rel="noopener noreferrer">https://example.com</a>');
126
142
  expect(onContentChange).toHaveBeenCalled();
127
143
  });
128
144
 
@@ -150,7 +166,7 @@ describe("useLinks", () => {
150
166
  expect(onContentChange).not.toHaveBeenCalled();
151
167
  });
152
168
 
153
- it("selects the inserted link text after insertion", () => {
169
+ it("inserts link and link is properly created", () => {
154
170
  editor = createTestEditor("<p>Hello world</p>");
155
171
  vi.spyOn(window, "prompt").mockReturnValue("https://example.com");
156
172
  const links = createLinks();
@@ -158,8 +174,11 @@ describe("useLinks", () => {
158
174
 
159
175
  links.insertLink();
160
176
 
161
- const selection = window.getSelection();
162
- expect(selection?.toString()).toBe("https://example.com");
177
+ // Verify the link was created with URL as text
178
+ const linkEl = editor.container.querySelector("a");
179
+ expect(linkEl).not.toBeNull();
180
+ expect(linkEl?.textContent).toBe("https://example.com");
181
+ expect(linkEl?.getAttribute("href")).toBe("https://example.com");
163
182
  });
164
183
  });
165
184
 
@@ -323,10 +323,15 @@ export function useMarkdownEditor(options: UseMarkdownEditorOptions): UseMarkdow
323
323
  codeBlocks.toggleCodeBlock();
324
324
  }
325
325
 
326
- // Computed character count
327
- const charCount = computed(() => {
328
- return contentRef.value?.textContent?.length || 0;
329
- });
326
+ // Character count - updated on each input since textContent is not reactive
327
+ const charCount = ref(0);
328
+
329
+ /**
330
+ * Update the character count from the current content
331
+ */
332
+ function updateCharCount(): void {
333
+ charCount.value = contentRef.value?.textContent?.length || 0;
334
+ }
330
335
 
331
336
  // Reactive hotkey definitions for UI
332
337
  const hotkeyDefinitions = computed(() => {
@@ -738,6 +743,9 @@ export function useMarkdownEditor(options: UseMarkdownEditorOptions): UseMarkdow
738
743
  * Note: Code fence patterns (```) are only converted on Enter key press, not on input
739
744
  */
740
745
  function onInput(): void {
746
+ // Update character count immediately for responsive UI
747
+ updateCharCount();
748
+
741
749
  // Check for heading pattern (e.g., "# " -> H1)
742
750
  let converted = headings.checkAndConvertHeadingPattern();
743
751
 
@@ -860,8 +868,8 @@ export function useMarkdownEditor(options: UseMarkdownEditorOptions): UseMarkdow
860
868
  function onKeyDown(event: KeyboardEvent): void {
861
869
  // Don't handle events that originate from inside code block wrappers
862
870
  // (CodeViewer handles its own keyboard events like Ctrl+A for select-all)
863
- const target = event.target as HTMLElement;
864
- const isInCodeBlock = target.closest("[data-code-block-id]");
871
+ const target = event.target as HTMLElement | null;
872
+ const isInCodeBlock = target?.closest("[data-code-block-id]");
865
873
 
866
874
  // SPECIAL CASE: Handle Ctrl+Alt+L for code block language cycling
867
875
  // This is a fallback in case the CodeViewer's handler doesn't receive the event
@@ -873,7 +881,7 @@ export function useMarkdownEditor(options: UseMarkdownEditorOptions): UseMarkdow
873
881
  event.stopPropagation();
874
882
 
875
883
  // Find the code block ID and cycle its language
876
- const wrapper = target.closest("[data-code-block-id]");
884
+ const wrapper = target?.closest("[data-code-block-id]");
877
885
  const codeBlockId = wrapper?.getAttribute("data-code-block-id");
878
886
 
879
887
  if (codeBlockId) {
@@ -1007,6 +1015,7 @@ export function useMarkdownEditor(options: UseMarkdownEditorOptions): UseMarkdow
1007
1015
  nextTick(() => {
1008
1016
  if (contentRef.value) {
1009
1017
  contentRef.value.innerHTML = sync.renderedHtml.value;
1018
+ updateCharCount();
1010
1019
  }
1011
1020
  });
1012
1021
  }
@@ -2,7 +2,10 @@ import { computed, ref, Ref } from "vue";
2
2
  import { parse as parseYAML, stringify as yamlStringify } from "yaml";
3
3
  import { fJSON, parseMarkdownJSON, parseMarkdownYAML } from "../helpers/formats/parsers";
4
4
 
5
- export type CodeFormat = "json" | "yaml" | "text" | "markdown";
5
+ // Version for HMR cache busting
6
+ export const USE_CODE_FORMAT_VERSION = "1.0.4";
7
+
8
+ export type CodeFormat = "json" | "yaml" | "text" | "markdown" | "html" | "css" | "javascript";
6
9
 
7
10
  export interface UseCodeFormatOptions {
8
11
  initialFormat?: CodeFormat;
@@ -58,8 +61,8 @@ export function useCodeFormat(options: UseCodeFormatOptions = {}): UseCodeFormat
58
61
  function formatValueToString(value: object | string | null, targetFormat: CodeFormat = format.value): string {
59
62
  if (!value) return "";
60
63
 
61
- // Text and markdown formats - just return as-is
62
- if (targetFormat === "text" || targetFormat === "markdown") {
64
+ // Text, markdown, and code formats (CSS, JavaScript, HTML) - just return as-is
65
+ if (targetFormat === "text" || targetFormat === "markdown" || targetFormat === "css" || targetFormat === "javascript" || targetFormat === "html") {
63
66
  return typeof value === "string" ? value : JSON.stringify(value, null, 2);
64
67
  }
65
68
 
@@ -79,11 +82,12 @@ export function useCodeFormat(options: UseCodeFormatOptions = {}): UseCodeFormat
79
82
  }
80
83
 
81
84
  // Validate string content for a format
85
+ // v2: Added CSS/JavaScript/HTML as always-valid formats
82
86
  function validateContent(content: string, targetFormat: CodeFormat = format.value): boolean {
83
87
  if (!content) return true;
84
88
 
85
- // Text and markdown formats are always valid
86
- if (targetFormat === "text" || targetFormat === "markdown") return true;
89
+ // Text, markdown, and code formats are always valid
90
+ if (targetFormat === "text" || targetFormat === "markdown" || targetFormat === "css" || targetFormat === "javascript" || targetFormat === "html") return true;
87
91
 
88
92
  try {
89
93
  if (targetFormat === "json") {
@@ -98,11 +102,12 @@ export function useCodeFormat(options: UseCodeFormatOptions = {}): UseCodeFormat
98
102
  }
99
103
 
100
104
  // Validate and return error details if invalid
105
+ // v2: Added CSS/JavaScript/HTML as always-valid formats
101
106
  function validateContentWithError(content: string, targetFormat: CodeFormat = format.value): ValidationError | null {
102
107
  if (!content) return null;
103
108
 
104
- // Text and markdown formats are always valid
105
- if (targetFormat === "text" || targetFormat === "markdown") return null;
109
+ // Text, markdown, and code formats are always valid
110
+ if (targetFormat === "text" || targetFormat === "markdown" || targetFormat === "css" || targetFormat === "javascript" || targetFormat === "html") return null;
106
111
 
107
112
  try {
108
113
  if (targetFormat === "json") {
@@ -144,16 +149,18 @@ export function useCodeFormat(options: UseCodeFormatOptions = {}): UseCodeFormat
144
149
 
145
150
  // Initialize with value if provided
146
151
  if (options.initialValue) {
147
- rawContent.value = formatValueToString(options.initialValue, format.value);
152
+ const formatted = formatValueToString(options.initialValue, format.value);
153
+ rawContent.value = formatted;
148
154
  }
149
155
 
150
156
  // Computed: parsed object from raw content
151
157
  const parsedValue = computed(() => parseContent(rawContent.value));
152
158
 
153
159
  // Computed: formatted string
154
- // For text and markdown formats, return rawContent directly without parsing
160
+ // v3: For text, markdown, and code formats (CSS, JavaScript, HTML), return rawContent directly without parsing
155
161
  const formattedContent = computed(() => {
156
- if (format.value === "text" || format.value === "markdown") {
162
+ const isStringFormat = format.value === "text" || format.value === "markdown" || format.value === "css" || format.value === "javascript" || format.value === "html";
163
+ if (isStringFormat) {
157
164
  return rawContent.value;
158
165
  }
159
166
  return formatValueToString(parsedValue.value, format.value);
@@ -54,6 +54,13 @@ export function useCodeViewerCollapse(options: UseCodeViewerCollapseOptions): Us
54
54
  const maxLength = 100;
55
55
  let preview = "";
56
56
 
57
+ // For text and markdown formats, just show the first line - no parsing
58
+ if (format.value === "text" || format.value === "markdown") {
59
+ const firstLine = content.split("\n")[0].trim();
60
+ preview = firstLine.length > maxLength ? firstLine.slice(0, maxLength) + "..." : firstLine;
61
+ return preview;
62
+ }
63
+
57
64
  if (format.value === "json") {
58
65
  // For JSON, show compact inline format
59
66
  try {
@@ -10,7 +10,11 @@ export function useFileNavigation(initialFile: Ref<UploadedFile | null>, initial
10
10
  const currentFile = ref<UploadedFile | null>(initialFile.value);
11
11
  const relatedFiles = ref<UploadedFile[]>(initialRelatedFiles.value);
12
12
  const parentStack = ref<FileNavigationParent[]>([]);
13
- const currentIndex = ref(0);
13
+ const currentIndex = ref(
14
+ initialFile.value
15
+ ? Math.max(0, initialRelatedFiles.value.findIndex(f => f.id === initialFile.value!.id))
16
+ : 0
17
+ );
14
18
 
15
19
  // Computed properties
16
20
  const hasParent = computed(() => parentStack.value.length > 0);