artifactuse 0.1.21 → 0.1.23

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,35 @@ 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
+ if (options.panelUrl) artifact.panelUrl = options.panelUrl;
6825
+ state.addArtifact(artifact);
6826
+ openArtifact(artifact);
6827
+ if (options.viewMode) state.setViewMode(options.viewMode);
6828
+ else if (options.tabs) state.setViewMode(options.tabs[0]);
6829
+ return artifact;
6830
+ }
6831
+
6368
6832
  /**
6369
6833
  * Close panel
6370
6834
  */
@@ -6417,7 +6881,10 @@ function createArtifactuse(userConfig = {}) {
6417
6881
  */
6418
6882
  function getPanelUrl(artifact, options = {}) {
6419
6883
  if (!artifact) return null;
6420
-
6884
+
6885
+ // Custom panel URL takes priority over registry lookup
6886
+ if (artifact.panelUrl) return artifact.panelUrl;
6887
+
6421
6888
  const { type, language } = artifact;
6422
6889
 
6423
6890
  // Build options for panel URL
@@ -6590,6 +7057,8 @@ function createArtifactuse(userConfig = {}) {
6590
7057
 
6591
7058
  // Panel control
6592
7059
  openArtifact,
7060
+ openFile,
7061
+ openCode,
6593
7062
  closePanel,
6594
7063
  togglePanel,
6595
7064
  toggleFullscreen,
@@ -6615,6 +7084,9 @@ function createArtifactuse(userConfig = {}) {
6615
7084
  off,
6616
7085
  emit,
6617
7086
 
7087
+ // Editor (CodeMirror integration)
7088
+ editor,
7089
+
6618
7090
  // Bridge (for advanced use)
6619
7091
  bridge,
6620
7092
 
@@ -6768,6 +7240,8 @@ function provideArtifactuse(config = {}) {
6768
7240
  processMessage: instance.processMessage,
6769
7241
  initializeContent: instance.initializeContent,
6770
7242
  openArtifact: instance.openArtifact,
7243
+ openFile: instance.openFile,
7244
+ openCode: instance.openCode,
6771
7245
  closePanel: instance.closePanel,
6772
7246
  togglePanel: instance.togglePanel,
6773
7247
  toggleFullscreen: instance.toggleFullscreen,
@@ -6886,6 +7360,8 @@ function createArtifactuseComposable(config = {}) {
6886
7360
  processMessage: instance.processMessage,
6887
7361
  initializeContent: instance.initializeContent,
6888
7362
  openArtifact: instance.openArtifact,
7363
+ openFile: instance.openFile,
7364
+ openCode: instance.openCode,
6889
7365
  closePanel: instance.closePanel,
6890
7366
  togglePanel: instance.togglePanel,
6891
7367
  toggleFullscreen: instance.toggleFullscreen,
@@ -26386,6 +26862,41 @@ var JSZip = /*@__PURE__*/getDefaultExportFromCjs(libExports);
26386
26862
  //
26387
26863
  //
26388
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
+ //
26896
+ //
26897
+ //
26898
+ //
26899
+ //
26389
26900
 
26390
26901
 
26391
26902
  var script$1 = defineComponent({
@@ -26419,6 +26930,7 @@ var script$1 = defineComponent({
26419
26930
  const contentRef = ref(null);
26420
26931
  const lineNumbersRef = ref(null);
26421
26932
  const codeScrollRef = ref(null);
26933
+ const editorContainerRef = ref(null);
26422
26934
 
26423
26935
  // State
26424
26936
  const copied = ref(false);
@@ -26449,7 +26961,11 @@ var script$1 = defineComponent({
26449
26961
  // Timers
26450
26962
  let streamEndTimer = null;
26451
26963
  let iframeLoadTimer = null;
26452
-
26964
+
26965
+ // Editor (CodeMirror) instance — not reactive to avoid proxy overhead
26966
+ let editorInstance = null;
26967
+ const isEditorAvailable = computed(() => instance.editor?.isAvailable() || false);
26968
+
26453
26969
  // Computed
26454
26970
  const languageDisplay = computed(() => {
26455
26971
  if (!activeArtifact.value) return '';
@@ -26570,7 +27086,35 @@ var script$1 = defineComponent({
26570
27086
  highlightCode();
26571
27087
  });
26572
27088
  }
26573
-
27089
+
27090
+ // Editor (CodeMirror) functions
27091
+ function initEditor() {
27092
+ if (!isEditorAvailable.value || !editorContainerRef.value || !activeArtifact.value) return;
27093
+ destroyEditor();
27094
+ editorInstance = instance.editor.create(editorContainerRef.value, {
27095
+ code: activeArtifact.value.code || '',
27096
+ language: activeArtifact.value.language || 'plaintext',
27097
+ sdkTheme: instance.getTheme(),
27098
+ });
27099
+ }
27100
+
27101
+ function destroyEditor() {
27102
+ if (editorInstance) {
27103
+ editorInstance.destroy();
27104
+ editorInstance = null;
27105
+ }
27106
+ }
27107
+
27108
+ function handleEditorSave() {
27109
+ if (!editorInstance || !activeArtifact.value) return;
27110
+ const code = editorInstance.getCode();
27111
+ instance.emit('edit:save', {
27112
+ artifactId: activeArtifact.value.id,
27113
+ artifact: activeArtifact.value,
27114
+ code,
27115
+ });
27116
+ }
27117
+
26574
27118
  function handleIframeLoad() {
26575
27119
  clearTimeout(iframeLoadTimer);
26576
27120
  iframeLoading.value = false;
@@ -26975,10 +27519,19 @@ var script$1 = defineComponent({
26975
27519
  }
26976
27520
 
26977
27521
  // Check if code changed
26978
- if (!oldArtifact || newArtifact.code !== oldArtifact.code) {
27522
+ if (!oldArtifact || newArtifact.id !== oldArtifact.id || newArtifact.code !== oldArtifact.code) {
26979
27523
  // Update code view immediately on each change
26980
27524
  updateCodeView();
26981
27525
 
27526
+ // Update editor content if in edit mode
27527
+ if (state.viewMode === 'edit') {
27528
+ if (!oldArtifact || newArtifact.id !== oldArtifact.id) {
27529
+ nextTick(() => initEditor());
27530
+ } else if (editorInstance) {
27531
+ editorInstance.setCode(newArtifact.code || '');
27532
+ }
27533
+ }
27534
+
26982
27535
  // Debounce iframe updates only
26983
27536
  clearTimeout(streamEndTimer);
26984
27537
  streamEndTimer = setTimeout(() => {
@@ -26995,8 +27548,21 @@ var script$1 = defineComponent({
26995
27548
  if (mode === 'code' || mode === 'split') {
26996
27549
  updateCodeView();
26997
27550
  }
27551
+ if (mode === 'edit') {
27552
+ nextTick(() => initEditor());
27553
+ }
26998
27554
  });
26999
-
27555
+
27556
+ // Watch panel open state — v-if destroys DOM on close, need to re-render code on reopen
27557
+ watch(() => state.isPanelOpen, (isOpen) => {
27558
+ if (isOpen && activeArtifact.value) {
27559
+ updateCodeView();
27560
+ }
27561
+ if (!isOpen) {
27562
+ destroyEditor();
27563
+ }
27564
+ });
27565
+
27000
27566
  onMounted(() => {
27001
27567
  instance.on('ai:request', (data) => emit('ai-request', data));
27002
27568
  instance.on('save:request', (data) => emit('save', data));
@@ -27012,6 +27578,7 @@ var script$1 = defineComponent({
27012
27578
  onUnmounted(() => {
27013
27579
  stopPanelResize();
27014
27580
  stopSplitResize();
27581
+ destroyEditor();
27015
27582
  document.removeEventListener('click', handleClickOutside);
27016
27583
  clearTimeout(streamEndTimer);
27017
27584
  clearTimeout(iframeLoadTimer);
@@ -27033,6 +27600,7 @@ var script$1 = defineComponent({
27033
27600
  contentRef,
27034
27601
  lineNumbersRef,
27035
27602
  codeScrollRef,
27603
+ editorContainerRef,
27036
27604
 
27037
27605
  // State
27038
27606
  copied,
@@ -27069,6 +27637,7 @@ var script$1 = defineComponent({
27069
27637
  panelClasses,
27070
27638
  sharingEnabled,
27071
27639
  isAuthenticated,
27640
+ isEditorAvailable,
27072
27641
 
27073
27642
  // Methods
27074
27643
  handleIframeLoad,
@@ -27084,6 +27653,7 @@ var script$1 = defineComponent({
27084
27653
  getArtifactIconHtml,
27085
27654
  startPanelResize,
27086
27655
  startSplitResize,
27656
+ handleEditorSave,
27087
27657
 
27088
27658
  // Share methods
27089
27659
  toggleSharePopup,
@@ -27816,141 +28386,205 @@ var __vue_render__$1 = function () {
27816
28386
  "div",
27817
28387
  { staticClass: "artifactuse-panel__tabs" },
27818
28388
  [
27819
- _c(
27820
- "button",
27821
- {
27822
- staticClass: "artifactuse-panel__tab",
27823
- class: {
27824
- "artifactuse-panel__tab--active":
27825
- _vm.state.viewMode === "preview",
27826
- },
27827
- attrs: {
27828
- disabled: !_vm.activeArtifact.isPreviewable,
27829
- title: "Preview",
27830
- },
27831
- on: {
27832
- click: function ($event) {
27833
- return _vm.setViewMode("preview")
27834
- },
27835
- },
27836
- },
27837
- [
27838
- _c(
27839
- "svg",
28389
+ !_vm.activeArtifact.tabs ||
28390
+ _vm.activeArtifact.tabs.includes("preview")
28391
+ ? _c(
28392
+ "button",
27840
28393
  {
28394
+ staticClass: "artifactuse-panel__tab",
28395
+ class: {
28396
+ "artifactuse-panel__tab--active":
28397
+ _vm.state.viewMode === "preview",
28398
+ },
27841
28399
  attrs: {
27842
- viewBox: "0 0 24 24",
27843
- fill: "none",
27844
- stroke: "currentColor",
27845
- "stroke-width": "2",
28400
+ disabled:
28401
+ !_vm.activeArtifact.isPreviewable,
28402
+ title: "Preview",
28403
+ },
28404
+ on: {
28405
+ click: function ($event) {
28406
+ return _vm.setViewMode("preview")
28407
+ },
27846
28408
  },
27847
28409
  },
27848
28410
  [
27849
- _c("path", {
27850
- attrs: {
27851
- d: "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z",
28411
+ _c(
28412
+ "svg",
28413
+ {
28414
+ attrs: {
28415
+ viewBox: "0 0 24 24",
28416
+ fill: "none",
28417
+ stroke: "currentColor",
28418
+ "stroke-width": "2",
28419
+ },
27852
28420
  },
27853
- }),
27854
- _vm._v(" "),
27855
- _c("circle", {
27856
- attrs: { cx: "12", cy: "12", r: "3" },
27857
- }),
28421
+ [
28422
+ _c("path", {
28423
+ attrs: {
28424
+ d: "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z",
28425
+ },
28426
+ }),
28427
+ _vm._v(" "),
28428
+ _c("circle", {
28429
+ attrs: {
28430
+ cx: "12",
28431
+ cy: "12",
28432
+ r: "3",
28433
+ },
28434
+ }),
28435
+ ]
28436
+ ),
27858
28437
  ]
27859
- ),
27860
- ]
27861
- ),
28438
+ )
28439
+ : _vm._e(),
27862
28440
  _vm._v(" "),
27863
- _c(
27864
- "button",
27865
- {
27866
- staticClass: "artifactuse-panel__tab",
27867
- class: {
27868
- "artifactuse-panel__tab--active":
27869
- _vm.state.viewMode === "code",
27870
- },
27871
- attrs: { title: "Code" },
27872
- on: {
27873
- click: function ($event) {
27874
- return _vm.setViewMode("code")
27875
- },
27876
- },
27877
- },
27878
- [
27879
- _c(
27880
- "svg",
28441
+ !_vm.activeArtifact.tabs ||
28442
+ _vm.activeArtifact.tabs.includes("code")
28443
+ ? _c(
28444
+ "button",
27881
28445
  {
27882
- attrs: {
27883
- viewBox: "0 0 24 24",
27884
- fill: "none",
27885
- stroke: "currentColor",
27886
- "stroke-width": "2",
28446
+ staticClass: "artifactuse-panel__tab",
28447
+ class: {
28448
+ "artifactuse-panel__tab--active":
28449
+ _vm.state.viewMode === "code",
28450
+ },
28451
+ attrs: { title: "Code" },
28452
+ on: {
28453
+ click: function ($event) {
28454
+ return _vm.setViewMode("code")
28455
+ },
27887
28456
  },
27888
28457
  },
27889
28458
  [
27890
- _c("polyline", {
27891
- attrs: { points: "16 18 22 12 16 6" },
27892
- }),
27893
- _vm._v(" "),
27894
- _c("polyline", {
27895
- attrs: { points: "8 6 2 12 8 18" },
27896
- }),
28459
+ _c(
28460
+ "svg",
28461
+ {
28462
+ attrs: {
28463
+ viewBox: "0 0 24 24",
28464
+ fill: "none",
28465
+ stroke: "currentColor",
28466
+ "stroke-width": "2",
28467
+ },
28468
+ },
28469
+ [
28470
+ _c("polyline", {
28471
+ attrs: {
28472
+ points: "16 18 22 12 16 6",
28473
+ },
28474
+ }),
28475
+ _vm._v(" "),
28476
+ _c("polyline", {
28477
+ attrs: { points: "8 6 2 12 8 18" },
28478
+ }),
28479
+ ]
28480
+ ),
27897
28481
  ]
27898
- ),
27899
- ]
27900
- ),
28482
+ )
28483
+ : _vm._e(),
27901
28484
  _vm._v(" "),
27902
- _c(
27903
- "button",
27904
- {
27905
- staticClass: "artifactuse-panel__tab",
27906
- class: {
27907
- "artifactuse-panel__tab--active":
27908
- _vm.state.viewMode === "split",
27909
- },
27910
- attrs: {
27911
- disabled: !_vm.activeArtifact.isPreviewable,
27912
- title: "Split view",
27913
- },
27914
- on: {
27915
- click: function ($event) {
27916
- return _vm.setViewMode("split")
27917
- },
27918
- },
27919
- },
27920
- [
27921
- _c(
27922
- "svg",
28485
+ !_vm.activeArtifact.tabs ||
28486
+ _vm.activeArtifact.tabs.includes("split")
28487
+ ? _c(
28488
+ "button",
27923
28489
  {
28490
+ staticClass: "artifactuse-panel__tab",
28491
+ class: {
28492
+ "artifactuse-panel__tab--active":
28493
+ _vm.state.viewMode === "split",
28494
+ },
27924
28495
  attrs: {
27925
- viewBox: "0 0 24 24",
27926
- fill: "none",
27927
- stroke: "currentColor",
27928
- "stroke-width": "2",
28496
+ disabled:
28497
+ !_vm.activeArtifact.isPreviewable,
28498
+ title: "Split view",
28499
+ },
28500
+ on: {
28501
+ click: function ($event) {
28502
+ return _vm.setViewMode("split")
28503
+ },
27929
28504
  },
27930
28505
  },
27931
28506
  [
27932
- _c("rect", {
27933
- attrs: {
27934
- x: "3",
27935
- y: "3",
27936
- width: "18",
27937
- height: "18",
27938
- rx: "2",
28507
+ _c(
28508
+ "svg",
28509
+ {
28510
+ attrs: {
28511
+ viewBox: "0 0 24 24",
28512
+ fill: "none",
28513
+ stroke: "currentColor",
28514
+ "stroke-width": "2",
28515
+ },
27939
28516
  },
27940
- }),
27941
- _vm._v(" "),
27942
- _c("line", {
27943
- attrs: {
27944
- x1: "12",
27945
- y1: "3",
27946
- x2: "12",
27947
- y2: "21",
28517
+ [
28518
+ _c("rect", {
28519
+ attrs: {
28520
+ x: "3",
28521
+ y: "3",
28522
+ width: "18",
28523
+ height: "18",
28524
+ rx: "2",
28525
+ },
28526
+ }),
28527
+ _vm._v(" "),
28528
+ _c("line", {
28529
+ attrs: {
28530
+ x1: "12",
28531
+ y1: "3",
28532
+ x2: "12",
28533
+ y2: "21",
28534
+ },
28535
+ }),
28536
+ ]
28537
+ ),
28538
+ ]
28539
+ )
28540
+ : _vm._e(),
28541
+ _vm._v(" "),
28542
+ _vm.activeArtifact.tabs &&
28543
+ _vm.activeArtifact.tabs.includes("edit") &&
28544
+ _vm.isEditorAvailable
28545
+ ? _c(
28546
+ "button",
28547
+ {
28548
+ staticClass: "artifactuse-panel__tab",
28549
+ class: {
28550
+ "artifactuse-panel__tab--active":
28551
+ _vm.state.viewMode === "edit",
28552
+ },
28553
+ attrs: { title: "Edit" },
28554
+ on: {
28555
+ click: function ($event) {
28556
+ return _vm.setViewMode("edit")
27948
28557
  },
27949
- }),
28558
+ },
28559
+ },
28560
+ [
28561
+ _c(
28562
+ "svg",
28563
+ {
28564
+ attrs: {
28565
+ viewBox: "0 0 24 24",
28566
+ fill: "none",
28567
+ stroke: "currentColor",
28568
+ "stroke-width": "2",
28569
+ },
28570
+ },
28571
+ [
28572
+ _c("path", {
28573
+ attrs: {
28574
+ d: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7",
28575
+ },
28576
+ }),
28577
+ _vm._v(" "),
28578
+ _c("path", {
28579
+ attrs: {
28580
+ d: "M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z",
28581
+ },
28582
+ }),
28583
+ ]
28584
+ ),
27950
28585
  ]
27951
- ),
27952
- ]
27953
- ),
28586
+ )
28587
+ : _vm._e(),
27954
28588
  ]
27955
28589
  ),
27956
28590
  _vm._v(" "),
@@ -27958,6 +28592,48 @@ var __vue_render__$1 = function () {
27958
28592
  "div",
27959
28593
  { staticClass: "artifactuse-panel__actions" },
27960
28594
  [
28595
+ _vm.state.viewMode === "edit"
28596
+ ? _c(
28597
+ "button",
28598
+ {
28599
+ staticClass:
28600
+ "artifactuse-panel__action artifactuse-panel__action--save",
28601
+ attrs: { title: "Save" },
28602
+ on: { click: _vm.handleEditorSave },
28603
+ },
28604
+ [
28605
+ _c(
28606
+ "svg",
28607
+ {
28608
+ attrs: {
28609
+ viewBox: "0 0 24 24",
28610
+ fill: "none",
28611
+ stroke: "currentColor",
28612
+ "stroke-width": "2",
28613
+ },
28614
+ },
28615
+ [
28616
+ _c("path", {
28617
+ attrs: {
28618
+ d: "M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z",
28619
+ },
28620
+ }),
28621
+ _vm._v(" "),
28622
+ _c("polyline", {
28623
+ attrs: {
28624
+ points: "17 21 17 13 7 13 7 21",
28625
+ },
28626
+ }),
28627
+ _vm._v(" "),
28628
+ _c("polyline", {
28629
+ attrs: { points: "7 3 7 8 15 8" },
28630
+ }),
28631
+ ]
28632
+ ),
28633
+ ]
28634
+ )
28635
+ : _vm._e(),
28636
+ _vm._v(" "),
27961
28637
  _c(
27962
28638
  "button",
27963
28639
  {
@@ -28308,6 +28984,28 @@ var __vue_render__$1 = function () {
28308
28984
  ),
28309
28985
  ]
28310
28986
  ),
28987
+ _vm._v(" "),
28988
+ _c(
28989
+ "div",
28990
+ {
28991
+ directives: [
28992
+ {
28993
+ name: "show",
28994
+ rawName: "v-show",
28995
+ value: _vm.state.viewMode === "edit",
28996
+ expression: "state.viewMode === 'edit'",
28997
+ },
28998
+ ],
28999
+ staticClass: "artifactuse-panel__edit",
29000
+ },
29001
+ [
29002
+ _c("div", {
29003
+ ref: "editorContainerRef",
29004
+ staticClass:
29005
+ "artifactuse-panel__editor-container",
29006
+ }),
29007
+ ]
29008
+ ),
28311
29009
  ]
28312
29010
  ),
28313
29011
  _vm._v(" "),