artifactuse 0.1.20 → 0.1.22

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.
@@ -47,6 +47,13 @@ const PANEL_LANGUAGES = [
47
47
  'form',
48
48
  ];
49
49
 
50
+ /**
51
+ * Generate unique artifact ID
52
+ */
53
+ function generateArtifactId(prefix = 'artifact') {
54
+ return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
55
+ }
56
+
50
57
  /**
51
58
  * Check if language/type supports preview
52
59
  */
@@ -120,6 +127,7 @@ function getLanguageDisplayName(language) {
120
127
  // Structured artifacts
121
128
  form: 'Form',
122
129
  social: 'Social Preview',
130
+ txt: 'Plain Text',
123
131
  };
124
132
  return names[language?.toLowerCase()] || language?.toUpperCase() || 'Code';
125
133
  }
@@ -171,6 +179,41 @@ function getFileExtension$1(language) {
171
179
  return extensions[language?.toLowerCase()] || 'txt';
172
180
  }
173
181
 
182
+ /**
183
+ * Get language from file extension (reverse of getFileExtension)
184
+ */
185
+ function getLanguageFromExtension(ext) {
186
+ const map = {
187
+ html: 'html', htm: 'html',
188
+ css: 'css',
189
+ js: 'javascript', mjs: 'javascript',
190
+ ts: 'typescript',
191
+ jsx: 'jsx', tsx: 'tsx',
192
+ vue: 'vue',
193
+ py: 'python',
194
+ java: 'java',
195
+ cs: 'csharp',
196
+ cpp: 'cpp', c: 'c', h: 'c',
197
+ go: 'go',
198
+ rs: 'rust',
199
+ rb: 'ruby',
200
+ php: 'php',
201
+ swift: 'swift',
202
+ kt: 'kotlin',
203
+ scala: 'scala',
204
+ sql: 'sql',
205
+ sh: 'bash',
206
+ json: 'json',
207
+ xml: 'xml',
208
+ yaml: 'yaml', yml: 'yaml',
209
+ md: 'markdown',
210
+ svg: 'svg',
211
+ diff: 'diff',
212
+ patch: 'patch',
213
+ };
214
+ return map[ext?.toLowerCase()] || null;
215
+ }
216
+
174
217
  /**
175
218
  * Get language/type icon SVG path
176
219
  */
@@ -625,14 +668,18 @@ function createState() {
625
668
  */
626
669
  function setActiveArtifact(artifactId) {
627
670
  const artifact = getArtifact(artifactId);
628
-
671
+
672
+ let viewMode = artifact?.isPreviewable === false ? 'code' : 'preview';
673
+ if (artifact?.tabs && !artifact.tabs.includes(viewMode)) {
674
+ viewMode = artifact.tabs[0];
675
+ }
676
+
629
677
  state = {
630
678
  ...state,
631
679
  activeArtifactId: artifactId,
632
- // Set view mode based on artifact type
633
- viewMode: artifact?.isPreviewable === false ? 'code' : 'preview',
680
+ viewMode,
634
681
  };
635
-
682
+
636
683
  notify();
637
684
  }
638
685
 
@@ -665,7 +712,7 @@ function createState() {
665
712
  * Set view mode
666
713
  */
667
714
  function setViewMode(mode) {
668
- if (!['preview', 'code', 'split'].includes(mode)) {
715
+ if (!['preview', 'code', 'split', 'edit'].includes(mode)) {
669
716
  console.warn(`Invalid view mode: ${mode}`);
670
717
  return;
671
718
  }
@@ -1764,6 +1811,389 @@ function createShareService(config = {}) {
1764
1811
  };
1765
1812
  }
1766
1813
 
1814
+ // artifactuse/core/editor.js
1815
+ // Optional CodeMirror 6 integration for the edit tab
1816
+ // Users must provide CodeMirror modules via config.editor.modules
1817
+
1818
+ /**
1819
+ * Create an editor manager that uses user-provided CodeMirror modules
1820
+ *
1821
+ * @param {object} editorConfig - config.editor from SDK config
1822
+ * @param {object} editorConfig.modules - CodeMirror module imports
1823
+ * @param {string} editorConfig.theme - 'dark' | 'light' | 'auto'
1824
+ * @returns {object} Editor manager with create/destroy methods
1825
+ */
1826
+ function createEditorManager(editorConfig = {}) {
1827
+ const modules = editorConfig.modules || null;
1828
+ let themePreference = editorConfig.theme || 'dark';
1829
+
1830
+ function isAvailable() {
1831
+ return !!(modules?.state && modules?.view);
1832
+ }
1833
+
1834
+ /**
1835
+ * Build dark theme using EditorView.theme
1836
+ */
1837
+ function buildDarkTheme(EditorView) {
1838
+ return EditorView.theme({
1839
+ '&': {
1840
+ backgroundColor: '#1e1e1e',
1841
+ color: '#e4e4e7',
1842
+ height: '100%',
1843
+ },
1844
+ '.cm-content': {
1845
+ caretColor: 'var(--accent, #60a5fa)',
1846
+ fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace',
1847
+ fontSize: '13px',
1848
+ lineHeight: '1.6',
1849
+ padding: '12px 0',
1850
+ },
1851
+ '.cm-cursor, .cm-dropCursor': {
1852
+ borderLeftColor: 'var(--accent, #60a5fa)',
1853
+ borderLeftWidth: '2px',
1854
+ },
1855
+ '.cm-selectionBackground, ::selection': {
1856
+ backgroundColor: 'rgba(99, 102, 241, 0.3) !important',
1857
+ },
1858
+ '.cm-focused .cm-selectionBackground': {
1859
+ backgroundColor: 'rgba(99, 102, 241, 0.3)',
1860
+ },
1861
+ '.cm-activeLine': {
1862
+ backgroundColor: 'rgba(255, 255, 255, 0.03)',
1863
+ },
1864
+ '.cm-activeLineGutter': {
1865
+ backgroundColor: 'rgba(255, 255, 255, 0.05)',
1866
+ },
1867
+ '.cm-gutters': {
1868
+ backgroundColor: 'transparent',
1869
+ color: '#52525b',
1870
+ border: 'none',
1871
+ paddingRight: '8px',
1872
+ },
1873
+ '.cm-lineNumbers .cm-gutterElement': {
1874
+ padding: '0 8px 0 16px',
1875
+ minWidth: '40px',
1876
+ },
1877
+ '.cm-foldGutter .cm-gutterElement': {
1878
+ padding: '0 4px',
1879
+ cursor: 'pointer',
1880
+ color: '#71717a',
1881
+ },
1882
+ '.cm-foldGutter .cm-gutterElement:hover': {
1883
+ color: '#a1a1aa',
1884
+ },
1885
+ '.cm-matchingBracket': {
1886
+ backgroundColor: 'rgba(99, 102, 241, 0.25)',
1887
+ outline: '1px solid rgba(99, 102, 241, 0.5)',
1888
+ },
1889
+ '.cm-tooltip': {
1890
+ backgroundColor: '#27272a',
1891
+ border: '1px solid #3f3f46',
1892
+ borderRadius: '6px',
1893
+ },
1894
+ '.cm-tooltip-autocomplete': {
1895
+ '& > ul': {
1896
+ fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace',
1897
+ },
1898
+ '& > ul > li': {
1899
+ padding: '4px 8px',
1900
+ },
1901
+ '& > ul > li[aria-selected]': {
1902
+ backgroundColor: 'rgba(99, 102, 241, 0.2)',
1903
+ },
1904
+ },
1905
+ '.cm-scroller': {
1906
+ overflow: 'auto',
1907
+ },
1908
+ }, { dark: true });
1909
+ }
1910
+
1911
+ /**
1912
+ * Build light theme using EditorView.theme
1913
+ */
1914
+ function buildLightTheme(EditorView) {
1915
+ return EditorView.theme({
1916
+ '&': {
1917
+ backgroundColor: '#ffffff',
1918
+ color: '#27272a',
1919
+ height: '100%',
1920
+ },
1921
+ '.cm-content': {
1922
+ caretColor: 'var(--accent, #3b82f6)',
1923
+ fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace',
1924
+ fontSize: '13px',
1925
+ lineHeight: '1.6',
1926
+ padding: '12px 0',
1927
+ },
1928
+ '.cm-cursor, .cm-dropCursor': {
1929
+ borderLeftColor: 'var(--accent, #3b82f6)',
1930
+ borderLeftWidth: '2px',
1931
+ },
1932
+ '.cm-selectionBackground, ::selection': {
1933
+ backgroundColor: 'rgba(59, 130, 246, 0.2) !important',
1934
+ },
1935
+ '.cm-focused .cm-selectionBackground': {
1936
+ backgroundColor: 'rgba(59, 130, 246, 0.2)',
1937
+ },
1938
+ '.cm-activeLine': {
1939
+ backgroundColor: 'rgba(0, 0, 0, 0.03)',
1940
+ },
1941
+ '.cm-activeLineGutter': {
1942
+ backgroundColor: 'rgba(0, 0, 0, 0.05)',
1943
+ },
1944
+ '.cm-gutters': {
1945
+ backgroundColor: 'transparent',
1946
+ color: '#a1a1aa',
1947
+ border: 'none',
1948
+ paddingRight: '8px',
1949
+ },
1950
+ '.cm-lineNumbers .cm-gutterElement': {
1951
+ padding: '0 8px 0 16px',
1952
+ minWidth: '40px',
1953
+ },
1954
+ '.cm-foldGutter .cm-gutterElement': {
1955
+ padding: '0 4px',
1956
+ cursor: 'pointer',
1957
+ color: '#a1a1aa',
1958
+ },
1959
+ '.cm-foldGutter .cm-gutterElement:hover': {
1960
+ color: '#71717a',
1961
+ },
1962
+ '.cm-matchingBracket': {
1963
+ backgroundColor: 'rgba(59, 130, 246, 0.15)',
1964
+ outline: '1px solid rgba(59, 130, 246, 0.4)',
1965
+ },
1966
+ '.cm-tooltip': {
1967
+ backgroundColor: '#ffffff',
1968
+ border: '1px solid #e4e4e7',
1969
+ borderRadius: '6px',
1970
+ boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1)',
1971
+ },
1972
+ '.cm-tooltip-autocomplete': {
1973
+ '& > ul': {
1974
+ fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace',
1975
+ },
1976
+ '& > ul > li': {
1977
+ padding: '4px 8px',
1978
+ },
1979
+ '& > ul > li[aria-selected]': {
1980
+ backgroundColor: 'rgba(59, 130, 246, 0.1)',
1981
+ },
1982
+ },
1983
+ '.cm-scroller': {
1984
+ overflow: 'auto',
1985
+ },
1986
+ }, { dark: false });
1987
+ }
1988
+
1989
+ /**
1990
+ * Build syntax highlighting styles
1991
+ */
1992
+ function buildHighlighting(HighlightStyle, tags, isDark) {
1993
+ if (isDark) {
1994
+ return HighlightStyle.define([
1995
+ { tag: tags.keyword, color: '#c084fc' },
1996
+ { tag: tags.operator, color: '#94a3b8' },
1997
+ { tag: tags.special(tags.variableName), color: '#67e8f9' },
1998
+ { tag: tags.typeName, color: '#fbbf24' },
1999
+ { tag: tags.atom, color: '#fb923c' },
2000
+ { tag: tags.number, color: '#fb923c' },
2001
+ { tag: tags.definition(tags.variableName), color: '#67e8f9' },
2002
+ { tag: tags.string, color: '#86efac' },
2003
+ { tag: tags.special(tags.string), color: '#86efac' },
2004
+ { tag: tags.comment, color: '#6b7280', fontStyle: 'italic' },
2005
+ { tag: tags.variableName, color: '#e4e4e7' },
2006
+ { tag: tags.tagName, color: '#f87171' },
2007
+ { tag: tags.bracket, color: '#a1a1aa' },
2008
+ { tag: tags.meta, color: '#fbbf24' },
2009
+ { tag: tags.link, color: '#60a5fa', textDecoration: 'underline' },
2010
+ { tag: tags.heading, fontWeight: 'bold', color: '#f472b6' },
2011
+ { tag: tags.emphasis, fontStyle: 'italic' },
2012
+ { tag: tags.strong, fontWeight: 'bold' },
2013
+ { tag: tags.strikethrough, textDecoration: 'line-through' },
2014
+ { tag: tags.className, color: '#fbbf24' },
2015
+ { tag: tags.propertyName, color: '#60a5fa' },
2016
+ { tag: tags.function(tags.variableName), color: '#60a5fa' },
2017
+ { tag: tags.function(tags.propertyName), color: '#60a5fa' },
2018
+ { tag: tags.bool, color: '#fb923c' },
2019
+ { tag: tags.null, color: '#fb923c' },
2020
+ { tag: tags.regexp, color: '#f87171' },
2021
+ ]);
2022
+ }
2023
+
2024
+ return HighlightStyle.define([
2025
+ { tag: tags.keyword, color: '#7c3aed' },
2026
+ { tag: tags.operator, color: '#64748b' },
2027
+ { tag: tags.special(tags.variableName), color: '#0891b2' },
2028
+ { tag: tags.typeName, color: '#d97706' },
2029
+ { tag: tags.atom, color: '#ea580c' },
2030
+ { tag: tags.number, color: '#ea580c' },
2031
+ { tag: tags.definition(tags.variableName), color: '#0891b2' },
2032
+ { tag: tags.string, color: '#16a34a' },
2033
+ { tag: tags.special(tags.string), color: '#16a34a' },
2034
+ { tag: tags.comment, color: '#9ca3af', fontStyle: 'italic' },
2035
+ { tag: tags.variableName, color: '#27272a' },
2036
+ { tag: tags.tagName, color: '#dc2626' },
2037
+ { tag: tags.bracket, color: '#71717a' },
2038
+ { tag: tags.meta, color: '#d97706' },
2039
+ { tag: tags.link, color: '#2563eb', textDecoration: 'underline' },
2040
+ { tag: tags.heading, fontWeight: 'bold', color: '#db2777' },
2041
+ { tag: tags.emphasis, fontStyle: 'italic' },
2042
+ { tag: tags.strong, fontWeight: 'bold' },
2043
+ { tag: tags.strikethrough, textDecoration: 'line-through' },
2044
+ { tag: tags.className, color: '#d97706' },
2045
+ { tag: tags.propertyName, color: '#2563eb' },
2046
+ { tag: tags.function(tags.variableName), color: '#2563eb' },
2047
+ { tag: tags.function(tags.propertyName), color: '#2563eb' },
2048
+ { tag: tags.bool, color: '#ea580c' },
2049
+ { tag: tags.null, color: '#ea580c' },
2050
+ { tag: tags.regexp, color: '#dc2626' },
2051
+ ]);
2052
+ }
2053
+
2054
+ /**
2055
+ * Resolve language extension from language string
2056
+ */
2057
+ function getLanguageExtension(lang) {
2058
+ const normalized = lang?.toLowerCase();
2059
+ if ((normalized === 'javascript' || normalized === 'js' || normalized === 'jsx' || normalized === 'tsx' || normalized === 'typescript' || normalized === 'ts') && modules.langJavascript) {
2060
+ return modules.langJavascript.javascript();
2061
+ }
2062
+ if ((normalized === 'python' || normalized === 'py') && modules.langPython) {
2063
+ return modules.langPython.python();
2064
+ }
2065
+ // No language extension — plain text
2066
+ return [];
2067
+ }
2068
+
2069
+ /**
2070
+ * Resolve whether to use dark theme
2071
+ */
2072
+ function isDark(sdkTheme) {
2073
+ if (themePreference === 'auto') {
2074
+ return sdkTheme === 'dark';
2075
+ }
2076
+ return themePreference === 'dark';
2077
+ }
2078
+
2079
+ /**
2080
+ * Create a CodeMirror editor instance
2081
+ *
2082
+ * @param {HTMLElement} container - DOM element to mount editor in
2083
+ * @param {object} options
2084
+ * @param {string} options.code - Initial code content
2085
+ * @param {string} options.language - Language for syntax highlighting
2086
+ * @param {string} options.sdkTheme - Current SDK theme ('dark'|'light')
2087
+ * @param {function} options.onChange - Called on content changes with new code
2088
+ * @returns {{ view: EditorView, getCode: () => string, setCode: (code: string) => void, destroy: () => void }}
2089
+ */
2090
+ function create(container, options = {}) {
2091
+ if (!isAvailable()) {
2092
+ console.warn('Artifactuse: CodeMirror modules not provided. Editor not available.');
2093
+ return null;
2094
+ }
2095
+
2096
+ const { EditorState } = modules.state;
2097
+ const {
2098
+ EditorView, keymap, lineNumbers, highlightActiveLineGutter,
2099
+ highlightSpecialChars, drawSelection, dropCursor,
2100
+ rectangularSelection, crosshairCursor, highlightActiveLine
2101
+ } = modules.view;
2102
+ const { defaultKeymap, history, historyKeymap, indentWithTab } = modules.commands;
2103
+ const {
2104
+ indentOnInput, syntaxHighlighting, bracketMatching,
2105
+ foldGutter, foldKeymap, HighlightStyle
2106
+ } = modules.language;
2107
+ const {
2108
+ closeBrackets, closeBracketsKeymap, autocompletion, completionKeymap
2109
+ } = modules.autocomplete;
2110
+
2111
+ const tags = modules.lezerHighlight?.tags || modules.language?.tags;
2112
+ const dark = isDark(options.sdkTheme);
2113
+ const theme = dark ? buildDarkTheme(EditorView) : buildLightTheme(EditorView);
2114
+
2115
+ const extensions = [
2116
+ lineNumbers(),
2117
+ highlightActiveLineGutter(),
2118
+ highlightSpecialChars(),
2119
+ history(),
2120
+ foldGutter({
2121
+ openText: '\u25BE',
2122
+ closedText: '\u25B8',
2123
+ }),
2124
+ drawSelection(),
2125
+ dropCursor(),
2126
+ EditorState.allowMultipleSelections.of(true),
2127
+ indentOnInput(),
2128
+ ...(tags ? [syntaxHighlighting(buildHighlighting(HighlightStyle, tags, dark))] : []),
2129
+ bracketMatching(),
2130
+ closeBrackets(),
2131
+ autocompletion(),
2132
+ rectangularSelection(),
2133
+ crosshairCursor(),
2134
+ highlightActiveLine(),
2135
+ keymap.of([
2136
+ ...closeBracketsKeymap,
2137
+ ...defaultKeymap,
2138
+ ...historyKeymap,
2139
+ ...foldKeymap,
2140
+ ...completionKeymap,
2141
+ indentWithTab,
2142
+ ]),
2143
+ getLanguageExtension(options.language),
2144
+ theme,
2145
+ ];
2146
+
2147
+ if (options.onChange) {
2148
+ extensions.push(
2149
+ EditorView.updateListener.of((update) => {
2150
+ if (update.docChanged) {
2151
+ options.onChange(update.state.doc.toString());
2152
+ }
2153
+ })
2154
+ );
2155
+ }
2156
+
2157
+ const state = EditorState.create({
2158
+ doc: options.code || '',
2159
+ extensions,
2160
+ });
2161
+
2162
+ const view = new EditorView({
2163
+ state,
2164
+ parent: container,
2165
+ });
2166
+
2167
+ return {
2168
+ view,
2169
+ getCode() {
2170
+ return view.state.doc.toString();
2171
+ },
2172
+ setCode(code) {
2173
+ const currentCode = view.state.doc.toString();
2174
+ if (currentCode !== code) {
2175
+ view.dispatch({
2176
+ changes: { from: 0, to: currentCode.length, insert: code },
2177
+ });
2178
+ }
2179
+ },
2180
+ destroy() {
2181
+ view.destroy();
2182
+ },
2183
+ };
2184
+ }
2185
+
2186
+ function setTheme(newTheme) {
2187
+ themePreference = newTheme;
2188
+ }
2189
+
2190
+ return {
2191
+ isAvailable,
2192
+ create,
2193
+ setTheme,
2194
+ };
2195
+ }
2196
+
1767
2197
  /**
1768
2198
  * marked v17.0.1 - a markdown parser
1769
2199
  * Copyright (c) 2018-2025, MarkedJS. (MIT License)
@@ -5849,6 +6279,9 @@ const DEFAULT_PANELS = {
5849
6279
  diff: 'diff-panel',
5850
6280
  patch: 'diff-panel',
5851
6281
 
6282
+ // Plain text
6283
+ txt: 'code-panel',
6284
+
5852
6285
  // Programming languages
5853
6286
  javascript: 'code-panel',
5854
6287
  js: 'code-panel',
@@ -6218,6 +6651,9 @@ function createArtifactuse(userConfig = {}) {
6218
6651
  // Create share service
6219
6652
  const share = createShareService(config.sharing);
6220
6653
 
6654
+ // Create editor manager (CodeMirror integration — optional)
6655
+ const editor = createEditorManager(config.editor);
6656
+
6221
6657
  /**
6222
6658
  * Process AI agent message content
6223
6659
  * Returns processed HTML with artifact placeholders
@@ -6364,7 +6800,34 @@ function createArtifactuse(userConfig = {}) {
6364
6800
 
6365
6801
  emit('artifact:opened', artifact);
6366
6802
  }
6367
-
6803
+
6804
+ /**
6805
+ * Open a file by name — auto-detects language from extension
6806
+ */
6807
+ function openFile(filename, code, options = {}) {
6808
+ const ext = filename.split('.').pop();
6809
+ const language = options.language || getLanguageFromExtension(ext) || ext;
6810
+ const title = options.title || filename;
6811
+ return openCode(code, language, { ...options, title });
6812
+ }
6813
+
6814
+ /**
6815
+ * Open code with explicit language
6816
+ */
6817
+ function openCode(code, language, options = {}) {
6818
+ const msgId = options.messageId || generateArtifactId('open');
6819
+ const resolvedLang = hasPanel({ type: language, language }) ? language : 'txt';
6820
+ const artifact = createArtifact(code, resolvedLang, msgId, 0);
6821
+ artifact.title = options.title || artifact.title;
6822
+ artifact.isInline = false;
6823
+ if (options.tabs) artifact.tabs = options.tabs;
6824
+ state.addArtifact(artifact);
6825
+ openArtifact(artifact);
6826
+ if (options.viewMode) state.setViewMode(options.viewMode);
6827
+ else if (options.tabs) state.setViewMode(options.tabs[0]);
6828
+ return artifact;
6829
+ }
6830
+
6368
6831
  /**
6369
6832
  * Close panel
6370
6833
  */
@@ -6590,6 +7053,8 @@ function createArtifactuse(userConfig = {}) {
6590
7053
 
6591
7054
  // Panel control
6592
7055
  openArtifact,
7056
+ openFile,
7057
+ openCode,
6593
7058
  closePanel,
6594
7059
  togglePanel,
6595
7060
  toggleFullscreen,
@@ -6615,6 +7080,9 @@ function createArtifactuse(userConfig = {}) {
6615
7080
  off,
6616
7081
  emit,
6617
7082
 
7083
+ // Editor (CodeMirror integration)
7084
+ editor,
7085
+
6618
7086
  // Bridge (for advanced use)
6619
7087
  bridge,
6620
7088
 
@@ -6768,6 +7236,8 @@ function provideArtifactuse(config = {}) {
6768
7236
  processMessage: instance.processMessage,
6769
7237
  initializeContent: instance.initializeContent,
6770
7238
  openArtifact: instance.openArtifact,
7239
+ openFile: instance.openFile,
7240
+ openCode: instance.openCode,
6771
7241
  closePanel: instance.closePanel,
6772
7242
  togglePanel: instance.togglePanel,
6773
7243
  toggleFullscreen: instance.toggleFullscreen,
@@ -6886,6 +7356,8 @@ function createArtifactuseComposable(config = {}) {
6886
7356
  processMessage: instance.processMessage,
6887
7357
  initializeContent: instance.initializeContent,
6888
7358
  openArtifact: instance.openArtifact,
7359
+ openFile: instance.openFile,
7360
+ openCode: instance.openCode,
6889
7361
  closePanel: instance.closePanel,
6890
7362
  togglePanel: instance.togglePanel,
6891
7363
  toggleFullscreen: instance.toggleFullscreen,
@@ -26385,6 +26857,42 @@ var JSZip = /*@__PURE__*/getDefaultExportFromCjs(libExports);
26385
26857
  //
26386
26858
  //
26387
26859
  //
26860
+ //
26861
+ //
26862
+ //
26863
+ //
26864
+ //
26865
+ //
26866
+ //
26867
+ //
26868
+ //
26869
+ //
26870
+ //
26871
+ //
26872
+ //
26873
+ //
26874
+ //
26875
+ //
26876
+ //
26877
+ //
26878
+ //
26879
+ //
26880
+ //
26881
+ //
26882
+ //
26883
+ //
26884
+ //
26885
+ //
26886
+ //
26887
+ //
26888
+ //
26889
+ //
26890
+ //
26891
+ //
26892
+ //
26893
+ //
26894
+ //
26895
+ //
26388
26896
 
26389
26897
 
26390
26898
  var script$1 = defineComponent({
@@ -26418,6 +26926,7 @@ var script$1 = defineComponent({
26418
26926
  const contentRef = ref(null);
26419
26927
  const lineNumbersRef = ref(null);
26420
26928
  const codeScrollRef = ref(null);
26929
+ const editorContainerRef = ref(null);
26421
26930
 
26422
26931
  // State
26423
26932
  const copied = ref(false);
@@ -26448,7 +26957,11 @@ var script$1 = defineComponent({
26448
26957
  // Timers
26449
26958
  let streamEndTimer = null;
26450
26959
  let iframeLoadTimer = null;
26451
-
26960
+
26961
+ // Editor (CodeMirror) instance — not reactive to avoid proxy overhead
26962
+ let editorInstance = null;
26963
+ const isEditorAvailable = computed(() => instance.editor?.isAvailable() || false);
26964
+
26452
26965
  // Computed
26453
26966
  const languageDisplay = computed(() => {
26454
26967
  if (!activeArtifact.value) return '';
@@ -26569,7 +27082,35 @@ var script$1 = defineComponent({
26569
27082
  highlightCode();
26570
27083
  });
26571
27084
  }
26572
-
27085
+
27086
+ // Editor (CodeMirror) functions
27087
+ function initEditor() {
27088
+ if (!isEditorAvailable.value || !editorContainerRef.value || !activeArtifact.value) return;
27089
+ destroyEditor();
27090
+ editorInstance = instance.editor.create(editorContainerRef.value, {
27091
+ code: activeArtifact.value.code || '',
27092
+ language: activeArtifact.value.language || 'plaintext',
27093
+ sdkTheme: instance.getTheme(),
27094
+ });
27095
+ }
27096
+
27097
+ function destroyEditor() {
27098
+ if (editorInstance) {
27099
+ editorInstance.destroy();
27100
+ editorInstance = null;
27101
+ }
27102
+ }
27103
+
27104
+ function handleEditorSave() {
27105
+ if (!editorInstance || !activeArtifact.value) return;
27106
+ const code = editorInstance.getCode();
27107
+ instance.emit('edit:save', {
27108
+ artifactId: activeArtifact.value.id,
27109
+ artifact: activeArtifact.value,
27110
+ code,
27111
+ });
27112
+ }
27113
+
26573
27114
  function handleIframeLoad() {
26574
27115
  clearTimeout(iframeLoadTimer);
26575
27116
  iframeLoading.value = false;
@@ -26974,10 +27515,19 @@ var script$1 = defineComponent({
26974
27515
  }
26975
27516
 
26976
27517
  // Check if code changed
26977
- if (!oldArtifact || newArtifact.code !== oldArtifact.code) {
27518
+ if (!oldArtifact || newArtifact.id !== oldArtifact.id || newArtifact.code !== oldArtifact.code) {
26978
27519
  // Update code view immediately on each change
26979
27520
  updateCodeView();
26980
27521
 
27522
+ // Update editor content if in edit mode
27523
+ if (state.viewMode === 'edit') {
27524
+ if (!oldArtifact || newArtifact.id !== oldArtifact.id) {
27525
+ nextTick(() => initEditor());
27526
+ } else if (editorInstance) {
27527
+ editorInstance.setCode(newArtifact.code || '');
27528
+ }
27529
+ }
27530
+
26981
27531
  // Debounce iframe updates only
26982
27532
  clearTimeout(streamEndTimer);
26983
27533
  streamEndTimer = setTimeout(() => {
@@ -26994,8 +27544,21 @@ var script$1 = defineComponent({
26994
27544
  if (mode === 'code' || mode === 'split') {
26995
27545
  updateCodeView();
26996
27546
  }
27547
+ if (mode === 'edit') {
27548
+ nextTick(() => initEditor());
27549
+ }
26997
27550
  });
26998
-
27551
+
27552
+ // Watch panel open state — v-if destroys DOM on close, need to re-render code on reopen
27553
+ watch(() => state.isPanelOpen, (isOpen) => {
27554
+ if (isOpen && activeArtifact.value) {
27555
+ updateCodeView();
27556
+ }
27557
+ if (!isOpen) {
27558
+ destroyEditor();
27559
+ }
27560
+ });
27561
+
26999
27562
  onMounted(() => {
27000
27563
  instance.on('ai:request', (data) => emit('ai-request', data));
27001
27564
  instance.on('save:request', (data) => emit('save', data));
@@ -27011,6 +27574,7 @@ var script$1 = defineComponent({
27011
27574
  onUnmounted(() => {
27012
27575
  stopPanelResize();
27013
27576
  stopSplitResize();
27577
+ destroyEditor();
27014
27578
  document.removeEventListener('click', handleClickOutside);
27015
27579
  clearTimeout(streamEndTimer);
27016
27580
  clearTimeout(iframeLoadTimer);
@@ -27032,6 +27596,7 @@ var script$1 = defineComponent({
27032
27596
  contentRef,
27033
27597
  lineNumbersRef,
27034
27598
  codeScrollRef,
27599
+ editorContainerRef,
27035
27600
 
27036
27601
  // State
27037
27602
  copied,
@@ -27068,6 +27633,7 @@ var script$1 = defineComponent({
27068
27633
  panelClasses,
27069
27634
  sharingEnabled,
27070
27635
  isAuthenticated,
27636
+ isEditorAvailable,
27071
27637
 
27072
27638
  // Methods
27073
27639
  handleIframeLoad,
@@ -27083,6 +27649,7 @@ var script$1 = defineComponent({
27083
27649
  getArtifactIconHtml,
27084
27650
  startPanelResize,
27085
27651
  startSplitResize,
27652
+ handleEditorSave,
27086
27653
 
27087
27654
  // Share methods
27088
27655
  toggleSharePopup,
@@ -27815,141 +28382,205 @@ var __vue_render__$1 = function () {
27815
28382
  "div",
27816
28383
  { staticClass: "artifactuse-panel__tabs" },
27817
28384
  [
27818
- _c(
27819
- "button",
27820
- {
27821
- staticClass: "artifactuse-panel__tab",
27822
- class: {
27823
- "artifactuse-panel__tab--active":
27824
- _vm.state.viewMode === "preview",
27825
- },
27826
- attrs: {
27827
- disabled: !_vm.activeArtifact.isPreviewable,
27828
- title: "Preview",
27829
- },
27830
- on: {
27831
- click: function ($event) {
27832
- return _vm.setViewMode("preview")
27833
- },
27834
- },
27835
- },
27836
- [
27837
- _c(
27838
- "svg",
28385
+ !_vm.activeArtifact.tabs ||
28386
+ _vm.activeArtifact.tabs.includes("preview")
28387
+ ? _c(
28388
+ "button",
27839
28389
  {
28390
+ staticClass: "artifactuse-panel__tab",
28391
+ class: {
28392
+ "artifactuse-panel__tab--active":
28393
+ _vm.state.viewMode === "preview",
28394
+ },
27840
28395
  attrs: {
27841
- viewBox: "0 0 24 24",
27842
- fill: "none",
27843
- stroke: "currentColor",
27844
- "stroke-width": "2",
28396
+ disabled:
28397
+ !_vm.activeArtifact.isPreviewable,
28398
+ title: "Preview",
28399
+ },
28400
+ on: {
28401
+ click: function ($event) {
28402
+ return _vm.setViewMode("preview")
28403
+ },
27845
28404
  },
27846
28405
  },
27847
28406
  [
27848
- _c("path", {
27849
- attrs: {
27850
- d: "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z",
28407
+ _c(
28408
+ "svg",
28409
+ {
28410
+ attrs: {
28411
+ viewBox: "0 0 24 24",
28412
+ fill: "none",
28413
+ stroke: "currentColor",
28414
+ "stroke-width": "2",
28415
+ },
27851
28416
  },
27852
- }),
27853
- _vm._v(" "),
27854
- _c("circle", {
27855
- attrs: { cx: "12", cy: "12", r: "3" },
27856
- }),
28417
+ [
28418
+ _c("path", {
28419
+ attrs: {
28420
+ d: "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z",
28421
+ },
28422
+ }),
28423
+ _vm._v(" "),
28424
+ _c("circle", {
28425
+ attrs: {
28426
+ cx: "12",
28427
+ cy: "12",
28428
+ r: "3",
28429
+ },
28430
+ }),
28431
+ ]
28432
+ ),
27857
28433
  ]
27858
- ),
27859
- ]
27860
- ),
28434
+ )
28435
+ : _vm._e(),
27861
28436
  _vm._v(" "),
27862
- _c(
27863
- "button",
27864
- {
27865
- staticClass: "artifactuse-panel__tab",
27866
- class: {
27867
- "artifactuse-panel__tab--active":
27868
- _vm.state.viewMode === "code",
27869
- },
27870
- attrs: { title: "Code" },
27871
- on: {
27872
- click: function ($event) {
27873
- return _vm.setViewMode("code")
27874
- },
27875
- },
27876
- },
27877
- [
27878
- _c(
27879
- "svg",
28437
+ !_vm.activeArtifact.tabs ||
28438
+ _vm.activeArtifact.tabs.includes("code")
28439
+ ? _c(
28440
+ "button",
27880
28441
  {
27881
- attrs: {
27882
- viewBox: "0 0 24 24",
27883
- fill: "none",
27884
- stroke: "currentColor",
27885
- "stroke-width": "2",
28442
+ staticClass: "artifactuse-panel__tab",
28443
+ class: {
28444
+ "artifactuse-panel__tab--active":
28445
+ _vm.state.viewMode === "code",
28446
+ },
28447
+ attrs: { title: "Code" },
28448
+ on: {
28449
+ click: function ($event) {
28450
+ return _vm.setViewMode("code")
28451
+ },
27886
28452
  },
27887
28453
  },
27888
28454
  [
27889
- _c("polyline", {
27890
- attrs: { points: "16 18 22 12 16 6" },
27891
- }),
27892
- _vm._v(" "),
27893
- _c("polyline", {
27894
- attrs: { points: "8 6 2 12 8 18" },
27895
- }),
28455
+ _c(
28456
+ "svg",
28457
+ {
28458
+ attrs: {
28459
+ viewBox: "0 0 24 24",
28460
+ fill: "none",
28461
+ stroke: "currentColor",
28462
+ "stroke-width": "2",
28463
+ },
28464
+ },
28465
+ [
28466
+ _c("polyline", {
28467
+ attrs: {
28468
+ points: "16 18 22 12 16 6",
28469
+ },
28470
+ }),
28471
+ _vm._v(" "),
28472
+ _c("polyline", {
28473
+ attrs: { points: "8 6 2 12 8 18" },
28474
+ }),
28475
+ ]
28476
+ ),
27896
28477
  ]
27897
- ),
27898
- ]
27899
- ),
28478
+ )
28479
+ : _vm._e(),
27900
28480
  _vm._v(" "),
27901
- _c(
27902
- "button",
27903
- {
27904
- staticClass: "artifactuse-panel__tab",
27905
- class: {
27906
- "artifactuse-panel__tab--active":
27907
- _vm.state.viewMode === "split",
27908
- },
27909
- attrs: {
27910
- disabled: !_vm.activeArtifact.isPreviewable,
27911
- title: "Split view",
27912
- },
27913
- on: {
27914
- click: function ($event) {
27915
- return _vm.setViewMode("split")
27916
- },
27917
- },
27918
- },
27919
- [
27920
- _c(
27921
- "svg",
28481
+ !_vm.activeArtifact.tabs ||
28482
+ _vm.activeArtifact.tabs.includes("split")
28483
+ ? _c(
28484
+ "button",
27922
28485
  {
28486
+ staticClass: "artifactuse-panel__tab",
28487
+ class: {
28488
+ "artifactuse-panel__tab--active":
28489
+ _vm.state.viewMode === "split",
28490
+ },
27923
28491
  attrs: {
27924
- viewBox: "0 0 24 24",
27925
- fill: "none",
27926
- stroke: "currentColor",
27927
- "stroke-width": "2",
28492
+ disabled:
28493
+ !_vm.activeArtifact.isPreviewable,
28494
+ title: "Split view",
28495
+ },
28496
+ on: {
28497
+ click: function ($event) {
28498
+ return _vm.setViewMode("split")
28499
+ },
27928
28500
  },
27929
28501
  },
27930
28502
  [
27931
- _c("rect", {
27932
- attrs: {
27933
- x: "3",
27934
- y: "3",
27935
- width: "18",
27936
- height: "18",
27937
- rx: "2",
28503
+ _c(
28504
+ "svg",
28505
+ {
28506
+ attrs: {
28507
+ viewBox: "0 0 24 24",
28508
+ fill: "none",
28509
+ stroke: "currentColor",
28510
+ "stroke-width": "2",
28511
+ },
27938
28512
  },
27939
- }),
27940
- _vm._v(" "),
27941
- _c("line", {
27942
- attrs: {
27943
- x1: "12",
27944
- y1: "3",
27945
- x2: "12",
27946
- y2: "21",
28513
+ [
28514
+ _c("rect", {
28515
+ attrs: {
28516
+ x: "3",
28517
+ y: "3",
28518
+ width: "18",
28519
+ height: "18",
28520
+ rx: "2",
28521
+ },
28522
+ }),
28523
+ _vm._v(" "),
28524
+ _c("line", {
28525
+ attrs: {
28526
+ x1: "12",
28527
+ y1: "3",
28528
+ x2: "12",
28529
+ y2: "21",
28530
+ },
28531
+ }),
28532
+ ]
28533
+ ),
28534
+ ]
28535
+ )
28536
+ : _vm._e(),
28537
+ _vm._v(" "),
28538
+ _vm.activeArtifact.tabs &&
28539
+ _vm.activeArtifact.tabs.includes("edit") &&
28540
+ _vm.isEditorAvailable
28541
+ ? _c(
28542
+ "button",
28543
+ {
28544
+ staticClass: "artifactuse-panel__tab",
28545
+ class: {
28546
+ "artifactuse-panel__tab--active":
28547
+ _vm.state.viewMode === "edit",
28548
+ },
28549
+ attrs: { title: "Edit" },
28550
+ on: {
28551
+ click: function ($event) {
28552
+ return _vm.setViewMode("edit")
27947
28553
  },
27948
- }),
28554
+ },
28555
+ },
28556
+ [
28557
+ _c(
28558
+ "svg",
28559
+ {
28560
+ attrs: {
28561
+ viewBox: "0 0 24 24",
28562
+ fill: "none",
28563
+ stroke: "currentColor",
28564
+ "stroke-width": "2",
28565
+ },
28566
+ },
28567
+ [
28568
+ _c("path", {
28569
+ attrs: {
28570
+ d: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7",
28571
+ },
28572
+ }),
28573
+ _vm._v(" "),
28574
+ _c("path", {
28575
+ attrs: {
28576
+ d: "M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z",
28577
+ },
28578
+ }),
28579
+ ]
28580
+ ),
27949
28581
  ]
27950
- ),
27951
- ]
27952
- ),
28582
+ )
28583
+ : _vm._e(),
27953
28584
  ]
27954
28585
  ),
27955
28586
  _vm._v(" "),
@@ -27957,6 +28588,48 @@ var __vue_render__$1 = function () {
27957
28588
  "div",
27958
28589
  { staticClass: "artifactuse-panel__actions" },
27959
28590
  [
28591
+ _vm.state.viewMode === "edit"
28592
+ ? _c(
28593
+ "button",
28594
+ {
28595
+ staticClass:
28596
+ "artifactuse-panel__action artifactuse-panel__action--save",
28597
+ attrs: { title: "Save" },
28598
+ on: { click: _vm.handleEditorSave },
28599
+ },
28600
+ [
28601
+ _c(
28602
+ "svg",
28603
+ {
28604
+ attrs: {
28605
+ viewBox: "0 0 24 24",
28606
+ fill: "none",
28607
+ stroke: "currentColor",
28608
+ "stroke-width": "2",
28609
+ },
28610
+ },
28611
+ [
28612
+ _c("path", {
28613
+ attrs: {
28614
+ d: "M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z",
28615
+ },
28616
+ }),
28617
+ _vm._v(" "),
28618
+ _c("polyline", {
28619
+ attrs: {
28620
+ points: "17 21 17 13 7 13 7 21",
28621
+ },
28622
+ }),
28623
+ _vm._v(" "),
28624
+ _c("polyline", {
28625
+ attrs: { points: "7 3 7 8 15 8" },
28626
+ }),
28627
+ ]
28628
+ ),
28629
+ ]
28630
+ )
28631
+ : _vm._e(),
28632
+ _vm._v(" "),
27960
28633
  _c(
27961
28634
  "button",
27962
28635
  {
@@ -28166,6 +28839,8 @@ var __vue_render__$1 = function () {
28166
28839
  src: _vm.panelUrl,
28167
28840
  sandbox:
28168
28841
  "allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-downloads allow-top-navigation-by-user-activation",
28842
+ allow:
28843
+ "camera; microphone; fullscreen; geolocation; display-capture; autoplay; clipboard-write",
28169
28844
  },
28170
28845
  on: {
28171
28846
  load: _vm.handleIframeLoad,
@@ -28305,6 +28980,28 @@ var __vue_render__$1 = function () {
28305
28980
  ),
28306
28981
  ]
28307
28982
  ),
28983
+ _vm._v(" "),
28984
+ _c(
28985
+ "div",
28986
+ {
28987
+ directives: [
28988
+ {
28989
+ name: "show",
28990
+ rawName: "v-show",
28991
+ value: _vm.state.viewMode === "edit",
28992
+ expression: "state.viewMode === 'edit'",
28993
+ },
28994
+ ],
28995
+ staticClass: "artifactuse-panel__edit",
28996
+ },
28997
+ [
28998
+ _c("div", {
28999
+ ref: "editorContainerRef",
29000
+ staticClass:
29001
+ "artifactuse-panel__editor-container",
29002
+ }),
29003
+ ]
29004
+ ),
28308
29005
  ]
28309
29006
  ),
28310
29007
  _vm._v(" "),