artifactuse 0.2.3 → 0.2.5

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.
@@ -83,20 +83,7 @@ export function provideArtifactuse(config?: {
83
83
  };
84
84
  initializeContent: (container?: Document) => Promise<void>;
85
85
  openArtifact: (artifact: any) => void;
86
- openFile: (filename: any, code: any, options?: {}) => {
87
- id: string;
88
- messageId: string;
89
- type: string;
90
- language: string;
91
- title: any;
92
- code: string;
93
- isInline: boolean;
94
- isPreviewable: boolean;
95
- isPanelArtifact: boolean;
96
- size: number;
97
- lineCount: number;
98
- createdAt: string;
99
- };
86
+ openFile: (filename: any, code: any, options?: {}) => any;
100
87
  openCode: (code: any, language: any, options?: {}) => {
101
88
  id: string;
102
89
  messageId: string;
@@ -207,20 +194,7 @@ export function provideArtifactuse(config?: {
207
194
  };
208
195
  initializeContent: (container?: Document) => Promise<void>;
209
196
  openArtifact: (artifact: any) => void;
210
- openFile: (filename: any, code: any, options?: {}) => {
211
- id: string;
212
- messageId: string;
213
- type: string;
214
- language: string;
215
- title: any;
216
- code: string;
217
- isInline: boolean;
218
- isPreviewable: boolean;
219
- isPanelArtifact: boolean;
220
- size: number;
221
- lineCount: number;
222
- createdAt: string;
223
- };
197
+ openFile: (filename: any, code: any, options?: {}) => any;
224
198
  openCode: (code: any, language: any, options?: {}) => {
225
199
  id: string;
226
200
  messageId: string;
@@ -317,20 +291,7 @@ export function createArtifactuseComposable(config?: {}): {
317
291
  };
318
292
  initializeContent: (container?: Document) => Promise<void>;
319
293
  openArtifact: (artifact: any) => void;
320
- openFile: (filename: any, code: any, options?: {}) => {
321
- id: string;
322
- messageId: string;
323
- type: string;
324
- language: string;
325
- title: any;
326
- code: string;
327
- isInline: boolean;
328
- isPreviewable: boolean;
329
- isPanelArtifact: boolean;
330
- size: number;
331
- lineCount: number;
332
- createdAt: string;
333
- };
294
+ openFile: (filename: any, code: any, options?: {}) => any;
334
295
  openCode: (code: any, language: any, options?: {}) => {
335
296
  id: string;
336
297
  messageId: string;
@@ -441,20 +402,7 @@ export function createArtifactuseComposable(config?: {}): {
441
402
  };
442
403
  initializeContent: (container?: Document) => Promise<void>;
443
404
  openArtifact: (artifact: any) => void;
444
- openFile: (filename: any, code: any, options?: {}) => {
445
- id: string;
446
- messageId: string;
447
- type: string;
448
- language: string;
449
- title: any;
450
- code: string;
451
- isInline: boolean;
452
- isPreviewable: boolean;
453
- isPanelArtifact: boolean;
454
- size: number;
455
- lineCount: number;
456
- createdAt: string;
457
- };
405
+ openFile: (filename: any, code: any, options?: {}) => any;
458
406
  openCode: (code: any, language: any, options?: {}) => {
459
407
  id: string;
460
408
  messageId: string;
@@ -6,9 +6,160 @@ import require$$2 from 'events';
6
6
  import require$$0$1 from 'buffer';
7
7
  import require$$1 from 'util';
8
8
 
9
+ // artifactuse/core/highlight.js
10
+ // Optional Prism.js integration for syntax highlighting
11
+ // Users must provide Prism.js themselves
12
+
13
+ /**
14
+ * Check if Prism is available
15
+ */
16
+ function isPrismAvailable() {
17
+ return typeof window !== 'undefined' && window.Prism;
18
+ }
19
+
20
+ /**
21
+ * Highlight all code blocks in a container
22
+ * @param {HTMLElement|string} container - Container element or selector
23
+ */
24
+ function highlightAll(container) {
25
+ if (!isPrismAvailable()) {
26
+ console.warn('Artifactuse: Prism.js not found. Install and include Prism.js for syntax highlighting.');
27
+ return;
28
+ }
29
+
30
+ const element = typeof container === 'string'
31
+ ? document.querySelector(container)
32
+ : container;
33
+
34
+ if (!element) return;
35
+
36
+ // Find all code blocks
37
+ const codeBlocks = element.querySelectorAll('pre code');
38
+
39
+ codeBlocks.forEach(block => {
40
+ // Skip if already highlighted
41
+ if (block.classList.contains('prism-highlighted')) return;
42
+
43
+ // Detect language from class
44
+ const language = detectLanguage(block);
45
+
46
+ if (language) {
47
+ // Add language class if not present
48
+ if (!block.className.includes(`language-${language}`)) {
49
+ block.classList.add(`language-${language}`);
50
+ }
51
+
52
+ // Highlight
53
+ window.Prism.highlightElement(block);
54
+ block.classList.add('prism-highlighted');
55
+ }
56
+ });
57
+ }
58
+
59
+ /**
60
+ * Highlight code string and return HTML
61
+ * @param {string} code - Code to highlight
62
+ * @param {string} language - Language identifier
63
+ * @returns {string} - Highlighted HTML
64
+ */
65
+ function highlightCode(code, language) {
66
+ if (!isPrismAvailable()) return escapeHtml$4(code);
67
+
68
+ const grammar = window.Prism.languages[language];
69
+
70
+ if (!grammar) {
71
+ return escapeHtml$4(code);
72
+ }
73
+
74
+ return window.Prism.highlight(code, grammar, language);
75
+ }
76
+
77
+ /**
78
+ * Detect language from element classes
79
+ * @param {HTMLElement} element - Code element
80
+ * @returns {string|null} - Detected language or null
81
+ */
82
+ function detectLanguage(element) {
83
+ // Check element classes
84
+ const classes = element.className.split(/\s+/);
85
+
86
+ for (const cls of classes) {
87
+ // Match language-xxx or lang-xxx
88
+ const match = cls.match(/^(?:language-|lang-)(.+)$/);
89
+ if (match) {
90
+ return normalizeLanguage(match[1]);
91
+ }
92
+ }
93
+
94
+ // Check parent <pre> classes
95
+ const pre = element.closest('pre');
96
+ if (pre) {
97
+ const preClasses = pre.className.split(/\s+/);
98
+ for (const cls of preClasses) {
99
+ const match = cls.match(/^(?:language-|lang-)(.+)$/);
100
+ if (match) {
101
+ return normalizeLanguage(match[1]);
102
+ }
103
+ }
104
+ }
105
+
106
+ // Check data-language attribute
107
+ const dataLang = element.dataset.language || pre?.dataset.language;
108
+ if (dataLang) {
109
+ return normalizeLanguage(dataLang);
110
+ }
111
+
112
+ return null;
113
+ }
114
+
115
+ /**
116
+ * Normalize language identifier to Prism-compatible name
117
+ */
118
+ function normalizeLanguage(lang) {
119
+ const aliases = {
120
+ 'js': 'javascript',
121
+ 'ts': 'typescript',
122
+ 'py': 'python',
123
+ 'rb': 'ruby',
124
+ 'sh': 'bash',
125
+ 'shell': 'bash',
126
+ 'zsh': 'bash',
127
+ 'yml': 'yaml',
128
+ 'md': 'markdown',
129
+ 'html': 'markup',
130
+ 'xml': 'markup',
131
+ 'svg': 'markup',
132
+ 'vue': 'markup',
133
+ 'jsx': 'jsx',
134
+ 'tsx': 'tsx',
135
+ 'c++': 'cpp',
136
+ 'c#': 'csharp',
137
+ 'cs': 'csharp',
138
+ 'f#': 'fsharp',
139
+ 'objective-c': 'objectivec',
140
+ 'objc': 'objectivec',
141
+ };
142
+
143
+ const normalized = lang.toLowerCase();
144
+ return aliases[normalized] || normalized;
145
+ }
146
+
147
+ /**
148
+ * Escape HTML special characters
149
+ */
150
+ function escapeHtml$4(str) {
151
+ return str
152
+ .replace(/&/g, '&amp;')
153
+ .replace(/</g, '&lt;')
154
+ .replace(/>/g, '&gt;')
155
+ .replace(/"/g, '&quot;')
156
+ .replace(/'/g, '&#039;');
157
+ }
158
+
9
159
  // artifactuse/core/detector.js
10
160
  // Artifact detection and extraction from AI responses
11
161
 
162
+
12
163
  /**
13
164
  * Artifact type definitions
14
165
  */
@@ -447,18 +598,6 @@ function decodeHtml(encoded) {
447
598
  .replace(/&nbsp;/g, ' ');
448
599
  }
449
600
 
450
- /**
451
- * Encode HTML for safe embedding
452
- */
453
- function encodeHtml(code) {
454
- return code
455
- .replace(/&/g, '&amp;')
456
- .replace(/"/g, '&quot;')
457
- .replace(/'/g, '&#39;')
458
- .replace(/</g, '&lt;')
459
- .replace(/>/g, '&gt;');
460
- }
461
-
462
601
  /**
463
602
  * Encode JSON for safe embedding in HTML attributes using Base64
464
603
  */
@@ -624,16 +763,18 @@ function createInlinePreview(artifact, code, langLower, inlinePreview) {
624
763
  // Separate markers (+/-/space) from code content
625
764
  const markers = truncDiffLines.map(l => l[0] || ' ');
626
765
  const codeLines = truncDiffLines.map(l => l.slice(1));
627
- const encoded = encodeHtml(codeLines.join('\n'));
628
- const isTruncated = allDiffLines.length > maxLines;
629
766
  const actualLang = diffData.language || 'plaintext';
767
+ const normalizedActualLang = normalizeLanguage(actualLang);
768
+ const encoded = highlightCode(codeLines.join('\n'), normalizedActualLang);
769
+ const isPreHighlighted = isPrismAvailable() && window.Prism.languages[normalizedActualLang];
770
+ const isTruncated = allDiffLines.length > maxLines;
630
771
  const nonClickable = isNonClickable(allDiffLines.length, isTruncated);
631
772
  const staticClass = nonClickable ? ' artifactuse-inline-preview--static' : '';
632
773
  const staticAttr = nonClickable ? ' data-non-clickable="true"' : '';
633
774
  const actionText = getActionLabel('smartdiff', allDiffLines.length);
634
775
 
635
776
  return `<div class="artifactuse-inline-preview${isTruncated ? ' artifactuse-inline-preview--truncated' : ''}${staticClass}" data-artifact-id="${artifact.id}" data-smartdiff="true" data-smartdiff-markers="${markers.join(',')}"${staticAttr}>`
636
- + `<pre class="artifactuse-inline-preview__pre"><code class="language-${actualLang}">${encoded}</code></pre>`
777
+ + `<pre class="artifactuse-inline-preview__pre${isPreHighlighted ? ' language-' + actualLang : ''}"><code class="language-${actualLang}${isPreHighlighted ? ' prism-highlighted' : ''}">${encoded}</code></pre>`
637
778
  + (isTruncated ? `<div class="artifactuse-inline-preview__fade"><span class="artifactuse-inline-preview__action">${actionText}</span></div>` : '')
638
779
  + `</div>`;
639
780
  } catch {
@@ -647,7 +788,9 @@ function createInlinePreview(artifact, code, langLower, inlinePreview) {
647
788
 
648
789
  const lines = previewCode.split('\n');
649
790
  const truncated = lines.slice(0, maxLines).join('\n');
650
- const encoded = encodeHtml(truncated);
791
+ const normalizedLang = normalizeLanguage(previewLang);
792
+ const encoded = highlightCode(truncated, normalizedLang);
793
+ const isPreHighlighted = isPrismAvailable() && window.Prism.languages[normalizedLang];
651
794
  const isTruncated = lines.length > maxLines;
652
795
  const nonClickable = isNonClickable(lines.length, isTruncated);
653
796
  const staticClass = nonClickable ? ' artifactuse-inline-preview--static' : '';
@@ -655,7 +798,7 @@ function createInlinePreview(artifact, code, langLower, inlinePreview) {
655
798
  const actionText = getActionLabel(langLower, lines.length);
656
799
 
657
800
  return `<div class="artifactuse-inline-preview${isTruncated ? ' artifactuse-inline-preview--truncated' : ''}${staticClass}" data-artifact-id="${artifact.id}"${staticAttr}>`
658
- + `<pre class="artifactuse-inline-preview__pre"><code class="language-${previewLang}">${encoded}</code></pre>`
801
+ + `<pre class="artifactuse-inline-preview__pre${isPreHighlighted ? ' language-' + previewLang : ''}"><code class="language-${previewLang}${isPreHighlighted ? ' prism-highlighted' : ''}">${encoded}</code></pre>`
659
802
  + (isTruncated ? `<div class="artifactuse-inline-preview__fade"><span class="artifactuse-inline-preview__action">${actionText}</span></div>` : '')
660
803
  + `</div>`;
661
804
  }
@@ -6557,126 +6700,6 @@ function loadKaTeX() {
6557
6700
  return katexLoadingPromise;
6558
6701
  }
6559
6702
 
6560
- // artifactuse/core/highlight.js
6561
- // Optional Prism.js integration for syntax highlighting
6562
- // Users must provide Prism.js themselves
6563
-
6564
- /**
6565
- * Check if Prism is available
6566
- */
6567
- function isPrismAvailable() {
6568
- return typeof window !== 'undefined' && window.Prism;
6569
- }
6570
-
6571
- /**
6572
- * Highlight all code blocks in a container
6573
- * @param {HTMLElement|string} container - Container element or selector
6574
- */
6575
- function highlightAll(container) {
6576
- if (!isPrismAvailable()) {
6577
- console.warn('Artifactuse: Prism.js not found. Install and include Prism.js for syntax highlighting.');
6578
- return;
6579
- }
6580
-
6581
- const element = typeof container === 'string'
6582
- ? document.querySelector(container)
6583
- : container;
6584
-
6585
- if (!element) return;
6586
-
6587
- // Find all code blocks
6588
- const codeBlocks = element.querySelectorAll('pre code');
6589
-
6590
- codeBlocks.forEach(block => {
6591
- // Skip if already highlighted
6592
- if (block.classList.contains('prism-highlighted')) return;
6593
-
6594
- // Detect language from class
6595
- const language = detectLanguage(block);
6596
-
6597
- if (language) {
6598
- // Add language class if not present
6599
- if (!block.className.includes(`language-${language}`)) {
6600
- block.classList.add(`language-${language}`);
6601
- }
6602
-
6603
- // Highlight
6604
- window.Prism.highlightElement(block);
6605
- block.classList.add('prism-highlighted');
6606
- }
6607
- });
6608
- }
6609
-
6610
- /**
6611
- * Detect language from element classes
6612
- * @param {HTMLElement} element - Code element
6613
- * @returns {string|null} - Detected language or null
6614
- */
6615
- function detectLanguage(element) {
6616
- // Check element classes
6617
- const classes = element.className.split(/\s+/);
6618
-
6619
- for (const cls of classes) {
6620
- // Match language-xxx or lang-xxx
6621
- const match = cls.match(/^(?:language-|lang-)(.+)$/);
6622
- if (match) {
6623
- return normalizeLanguage(match[1]);
6624
- }
6625
- }
6626
-
6627
- // Check parent <pre> classes
6628
- const pre = element.closest('pre');
6629
- if (pre) {
6630
- const preClasses = pre.className.split(/\s+/);
6631
- for (const cls of preClasses) {
6632
- const match = cls.match(/^(?:language-|lang-)(.+)$/);
6633
- if (match) {
6634
- return normalizeLanguage(match[1]);
6635
- }
6636
- }
6637
- }
6638
-
6639
- // Check data-language attribute
6640
- const dataLang = element.dataset.language || pre?.dataset.language;
6641
- if (dataLang) {
6642
- return normalizeLanguage(dataLang);
6643
- }
6644
-
6645
- return null;
6646
- }
6647
-
6648
- /**
6649
- * Normalize language identifier to Prism-compatible name
6650
- */
6651
- function normalizeLanguage(lang) {
6652
- const aliases = {
6653
- 'js': 'javascript',
6654
- 'ts': 'typescript',
6655
- 'py': 'python',
6656
- 'rb': 'ruby',
6657
- 'sh': 'bash',
6658
- 'shell': 'bash',
6659
- 'zsh': 'bash',
6660
- 'yml': 'yaml',
6661
- 'md': 'markdown',
6662
- 'html': 'markup',
6663
- 'xml': 'markup',
6664
- 'svg': 'markup',
6665
- 'vue': 'markup',
6666
- 'jsx': 'jsx',
6667
- 'tsx': 'tsx',
6668
- 'c++': 'cpp',
6669
- 'c#': 'csharp',
6670
- 'cs': 'csharp',
6671
- 'f#': 'fsharp',
6672
- 'objective-c': 'objectivec',
6673
- 'objc': 'objectivec',
6674
- };
6675
-
6676
- const normalized = lang.toLowerCase();
6677
- return aliases[normalized] || normalized;
6678
- }
6679
-
6680
6703
  // artifactuse/core/index.js
6681
6704
  // Main entry point for Artifactuse SDK
6682
6705
 
@@ -6839,6 +6862,10 @@ const DEFAULT_CONFIG = {
6839
6862
  // Can be overridden per-component via props
6840
6863
  splitPosition: 50,
6841
6864
 
6865
+ // Show "Open in new tab" button in panel header to open preview URL externally
6866
+ // Can be overridden per-artifact via openFile/openCode options or per-component via props
6867
+ externalPreview: false,
6868
+
6842
6869
  // Enable multi-tab mode (open multiple artifacts as tabs)
6843
6870
  multiTab: false,
6844
6871
 
@@ -7369,6 +7396,17 @@ function createArtifactuse(userConfig = {}) {
7369
7396
  const ext = filename.split('.').pop();
7370
7397
  const language = options.language || getLanguageFromExtension(ext) || ext;
7371
7398
  const title = options.title || filename;
7399
+
7400
+ // Deduplicate: if an artifact with the same title already exists, update + focus it
7401
+ const existing = state.getState().artifacts.find(a => a.title === title && !a.isInline);
7402
+ if (existing) {
7403
+ updateFile(existing.id, code, { ...options, title });
7404
+ openArtifact(existing);
7405
+ if (options.viewMode) state.setViewMode(options.viewMode);
7406
+ else if (options.tabs) state.setViewMode(options.tabs[0]);
7407
+ return state.getArtifact(existing.id);
7408
+ }
7409
+
7372
7410
  return openCode(code, language, { ...options, title });
7373
7411
  }
7374
7412
 
@@ -7384,6 +7422,7 @@ function createArtifactuse(userConfig = {}) {
7384
7422
  artifact.editorLanguage = language;
7385
7423
  if (options.tabs) artifact.tabs = options.tabs;
7386
7424
  if (options.panelUrl) artifact.panelUrl = options.panelUrl;
7425
+ if (options.externalPreview !== undefined) artifact.externalPreview = options.externalPreview;
7387
7426
  state.addArtifact(artifact);
7388
7427
  openArtifact(artifact);
7389
7428
  if (options.viewMode) state.setViewMode(options.viewMode);
@@ -7403,6 +7442,7 @@ function createArtifactuse(userConfig = {}) {
7403
7442
  if (options.title !== undefined) updates.title = options.title;
7404
7443
  if (options.tabs !== undefined) updates.tabs = options.tabs;
7405
7444
  if (options.panelUrl !== undefined) updates.panelUrl = options.panelUrl;
7445
+ if (options.externalPreview !== undefined) updates.externalPreview = options.externalPreview;
7406
7446
 
7407
7447
  state.addArtifact(updates);
7408
7448
  emit('artifact:updated', { artifactId: id, artifact: state.getArtifact(id) });
@@ -27605,6 +27645,18 @@ var JSZip = /*@__PURE__*/getDefaultExportFromCjs(libExports);
27605
27645
  //
27606
27646
  //
27607
27647
  //
27648
+ //
27649
+ //
27650
+ //
27651
+ //
27652
+ //
27653
+ //
27654
+ //
27655
+ //
27656
+ //
27657
+ //
27658
+ //
27659
+ //
27608
27660
 
27609
27661
 
27610
27662
  var script$1 = defineComponent({
@@ -27624,6 +27676,10 @@ var script$1 = defineComponent({
27624
27676
  type: Number,
27625
27677
  default: undefined,
27626
27678
  },
27679
+ externalPreview: {
27680
+ type: Boolean,
27681
+ default: undefined,
27682
+ },
27627
27683
  },
27628
27684
 
27629
27685
  setup(props, { emit }) {
@@ -27729,6 +27785,13 @@ var script$1 = defineComponent({
27729
27785
  return instance.config?.branding !== false;
27730
27786
  });
27731
27787
 
27788
+ const showExternalPreview = computed(() => {
27789
+ if (!panelUrl.value) return false;
27790
+ if (activeArtifact.value?.externalPreview !== undefined) return activeArtifact.value.externalPreview;
27791
+ if (props.externalPreview !== undefined) return props.externalPreview;
27792
+ return instance.config?.externalPreview ?? false;
27793
+ });
27794
+
27732
27795
  // Multi-tab computed
27733
27796
  const isMultiTab = computed(() => instance.config?.multiTab === true);
27734
27797
 
@@ -27878,6 +27941,10 @@ var script$1 = defineComponent({
27878
27941
  });
27879
27942
  }
27880
27943
 
27944
+ function handleExternalPreview() {
27945
+ if (panelUrl.value) window.open(panelUrl.value, '_blank', 'noopener,noreferrer');
27946
+ }
27947
+
27881
27948
  function handleIframeLoad() {
27882
27949
  clearTimeout(iframeLoadTimer);
27883
27950
  iframeLoading.value = false;
@@ -28421,6 +28488,7 @@ var script$1 = defineComponent({
28421
28488
  nonInlineArtifacts,
28422
28489
  currentNonInlineIndex,
28423
28490
  showBranding,
28491
+ showExternalPreview,
28424
28492
  effectivePanelWidth,
28425
28493
  panelClasses,
28426
28494
  forceEmptyView,
@@ -28445,6 +28513,7 @@ var script$1 = defineComponent({
28445
28513
  startPanelResize,
28446
28514
  startSplitResize,
28447
28515
  handleEditorSave,
28516
+ handleExternalPreview,
28448
28517
 
28449
28518
  // Multi-tab methods
28450
28519
  selectTab,
@@ -29480,6 +29549,50 @@ var __vue_render__$1 = function () {
29480
29549
  )
29481
29550
  : _vm._e(),
29482
29551
  _vm._v(" "),
29552
+ _vm.showExternalPreview
29553
+ ? _c(
29554
+ "button",
29555
+ {
29556
+ staticClass: "artifactuse-panel__action",
29557
+ attrs: { title: "Open in new tab" },
29558
+ on: { click: _vm.handleExternalPreview },
29559
+ },
29560
+ [
29561
+ _c(
29562
+ "svg",
29563
+ {
29564
+ attrs: {
29565
+ viewBox: "0 0 24 24",
29566
+ fill: "none",
29567
+ stroke: "currentColor",
29568
+ "stroke-width": "2",
29569
+ },
29570
+ },
29571
+ [
29572
+ _c("path", {
29573
+ attrs: {
29574
+ d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",
29575
+ },
29576
+ }),
29577
+ _vm._v(" "),
29578
+ _c("polyline", {
29579
+ attrs: { points: "15 3 21 3 21 9" },
29580
+ }),
29581
+ _vm._v(" "),
29582
+ _c("line", {
29583
+ attrs: {
29584
+ x1: "10",
29585
+ y1: "14",
29586
+ x2: "21",
29587
+ y2: "3",
29588
+ },
29589
+ }),
29590
+ ]
29591
+ ),
29592
+ ]
29593
+ )
29594
+ : _vm._e(),
29595
+ _vm._v(" "),
29483
29596
  _c(
29484
29597
  "button",
29485
29598
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "artifactuse",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "type": "module",
5
5
  "description": "The Artifact SDK for AI Agents - Turn AI outputs into interactive experiences",
6
6
  "author": "Artifactuse",