artifactuse 0.2.3 → 0.2.4

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.
@@ -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
 
@@ -7369,6 +7392,17 @@ function createArtifactuse(userConfig = {}) {
7369
7392
  const ext = filename.split('.').pop();
7370
7393
  const language = options.language || getLanguageFromExtension(ext) || ext;
7371
7394
  const title = options.title || filename;
7395
+
7396
+ // Deduplicate: if an artifact with the same title already exists, update + focus it
7397
+ const existing = state.getState().artifacts.find(a => a.title === title && !a.isInline);
7398
+ if (existing) {
7399
+ updateFile(existing.id, code, { ...options, title });
7400
+ openArtifact(existing);
7401
+ if (options.viewMode) state.setViewMode(options.viewMode);
7402
+ else if (options.tabs) state.setViewMode(options.tabs[0]);
7403
+ return state.getArtifact(existing.id);
7404
+ }
7405
+
7372
7406
  return openCode(code, language, { ...options, title });
7373
7407
  }
7374
7408
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "artifactuse",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "type": "module",
5
5
  "description": "The Artifact SDK for AI Agents - Turn AI outputs into interactive experiences",
6
6
  "author": "Artifactuse",