@kolkrabbi/kol-component 0.12.2 → 0.12.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolkrabbi/kol-component",
3
- "version": "0.12.2",
3
+ "version": "0.12.4",
4
4
  "description": "KOL design-system components — atoms through organisms, emitting canonical kol-* classes. Pairs with @kolkrabbi/kol-theme for styling.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -23,16 +23,17 @@
23
23
  "dependencies": {
24
24
  "@floating-ui/react": "^0.27.19",
25
25
  "embla-carousel-react": "^8.6.0",
26
- "@kolkrabbi/kol-icons": "0.7.1"
26
+ "refractor": "^5.0.0",
27
+ "@kolkrabbi/kol-icons": "0.8.3"
27
28
  },
28
29
  "peerDependencies": {
29
- "react": "^18.3.0 || ^19.0.0",
30
- "react-dom": "^18.3.0 || ^19.0.0",
31
- "react-router-dom": "^6.0.0 || ^7.0.0",
32
30
  "framer-motion": "^12.0.0",
33
31
  "gsap": "^3.13.0",
34
32
  "hls.js": "^1.6.0",
35
- "opentype.js": "^1.3.4"
33
+ "opentype.js": "^1.3.4",
34
+ "react": "^18.3.0 || ^19.0.0",
35
+ "react-dom": "^18.3.0 || ^19.0.0",
36
+ "react-router-dom": "^6.0.0 || ^7.0.0"
36
37
  },
37
38
  "peerDependenciesMeta": {
38
39
  "react-router-dom": {
@@ -1,7 +1,10 @@
1
- import CopyButton from '../atoms/CopyButton.jsx'
1
+ import { useState } from 'react'
2
+ import { refractor } from 'refractor'
2
3
 
3
4
  /**
4
- * CodeBlock — a chrome'd code block with language chip, filename chip, and copy.
5
+ * CodeBlock — a chrome'd code block with language chip, filename chip, copy,
6
+ * and syntax highlighting (refractor — the same Prism engine the Sanity
7
+ * studio's code-input uses, so schema languages match 1:1).
5
8
  *
6
9
  * Accepts three input shapes (first match wins per field):
7
10
  * • Portable Text: `value={{ code, language, filename }}` — the Sanity code-block
@@ -9,19 +12,85 @@ import CopyButton from '../atoms/CopyButton.jsx'
9
12
  * • Direct props: `code` / `language` / `filename`.
10
13
  * • Children: `<CodeBlock language="js">{'…'}</CodeBlock>` (the original API).
11
14
  *
15
+ * Token colors are theme concerns: refractor's `token x` classes render as
16
+ * `kol-tok kol-tok-x` spans, colored by kol-theme (light + dark). An
17
+ * unregistered/absent language renders plain — never crashes.
18
+ *
12
19
  * DOM order is copy → filename → lang → pre so the `.kol-codeblock-*` + pre
13
20
  * adjacency rules in the chrome CSS can pad the pre when chips are present.
14
21
  */
22
+
23
+ /* Sanity/code-input language ids → refractor grammar names */
24
+ const LANG_ALIASES = { js: 'javascript', ts: 'typescript', sh: 'bash', shell: 'bash', html: 'markup', xml: 'markup' }
25
+
26
+ /* hast → React. refractor emits { type: 'element'|'text', tagName, properties, children }. */
27
+ const renderNode = (node, key) => {
28
+ if (node.type === 'text') return node.value
29
+ const cls = (node.properties?.className || [])
30
+ .map((c) => (c === 'token' ? 'kol-tok' : `kol-tok-${c}`))
31
+ .join(' ')
32
+ return (
33
+ <span key={key} className={cls}>
34
+ {node.children.map(renderNode)}
35
+ </span>
36
+ )
37
+ }
38
+
39
+ const highlight = (src, language) => {
40
+ const lang = LANG_ALIASES[language] ?? language
41
+ if (!lang || !refractor.registered(lang)) return src
42
+ try {
43
+ return refractor.highlight(src, lang).children.map(renderNode)
44
+ } catch {
45
+ return src
46
+ }
47
+ }
48
+
49
+ /* Icon-swap copy control — the studio primitive's minimal form (clipboard →
50
+ * checkmark), ported 2026-07-28. Positioning comes from .kol-codeblock-copy. */
51
+ function CopyControl({ text }) {
52
+ const [copied, setCopied] = useState(false)
53
+ const onCopy = async () => {
54
+ try {
55
+ await navigator.clipboard.writeText(text)
56
+ setCopied(true)
57
+ setTimeout(() => setCopied(false), 1800)
58
+ } catch {
59
+ /* clipboard blocked — silent */
60
+ }
61
+ }
62
+ return (
63
+ <button
64
+ type="button"
65
+ className="kol-codeblock-copy"
66
+ onClick={onCopy}
67
+ aria-label={copied ? 'Copied' : 'Copy to clipboard'}
68
+ title={copied ? 'Copied' : 'Copy'}
69
+ >
70
+ {copied ? (
71
+ <svg viewBox="0 0 20 20" width="14" height="14" aria-hidden="true">
72
+ <path d="M5 10 L9 14 L15 6" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" />
73
+ </svg>
74
+ ) : (
75
+ <svg viewBox="0 0 20 20" width="14" height="14" aria-hidden="true">
76
+ <rect x="6" y="3" width="10" height="12" rx="1" fill="none" stroke="currentColor" strokeWidth="1.4" />
77
+ <rect x="4" y="6" width="10" height="12" rx="1" fill="none" stroke="currentColor" strokeWidth="1.4" />
78
+ </svg>
79
+ )}
80
+ </button>
81
+ )
82
+ }
83
+
15
84
  export default function CodeBlock({ children, code, language, filename, value }) {
16
- const src = code ?? value?.code ?? children
85
+ const src = String(code ?? value?.code ?? children ?? '')
17
86
  const lang = language ?? value?.language
18
87
  const file = filename ?? value?.filename
19
88
  return (
20
89
  <div className="kol-codeblock">
21
- <CopyButton text={String(src ?? '')} className="kol-codeblock-copy" />
90
+ <CopyControl text={src} />
22
91
  {file && <span className="kol-codeblock-filename">{file}</span>}
23
92
  {lang && <span className="kol-codeblock-lang">{lang}</span>}
24
- <pre><code>{src}</code></pre>
93
+ <pre><code>{highlight(src, lang)}</code></pre>
25
94
  </div>
26
95
  )
27
96
  }
@@ -156,7 +156,7 @@ export default function ShellDrawer({
156
156
  aria-label="Close"
157
157
  className="ml-auto flex h-8 w-8 shrink-0 items-center justify-center rounded-md border-0 bg-transparent cursor-pointer text-fg-64 transition-colors hover:bg-fg-08 hover:text-emphasis"
158
158
  >
159
- <Icon name="close" size={14} />
159
+ <Icon name="x" size={14} />
160
160
  </button>
161
161
  </div>
162
162
  <div className="flex-1 overflow-y-auto pr-1" style={{ overflowAnchor: 'none' }}>