@rogieking/figui3 2.24.0 → 2.26.0
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.
- package/components.css +94 -8
- package/fig.js +894 -119
- package/index.html +369 -3
- package/package.json +1 -1
package/index.html
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
<link rel="stylesheet"
|
|
10
10
|
type="text/css"
|
|
11
11
|
href="fig.css">
|
|
12
|
+
<link rel="stylesheet"
|
|
13
|
+
href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism.min.css">
|
|
12
14
|
<script src="fig.js"></script>
|
|
13
15
|
<style>
|
|
14
16
|
* {
|
|
@@ -161,6 +163,97 @@
|
|
|
161
163
|
color: var(--figma-color-text);
|
|
162
164
|
}
|
|
163
165
|
|
|
166
|
+
pre[class*="language-"] {
|
|
167
|
+
background: var(--figma-color-bg-secondary);
|
|
168
|
+
border: 1px solid var(--figma-color-border);
|
|
169
|
+
border-radius: 6px;
|
|
170
|
+
padding: 12px 16px;
|
|
171
|
+
margin: 0;
|
|
172
|
+
overflow-x: auto;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
pre[class*="language-"]>code[class*="language-"] {
|
|
176
|
+
display: block;
|
|
177
|
+
background: transparent;
|
|
178
|
+
padding: 0;
|
|
179
|
+
font-family: "SF Mono", Monaco, Consolas, monospace;
|
|
180
|
+
font-size: 12px;
|
|
181
|
+
line-height: 1.5;
|
|
182
|
+
color: var(--figma-color-text);
|
|
183
|
+
text-shadow: none;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
:root {
|
|
187
|
+
--code-token-comment: #6b7280;
|
|
188
|
+
--code-token-punctuation: #374151;
|
|
189
|
+
--code-token-primary: #b42318;
|
|
190
|
+
--code-token-string: #0f766e;
|
|
191
|
+
--code-token-operator: #1d4ed8;
|
|
192
|
+
--code-token-keyword: #6d28d9;
|
|
193
|
+
--code-token-function: #9a3412;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
html[style*="color-scheme: dark"] {
|
|
197
|
+
--code-token-comment: #8b93a7;
|
|
198
|
+
--code-token-punctuation: #c7cfdd;
|
|
199
|
+
--code-token-primary: #ff8f8f;
|
|
200
|
+
--code-token-string: #6dd3b6;
|
|
201
|
+
--code-token-operator: #7fb4ff;
|
|
202
|
+
--code-token-keyword: #b79bff;
|
|
203
|
+
--code-token-function: #ffc27a;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/* Keep token colors aligned with theme and improve contrast in light mode */
|
|
207
|
+
.token.comment,
|
|
208
|
+
.token.prolog,
|
|
209
|
+
.token.doctype,
|
|
210
|
+
.token.cdata {
|
|
211
|
+
color: var(--code-token-comment);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.token.punctuation {
|
|
215
|
+
color: var(--code-token-punctuation);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.token.property,
|
|
219
|
+
.token.tag,
|
|
220
|
+
.token.boolean,
|
|
221
|
+
.token.number,
|
|
222
|
+
.token.constant,
|
|
223
|
+
.token.symbol,
|
|
224
|
+
.token.deleted {
|
|
225
|
+
color: var(--code-token-primary);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.token.selector,
|
|
229
|
+
.token.attr-name,
|
|
230
|
+
.token.string,
|
|
231
|
+
.token.char,
|
|
232
|
+
.token.builtin,
|
|
233
|
+
.token.inserted {
|
|
234
|
+
color: var(--code-token-string);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.token.operator,
|
|
238
|
+
.token.entity,
|
|
239
|
+
.token.url,
|
|
240
|
+
.language-css .token.string,
|
|
241
|
+
.style .token.string {
|
|
242
|
+
color: var(--code-token-operator);
|
|
243
|
+
background: transparent;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.token.atrule,
|
|
247
|
+
.token.attr-value,
|
|
248
|
+
.token.keyword {
|
|
249
|
+
color: var(--code-token-keyword);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.token.function,
|
|
253
|
+
.token.class-name {
|
|
254
|
+
color: var(--code-token-function);
|
|
255
|
+
}
|
|
256
|
+
|
|
164
257
|
.toolbelt {
|
|
165
258
|
position: fixed;
|
|
166
259
|
bottom: 0.75rem;
|
|
@@ -1650,9 +1743,208 @@
|
|
|
1650
1743
|
</fig-fill-picker>
|
|
1651
1744
|
|
|
1652
1745
|
<h3>Custom Trigger</h3>
|
|
1746
|
+
<p class="description">Any child element can act as the trigger. If the child is not a
|
|
1747
|
+
<code>fig-chit</code>, it becomes click-only — the fill picker won't update its appearance.
|
|
1748
|
+
</p>
|
|
1749
|
+
|
|
1750
|
+
<h4>Button Trigger</h4>
|
|
1653
1751
|
<fig-fill-picker value='{"type":"solid","color":"#95E1D3"}'>
|
|
1654
1752
|
<fig-button>Edit Fill</fig-button>
|
|
1655
1753
|
</fig-fill-picker>
|
|
1754
|
+
<pre
|
|
1755
|
+
style="background: var(--figma-color-bg-secondary); padding: 12px 16px; border-radius: 6px; overflow-x: auto; margin: 0;"><code style="font-family: monospace; font-size: 12px; color: var(--figma-color-text);"><fig-fill-picker value='{"type":"solid","color":"#95E1D3"}'>
|
|
1756
|
+
<fig-button>Edit Fill</fig-button>
|
|
1757
|
+
</fig-fill-picker></code></pre>
|
|
1758
|
+
|
|
1759
|
+
<h4>Icon Button Trigger</h4>
|
|
1760
|
+
<fig-fill-picker value='{"type":"solid","color":"#667eea"}'>
|
|
1761
|
+
<fig-button icon
|
|
1762
|
+
variant="ghost"
|
|
1763
|
+
title="Pick a fill">
|
|
1764
|
+
<span class="fig-mask-icon"
|
|
1765
|
+
style="--icon: var(--icon-eyedropper)"></span>
|
|
1766
|
+
</fig-button>
|
|
1767
|
+
</fig-fill-picker>
|
|
1768
|
+
<pre
|
|
1769
|
+
style="background: var(--figma-color-bg-secondary); padding: 12px 16px; border-radius: 6px; overflow-x: auto; margin: 0;"><code style="font-family: monospace; font-size: 12px; color: var(--figma-color-text);"><fig-fill-picker value='{"type":"solid","color":"#667eea"}'>
|
|
1770
|
+
<fig-button icon variant="ghost" title="Pick a fill">
|
|
1771
|
+
<span class="fig-mask-icon" style="--icon: var(--icon-eyedropper)"></span>
|
|
1772
|
+
</fig-button>
|
|
1773
|
+
</fig-fill-picker></code></pre>
|
|
1774
|
+
|
|
1775
|
+
<h4>Inline Text Trigger</h4>
|
|
1776
|
+
<fig-fill-picker value='{"type":"solid","color":"#F38181"}'>
|
|
1777
|
+
<span style="cursor: pointer; text-decoration: underline; color: var(--figma-color-text-brand)">Change
|
|
1778
|
+
background</span>
|
|
1779
|
+
</fig-fill-picker>
|
|
1780
|
+
<pre
|
|
1781
|
+
style="background: var(--figma-color-bg-secondary); padding: 12px 16px; border-radius: 6px; overflow-x: auto; margin: 0;"><code style="font-family: monospace; font-size: 12px; color: var(--figma-color-text);"><fig-fill-picker value='{"type":"solid","color":"#F38181"}'>
|
|
1782
|
+
<span style="cursor: pointer">Change background</span>
|
|
1783
|
+
</fig-fill-picker></code></pre>
|
|
1784
|
+
|
|
1785
|
+
<h4>Swatch with Custom Size</h4>
|
|
1786
|
+
<fig-fill-picker id="fill-picker-custom-swatch"
|
|
1787
|
+
value='{"type":"gradient","gradient":{"type":"linear","angle":135,"stops":[{"position":0,"color":"#f093fb","opacity":100},{"position":100,"color":"#f5576c","opacity":100}]}}'>
|
|
1788
|
+
<div id="custom-swatch-preview"
|
|
1789
|
+
style="width: 3rem; height: 3rem; border-radius: 0.5rem; cursor: pointer; background: linear-gradient(135deg, #f093fb, #f5576c)">
|
|
1790
|
+
</div>
|
|
1791
|
+
</fig-fill-picker>
|
|
1792
|
+
<pre
|
|
1793
|
+
style="background: var(--figma-color-bg-secondary); padding: 12px 16px; border-radius: 6px; overflow-x: auto; margin: 0;"><code style="font-family: monospace; font-size: 12px; color: var(--figma-color-text);"><fig-fill-picker value='{"type":"gradient",...}'>
|
|
1794
|
+
<div style="width: 3rem; height: 3rem; border-radius: 0.5rem;
|
|
1795
|
+
cursor: pointer; background: linear-gradient(...)">
|
|
1796
|
+
</div>
|
|
1797
|
+
</fig-fill-picker></code></pre>
|
|
1798
|
+
<script>
|
|
1799
|
+
(() => {
|
|
1800
|
+
const picker = document.getElementById('fill-picker-custom-swatch');
|
|
1801
|
+
const swatch = document.getElementById('custom-swatch-preview');
|
|
1802
|
+
if (!picker || !swatch) return;
|
|
1803
|
+
|
|
1804
|
+
const getSizingForMedia = (scaleMode, scale) => {
|
|
1805
|
+
switch (scaleMode) {
|
|
1806
|
+
case 'fit':
|
|
1807
|
+
return { size: 'contain', position: 'center' };
|
|
1808
|
+
case 'tile':
|
|
1809
|
+
return { size: `${scale || 50}%`, position: 'top left' };
|
|
1810
|
+
case 'fill':
|
|
1811
|
+
case 'crop':
|
|
1812
|
+
default:
|
|
1813
|
+
return { size: 'cover', position: 'center' };
|
|
1814
|
+
}
|
|
1815
|
+
};
|
|
1816
|
+
|
|
1817
|
+
const updateSwatchPreview = (fill) => {
|
|
1818
|
+
if (!fill) return;
|
|
1819
|
+
|
|
1820
|
+
let background = 'transparent';
|
|
1821
|
+
let backgroundSize = 'cover';
|
|
1822
|
+
let backgroundPosition = 'center';
|
|
1823
|
+
|
|
1824
|
+
if (fill.type === 'solid') {
|
|
1825
|
+
background = fill.color || '#D9D9D9';
|
|
1826
|
+
} else if (fill.type === 'gradient') {
|
|
1827
|
+
background = fill.css || background;
|
|
1828
|
+
} else if (fill.type === 'image' && fill.image?.url) {
|
|
1829
|
+
background = `url(${fill.image.url})`;
|
|
1830
|
+
const sizing = getSizingForMedia(fill.image.scaleMode, fill.image.scale);
|
|
1831
|
+
backgroundSize = sizing.size;
|
|
1832
|
+
backgroundPosition = sizing.position;
|
|
1833
|
+
} else if (fill.type === 'video' && fill.video?.url) {
|
|
1834
|
+
background = `url(${fill.video.url})`;
|
|
1835
|
+
const sizing = getSizingForMedia(fill.video.scaleMode, fill.video.scale);
|
|
1836
|
+
backgroundSize = sizing.size;
|
|
1837
|
+
backgroundPosition = sizing.position;
|
|
1838
|
+
} else if (fill.type === 'webcam' && fill.image?.url) {
|
|
1839
|
+
background = `url(${fill.image.url})`;
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
swatch.style.background = background;
|
|
1843
|
+
swatch.style.backgroundSize = backgroundSize;
|
|
1844
|
+
swatch.style.backgroundPosition = backgroundPosition;
|
|
1845
|
+
};
|
|
1846
|
+
|
|
1847
|
+
picker.addEventListener('input', (e) => updateSwatchPreview(e.detail));
|
|
1848
|
+
picker.addEventListener('change', (e) => updateSwatchPreview(e.detail));
|
|
1849
|
+
updateSwatchPreview(picker.value);
|
|
1850
|
+
})();
|
|
1851
|
+
</script>
|
|
1852
|
+
|
|
1853
|
+
<h3>Custom Mode (Slots)</h3>
|
|
1854
|
+
<p class="description">Add custom modes via <code>slot="mode-{name}"</code> children. The mode name must
|
|
1855
|
+
also appear in the <code>mode</code> attribute. Custom slot content manages its own UI; dispatch
|
|
1856
|
+
<code>input</code>/<code>change</code> events with a <code>detail</code> payload to relay values back.
|
|
1857
|
+
</p>
|
|
1858
|
+
|
|
1859
|
+
<h4>Custom "Shader" Mode</h4>
|
|
1860
|
+
<fig-fill-picker mode="solid,shader"
|
|
1861
|
+
value='{"type":"solid","color":"#95E1D3"}'
|
|
1862
|
+
experimental="modern">
|
|
1863
|
+
<div slot="mode-shader"
|
|
1864
|
+
label="Shader">
|
|
1865
|
+
<fig-field label="Fragment Shader">
|
|
1866
|
+
<fig-input-text multiline
|
|
1867
|
+
placeholder="void main() { ... }"
|
|
1868
|
+
value="void main() { gl_FragColor = vec4(1.0, 0.0, 0.5, 1.0); }"></fig-input-text>
|
|
1869
|
+
</fig-field>
|
|
1870
|
+
</div>
|
|
1871
|
+
</fig-fill-picker>
|
|
1872
|
+
<pre
|
|
1873
|
+
style="background: var(--figma-color-bg-secondary); padding: 12px 16px; border-radius: 6px; overflow-x: auto; margin: 0;"><code style="font-family: monospace; font-size: 12px; color: var(--figma-color-text);"><fig-fill-picker mode="solid,shader">
|
|
1874
|
+
<div slot="mode-shader" label="Shader">
|
|
1875
|
+
<fig-field label="Fragment Shader">
|
|
1876
|
+
<fig-input-text multiline
|
|
1877
|
+
placeholder="void main() { ... }"
|
|
1878
|
+
value="..."></fig-input-text>
|
|
1879
|
+
</fig-field>
|
|
1880
|
+
</div>
|
|
1881
|
+
</fig-fill-picker></code></pre>
|
|
1882
|
+
|
|
1883
|
+
<h4>React Custom Mode (via <code>modeready</code>)</h4>
|
|
1884
|
+
<p class="description">Frameworks like React can listen for the <code>modeready</code> event and render
|
|
1885
|
+
directly into the provided container. The DOM is never moved — React owns its tree from the start.</p>
|
|
1886
|
+
<fig-fill-picker id="react-picker" mode="solid,react-demo">
|
|
1887
|
+
<div slot="mode-react-demo" label="React"></div>
|
|
1888
|
+
</fig-fill-picker>
|
|
1889
|
+
<script type="module">
|
|
1890
|
+
import React from 'https://esm.sh/react@18';
|
|
1891
|
+
import { createRoot } from 'https://esm.sh/react-dom@18/client';
|
|
1892
|
+
|
|
1893
|
+
const h = React.createElement;
|
|
1894
|
+
const picker = document.getElementById('react-picker');
|
|
1895
|
+
|
|
1896
|
+
picker.addEventListener('modeready', (e) => {
|
|
1897
|
+
if (e.detail.mode !== 'react-demo') return;
|
|
1898
|
+
|
|
1899
|
+
function ColorButtons() {
|
|
1900
|
+
const [color, setColor] = React.useState('#667eea');
|
|
1901
|
+
const colors = ['#FF6B6B', '#4ECDC4', '#667eea', '#f093fb', '#95E1D3'];
|
|
1902
|
+
return h('div', { style: { display: 'flex', flexDirection: 'column', gap: 8 } },
|
|
1903
|
+
h('p', {
|
|
1904
|
+
style: {
|
|
1905
|
+
fontSize: 11,
|
|
1906
|
+
color: 'var(--figma-color-text-secondary)',
|
|
1907
|
+
margin: 0,
|
|
1908
|
+
}
|
|
1909
|
+
}, `Selected: ${color}`),
|
|
1910
|
+
h('div', { style: { display: 'flex', gap: 4, flexWrap: 'wrap' } },
|
|
1911
|
+
colors.map((c) =>
|
|
1912
|
+
h('button', {
|
|
1913
|
+
key: c,
|
|
1914
|
+
onClick: () => setColor(c),
|
|
1915
|
+
style: {
|
|
1916
|
+
width: 28,
|
|
1917
|
+
height: 28,
|
|
1918
|
+
borderRadius: 4,
|
|
1919
|
+
border: c === color ? '2px solid var(--figma-color-text)' : '1px solid var(--figma-color-border)',
|
|
1920
|
+
background: c,
|
|
1921
|
+
cursor: 'pointer',
|
|
1922
|
+
padding: 0,
|
|
1923
|
+
}
|
|
1924
|
+
})
|
|
1925
|
+
)
|
|
1926
|
+
)
|
|
1927
|
+
);
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
createRoot(e.detail.container).render(h(ColorButtons));
|
|
1931
|
+
});
|
|
1932
|
+
</script>
|
|
1933
|
+
<pre
|
|
1934
|
+
style="background: var(--figma-color-bg-secondary); padding: 12px 16px; border-radius: 6px; overflow-x: auto; margin: 0;"><code style="font-family: monospace; font-size: 12px; color: var(--figma-color-text);"><fig-fill-picker id="react-picker" mode="solid,react-demo">
|
|
1935
|
+
<div slot="mode-react-demo" label="React"></div>
|
|
1936
|
+
</fig-fill-picker>
|
|
1937
|
+
|
|
1938
|
+
<script type="module">
|
|
1939
|
+
import React from 'https://esm.sh/react@18';
|
|
1940
|
+
import { createRoot } from 'https://esm.sh/react-dom@18/client';
|
|
1941
|
+
|
|
1942
|
+
const picker = document.getElementById('react-picker');
|
|
1943
|
+
picker.addEventListener('modeready', (e) => {
|
|
1944
|
+
if (e.detail.mode !== 'react-demo') return;
|
|
1945
|
+
createRoot(e.detail.container).render(<MyComponent />);
|
|
1946
|
+
});
|
|
1947
|
+
</script></code></pre>
|
|
1656
1948
|
|
|
1657
1949
|
<h3>Without Alpha</h3>
|
|
1658
1950
|
<fig-fill-picker alpha="false"
|
|
@@ -3274,9 +3566,11 @@
|
|
|
3274
3566
|
</dialog></code></pre>
|
|
3275
3567
|
|
|
3276
3568
|
<h3>Viewport Margin</h3>
|
|
3277
|
-
<p class="description">Use <code>viewport-margin</code> to define safe areas the popup should avoid (e.g. a
|
|
3569
|
+
<p class="description">Use <code>viewport-margin</code> to define safe areas the popup should avoid (e.g. a
|
|
3570
|
+
bottom toolbar). Uses CSS margin shorthand: <code>top right bottom left</code>.</p>
|
|
3278
3571
|
<fig-button id="popup-open-viewport-margin"
|
|
3279
|
-
onclick="document.getElementById('popup-viewport-margin').open = true">Open (64px bottom
|
|
3572
|
+
onclick="document.getElementById('popup-viewport-margin').open = true">Open (64px bottom
|
|
3573
|
+
margin)</fig-button>
|
|
3280
3574
|
<dialog id="popup-viewport-margin"
|
|
3281
3575
|
is="fig-popup"
|
|
3282
3576
|
anchor="#popup-open-viewport-margin"
|
|
@@ -3285,7 +3579,9 @@
|
|
|
3285
3579
|
viewport-margin="8 8 64 8">
|
|
3286
3580
|
<vstack style="min-width: 14rem;">
|
|
3287
3581
|
<strong style="padding: 0 var(--spacer-1);">Viewport Margin</strong>
|
|
3288
|
-
<p
|
|
3582
|
+
<p
|
|
3583
|
+
style="padding: 0 var(--spacer-1); margin: 0; font-size: var(--font-size-small); color: var(--figma-color-text-secondary);">
|
|
3584
|
+
This popup won't overlap the bottom 64px of the viewport.</p>
|
|
3289
3585
|
<fig-input-text placeholder="Try scrolling down"></fig-input-text>
|
|
3290
3586
|
</vstack>
|
|
3291
3587
|
</dialog>
|
|
@@ -4043,6 +4339,32 @@
|
|
|
4043
4339
|
<fig-button>No delay</fig-button>
|
|
4044
4340
|
</fig-tooltip>
|
|
4045
4341
|
|
|
4342
|
+
<h3>Warmup (Rapid Hover)</h3>
|
|
4343
|
+
<p class="description">After the first tooltip appears, moving quickly between these buttons shows
|
|
4344
|
+
subsequent tooltips instantly — no repeated delay.</p>
|
|
4345
|
+
<hstack>
|
|
4346
|
+
<fig-tooltip text="Add" delay="500">
|
|
4347
|
+
<fig-button icon variant="ghost">
|
|
4348
|
+
<span class="fig-mask-icon" style="--icon: var(--icon-add)"></span>
|
|
4349
|
+
</fig-button>
|
|
4350
|
+
</fig-tooltip>
|
|
4351
|
+
<fig-tooltip text="Remove" delay="500">
|
|
4352
|
+
<fig-button icon variant="ghost">
|
|
4353
|
+
<span class="fig-mask-icon" style="--icon: var(--icon-minus)"></span>
|
|
4354
|
+
</fig-button>
|
|
4355
|
+
</fig-tooltip>
|
|
4356
|
+
<fig-tooltip text="Rotate" delay="500">
|
|
4357
|
+
<fig-button icon variant="ghost">
|
|
4358
|
+
<span class="fig-mask-icon" style="--icon: var(--icon-rotate)"></span>
|
|
4359
|
+
</fig-button>
|
|
4360
|
+
</fig-tooltip>
|
|
4361
|
+
<fig-tooltip text="Swap" delay="500">
|
|
4362
|
+
<fig-button icon variant="ghost">
|
|
4363
|
+
<span class="fig-mask-icon" style="--icon: var(--icon-swap)"></span>
|
|
4364
|
+
</fig-button>
|
|
4365
|
+
</fig-tooltip>
|
|
4366
|
+
</hstack>
|
|
4367
|
+
|
|
4046
4368
|
<h3>Long Text</h3>
|
|
4047
4369
|
<fig-tooltip text="This is a much longer tooltip that contains more detailed information about the element">
|
|
4048
4370
|
<fig-button variant="secondary">Long tooltip</fig-button>
|
|
@@ -4565,7 +4887,50 @@ button.addEventListener('click', () => {
|
|
|
4565
4887
|
</section>
|
|
4566
4888
|
</div>
|
|
4567
4889
|
|
|
4890
|
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-core.min.js"></script>
|
|
4891
|
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-markup.min.js"></script>
|
|
4892
|
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-css.min.js"></script>
|
|
4893
|
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-clike.min.js"></script>
|
|
4894
|
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-javascript.min.js"></script>
|
|
4568
4895
|
<script>
|
|
4896
|
+
function detectCodeLanguage(codeText) {
|
|
4897
|
+
const source = codeText.trim();
|
|
4898
|
+
if (!source) return 'javascript';
|
|
4899
|
+
|
|
4900
|
+
if (
|
|
4901
|
+
source.startsWith('<') ||
|
|
4902
|
+
source.startsWith('<') ||
|
|
4903
|
+
source.includes('</') ||
|
|
4904
|
+
source.includes('</')
|
|
4905
|
+
) {
|
|
4906
|
+
return 'markup';
|
|
4907
|
+
}
|
|
4908
|
+
|
|
4909
|
+
if (
|
|
4910
|
+
/(^|[\n\r])\s*[@.#a-zA-Z][\w\s.#:[\]-]*\{/.test(source) ||
|
|
4911
|
+
/--[\w-]+\s*:/.test(source)
|
|
4912
|
+
) {
|
|
4913
|
+
return 'css';
|
|
4914
|
+
}
|
|
4915
|
+
|
|
4916
|
+
return 'javascript';
|
|
4917
|
+
}
|
|
4918
|
+
|
|
4919
|
+
function highlightCodeBlocks() {
|
|
4920
|
+
const blocks = document.querySelectorAll('pre > code');
|
|
4921
|
+
blocks.forEach((code) => {
|
|
4922
|
+
if (!code.className.includes('language-')) {
|
|
4923
|
+
const language = detectCodeLanguage(code.textContent || '');
|
|
4924
|
+
code.classList.add(`language-${language}`);
|
|
4925
|
+
code.parentElement?.classList.add(`language-${language}`);
|
|
4926
|
+
}
|
|
4927
|
+
});
|
|
4928
|
+
|
|
4929
|
+
if (window.Prism) {
|
|
4930
|
+
window.Prism.highlightAll();
|
|
4931
|
+
}
|
|
4932
|
+
}
|
|
4933
|
+
|
|
4569
4934
|
// Highlight nav item based on hash
|
|
4570
4935
|
function updateActiveNav() {
|
|
4571
4936
|
const hash = location.hash || '#avatar';
|
|
@@ -4610,6 +4975,7 @@ button.addEventListener('click', () => {
|
|
|
4610
4975
|
|
|
4611
4976
|
// Initial state
|
|
4612
4977
|
window.addEventListener('load', () => {
|
|
4978
|
+
highlightCodeBlocks();
|
|
4613
4979
|
updateActiveNav();
|
|
4614
4980
|
if (location.hash) {
|
|
4615
4981
|
document.querySelector(location.hash)?.scrollIntoView();
|
package/package.json
CHANGED