@techzyapp/ui-library 1.0.0 → 1.0.1

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.
Files changed (51) hide show
  1. package/README.md +134 -0
  2. package/dist/Blockquote-CNIuJFHq.js +46 -0
  3. package/dist/Blockquote-CNIuJFHq.js.map +1 -0
  4. package/dist/Blockquote-CxHwOecc.cjs +2 -0
  5. package/dist/Blockquote-CxHwOecc.cjs.map +1 -0
  6. package/dist/Code-CUdpWlY5.cjs +2 -0
  7. package/dist/Code-CUdpWlY5.cjs.map +1 -0
  8. package/dist/Code-DaEpXHfF.js +26 -0
  9. package/dist/Code-DaEpXHfF.js.map +1 -0
  10. package/dist/CodeBlock-BWxQyKRo.js +96 -0
  11. package/dist/CodeBlock-BWxQyKRo.js.map +1 -0
  12. package/dist/CodeBlock-CgXOjKS-.cjs +2 -0
  13. package/dist/CodeBlock-CgXOjKS-.cjs.map +1 -0
  14. package/dist/components/Blockquote/Blockquote.d.cts +25 -0
  15. package/dist/components/Blockquote/Blockquote.d.ts +25 -0
  16. package/dist/components/Blockquote/index.cjs +2 -0
  17. package/dist/components/Blockquote/index.cjs.map +1 -0
  18. package/dist/components/Blockquote/index.d.cts +2 -0
  19. package/dist/components/Blockquote/index.d.ts +2 -0
  20. package/dist/components/Blockquote/index.js +6 -0
  21. package/dist/components/Blockquote/index.js.map +1 -0
  22. package/dist/components/Code/Code.d.cts +21 -0
  23. package/dist/components/Code/Code.d.ts +21 -0
  24. package/dist/components/Code/index.cjs +2 -0
  25. package/dist/components/Code/index.cjs.map +1 -0
  26. package/dist/components/Code/index.d.cts +2 -0
  27. package/dist/components/Code/index.d.ts +2 -0
  28. package/dist/components/Code/index.js +6 -0
  29. package/dist/components/Code/index.js.map +1 -0
  30. package/dist/components/CodeBlock/CodeBlock.d.cts +10 -6
  31. package/dist/components/CodeBlock/CodeBlock.d.ts +10 -6
  32. package/dist/components/CodeBlock/index.cjs +1 -1
  33. package/dist/components/CodeBlock/index.js +1 -1
  34. package/dist/components/index.d.cts +2 -0
  35. package/dist/components/index.d.ts +2 -0
  36. package/dist/entries/components.cjs +1 -1
  37. package/dist/entries/components.js +128 -124
  38. package/dist/entries/components.js.map +1 -1
  39. package/dist/helpers/translations.d.cts +1 -1
  40. package/dist/helpers/translations.d.ts +1 -1
  41. package/dist/index.cjs +1 -1
  42. package/dist/index.css +1 -1
  43. package/dist/index.js +140 -136
  44. package/dist/index.js.map +1 -1
  45. package/dist/translations-CXEeeUDL.cjs.map +1 -1
  46. package/dist/translations-D15tFHM1.js.map +1 -1
  47. package/package.json +1 -1
  48. package/dist/CodeBlock-DOk5wekc.cjs +0 -2
  49. package/dist/CodeBlock-DOk5wekc.cjs.map +0 -1
  50. package/dist/CodeBlock-n3KExtTO.js +0 -104
  51. package/dist/CodeBlock-n3KExtTO.js.map +0 -1
package/README.md ADDED
@@ -0,0 +1,134 @@
1
+ # @techzyapp/ui-library
2
+
3
+ **Techzy UI** — a reusable, framework-agnostic React component library and the shared UI foundation for
4
+ Techzy admin panels. Ships ~60 components plus a batteries-included admin shell (sidebar + header +
5
+ routing), theming, and built-in UI localization.
6
+
7
+ - **React 19** (peer `>=18`), TypeScript (strict), CSS Modules, dual **ESM/CJS**.
8
+ - **Theming** via `ConfigProvider` — 9 palette colors, light/dark, per-mode accent, custom fonts.
9
+ - **i18n out of the box** — the panel ships translated in **7 languages** (en · ka · es · de · it · fr · ru);
10
+ just list the ones you want.
11
+ - **Batteries included** — `Table`, `RichTextEditor`, `FileUploader`, date/time pickers, the admin shell, and more.
12
+
13
+ ---
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm i @techzyapp/ui-library
19
+ ```
20
+
21
+ `react` / `react-dom` are required peers. Everything else is an **optional peer** — install only what the
22
+ features you use need:
23
+
24
+ | Feature | Peer dependency |
25
+ | -------------------------------------- | --------------------------------------------------------------------------- |
26
+ | Forms (`useForm`) | `zod` |
27
+ | Admin shell (`RootLayout` / `Sidebar`) | `@tanstack/react-router` |
28
+ | `Table` | `@tanstack/react-table` |
29
+ | Date/time pickers | `dayjs` |
30
+ | `RichTextEditor` | `lexical` + `@lexical/*` |
31
+ | `FileUploader` | `react-dropzone`, `@dnd-kit/*`, `@formkit/auto-animate`, `react-image-crop` |
32
+ | `Table` row reorder | `@dnd-kit/*` |
33
+ | `CodeBlock` | `shiki` |
34
+
35
+ ---
36
+
37
+ ## Scaffold a whole project (fastest)
38
+
39
+ Building a new admin panel from scratch? The repo ships **[`CONSUMER.md`](./CONSUMER.md)** — a drop-in
40
+ guide for Claude Code. Copy it into an **empty folder** as `CLAUDE.md`, tell Claude Code to _"set up the
41
+ project,"_ and it scaffolds a clean **Vite + TanStack Router** app wired to this library — theming,
42
+ routing, and a demo **Example Page** — with the boilerplate removed. Keep the file at the repo root
43
+ afterwards as the assistant's reference for building pages, forms, and the admin shell.
44
+
45
+ For a manual / existing-project setup, follow the steps below.
46
+
47
+ ---
48
+
49
+ ## Setup
50
+
51
+ Import the two stylesheets once at your app entry, then wrap the app in `ConfigProvider`:
52
+
53
+ ```tsx
54
+ // main.tsx
55
+ import '@techzyapp/ui-library/css/reset.css'
56
+ import '@techzyapp/ui-library/css/styles.css'
57
+
58
+ import { ConfigProvider, type Config } from '@techzyapp/ui-library/theme'
59
+
60
+ const config: Config = {
61
+ theme: { mode: 'light' },
62
+ // the panel ships en + ka + es + de + it + fr + ru — listing them is all it takes
63
+ i18n: {
64
+ languages: [
65
+ { code: 'en-US', label: 'English' },
66
+ { code: 'ka-GE', label: 'ქართული' },
67
+ ],
68
+ },
69
+ }
70
+
71
+ export function App() {
72
+ return <ConfigProvider config={config}>{/* your app / router */}</ConfigProvider>
73
+ }
74
+ ```
75
+
76
+ > `ConfigProvider` injects the theme's color variables before first paint, so it must wrap anything that
77
+ > uses the components.
78
+
79
+ ## Quick example
80
+
81
+ ```tsx
82
+ import { Button, Card, TextField } from '@techzyapp/ui-library/components'
83
+ import { Icon } from '@techzyapp/ui-library/icons'
84
+
85
+ function SignInCard() {
86
+ return (
87
+ <Card title="Sign in" icon="User">
88
+ <TextField label="Email" placeholder="you@techzy.app" />
89
+ <TextField label="Password" type="password" />
90
+ <Button fullWidth startIcon={<Icon name="Login" />}>
91
+ Continue
92
+ </Button>
93
+ </Card>
94
+ )
95
+ }
96
+ ```
97
+
98
+ ## Localizing your own text
99
+
100
+ `useTranslations()` binds your app's copy to the same language switcher the panel uses:
101
+
102
+ ```tsx
103
+ import { useTranslations } from '@techzyapp/ui-library/i18n'
104
+
105
+ // config.i18n.appMessages = { en: { 'welcome': 'Welcome' }, ka: { 'welcome': 'მოგესალმებით' } }
106
+ const t = useTranslations()
107
+ ;<h1>{t('welcome')}</h1>
108
+ ```
109
+
110
+ ---
111
+
112
+ ## Subpath exports
113
+
114
+ Import from the scoped surfaces (the root `.` re-exports everything too):
115
+
116
+ | Import | What's in it |
117
+ | --------------------------------------------------------- | --------------------------------------------------------------------------------- |
118
+ | `@techzyapp/ui-library/components` | every component + the admin shell + `Form` |
119
+ | `@techzyapp/ui-library/components/<Name>` | a single component |
120
+ | `@techzyapp/ui-library/hooks` | `useDisclosure`, `useMediaQuery`, `useForm`, `useLockBodyScroll`, `useAccessKeys` |
121
+ | `@techzyapp/ui-library/theme` | `ConfigProvider`, `useTheme`, config types, `applyTheme` |
122
+ | `@techzyapp/ui-library/i18n` | `useT`, `useTranslations`, `useLanguage` + message types |
123
+ | `@techzyapp/ui-library/icons` | `Icon`, `IconName`, `ICON_NAMES` |
124
+ | `@techzyapp/ui-library/helpers` | RBAC + translation + `Table` query helpers |
125
+ | `@techzyapp/ui-library/css/reset.css` · `/css/styles.css` | the two stylesheets |
126
+
127
+ > Subpath imports need `moduleResolution: "bundler"` or `"node16"` in your `tsconfig`; the root import
128
+ > works on classic `"node"` too.
129
+
130
+ ---
131
+
132
+ ## License
133
+
134
+ MIT © Techzy
@@ -0,0 +1,46 @@
1
+ import { jsxs as e, jsx as t } from "react/jsx-runtime";
2
+ import { forwardRef as b } from "react";
3
+ import { clsx as f } from "clsx";
4
+ const k = "_blockquote_11r93_3", u = "_filled_11r93_16", p = "_plain_11r93_19", q = "_sm_11r93_24", h = "_md_11r93_27", y = "_lg_11r93_30", g = "_mark_11r93_35", N = "_body_11r93_45", x = "_content_11r93_54", v = "_cite_11r93_59", o = {
5
+ blockquote: k,
6
+ filled: u,
7
+ plain: p,
8
+ sm: q,
9
+ md: h,
10
+ lg: y,
11
+ mark: g,
12
+ body: N,
13
+ content: x,
14
+ cite: v
15
+ }, R = b(function({
16
+ variant: l = "filled",
17
+ color: r = "accent",
18
+ size: n = "md",
19
+ cite: c,
20
+ quoteMark: s = !1,
21
+ className: _,
22
+ style: a,
23
+ children: i,
24
+ ...d
25
+ }, m) {
26
+ return /* @__PURE__ */ e(
27
+ "blockquote",
28
+ {
29
+ ref: m,
30
+ className: f(o.blockquote, o[l], o[n], _),
31
+ style: { "--tz-btn-rgb": `var(--tz-color-${r}-rgb)`, ...a },
32
+ ...d,
33
+ children: [
34
+ s && /* @__PURE__ */ t("span", { className: o.mark, "aria-hidden": !0, children: "“" }),
35
+ /* @__PURE__ */ e("div", { className: o.body, children: [
36
+ /* @__PURE__ */ t("div", { className: o.content, children: i }),
37
+ c != null && /* @__PURE__ */ t("footer", { className: o.cite, children: /* @__PURE__ */ t("cite", { children: c }) })
38
+ ] })
39
+ ]
40
+ }
41
+ );
42
+ });
43
+ export {
44
+ R as B
45
+ };
46
+ //# sourceMappingURL=Blockquote-CNIuJFHq.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Blockquote-CNIuJFHq.js","sources":["../src/components/Blockquote/Blockquote.tsx"],"sourcesContent":["import {\n forwardRef,\n type BlockquoteHTMLAttributes,\n type CSSProperties,\n type ReactNode,\n} from 'react'\nimport { clsx } from 'clsx'\nimport type { ThemeColor } from '../../theme'\nimport styles from './Blockquote.module.css'\n\nexport type BlockquoteVariant = 'filled' | 'plain'\nexport type BlockquoteSize = 'sm' | 'md' | 'lg'\n\nexport interface BlockquoteProps extends Omit<\n BlockquoteHTMLAttributes<HTMLQuoteElement>,\n 'color' | 'cite'\n> {\n /** Tint style — `filled` (soft tinted background + accent bar) or `plain` (accent bar only). Defaults to `filled`. */\n variant?: BlockquoteVariant\n /** Theme palette token that tints the accent bar, background and quote mark. Defaults to `accent`. */\n color?: ThemeColor\n /** Quote text size. Defaults to `md`. */\n size?: BlockquoteSize\n /** Optional attribution shown below the quote (e.g. an author or source), prefixed with an em dash. */\n cite?: ReactNode\n /** Show the decorative leading quote mark. Defaults to `false`. */\n quoteMark?: boolean\n /** The quoted content. */\n children?: ReactNode\n}\n\n/**\n * A styled quotation block. Renders a semantic `<blockquote>` with a tinted left accent bar, an\n * optional decorative quote mark, and an optional `cite` attribution line. Tinted by `color` via the\n * shared `--tz-btn-rgb` pattern; `variant` toggles a soft tinted background (`filled`) vs. a bare bar\n * (`plain`). The attribution renders in a `<footer>` with a semantic `<cite>`.\n */\nexport const Blockquote = forwardRef<HTMLQuoteElement, BlockquoteProps>(function Blockquote(\n {\n variant = 'filled',\n color = 'accent',\n size = 'md',\n cite,\n quoteMark = false,\n className,\n style,\n children,\n ...props\n },\n ref,\n) {\n return (\n <blockquote\n ref={ref}\n className={clsx(styles.blockquote, styles[variant], styles[size], className)}\n style={{ '--tz-btn-rgb': `var(--tz-color-${color}-rgb)`, ...style } as CSSProperties}\n {...props}\n >\n {quoteMark && (\n <span className={styles.mark} aria-hidden>\n &ldquo;\n </span>\n )}\n <div className={styles.body}>\n <div className={styles.content}>{children}</div>\n {cite != null && (\n <footer className={styles.cite}>\n <cite>{cite}</cite>\n </footer>\n )}\n </div>\n </blockquote>\n )\n})\n"],"names":["Blockquote","forwardRef","variant","color","size","cite","quoteMark","className","style","children","props","ref","jsxs","clsx","styles","jsx"],"mappings":";;;;;;;;;;;;;;GAqCaA,IAAaC,EAA8C,SACtE;AAAA,EACE,SAAAC,IAAU;AAAA,EACV,OAAAC,IAAQ;AAAA,EACR,MAAAC,IAAO;AAAA,EACP,MAAAC;AAAA,EACA,WAAAC,IAAY;AAAA,EACZ,WAAAC;AAAA,EACA,OAAAC;AAAA,EACA,UAAAC;AAAA,EACA,GAAGC;AACL,GACAC,GACA;AACA,SACE,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAAD;AAAA,MACA,WAAWE,EAAKC,EAAO,YAAYA,EAAOZ,CAAO,GAAGY,EAAOV,CAAI,GAAGG,CAAS;AAAA,MAC3E,OAAO,EAAE,gBAAgB,kBAAkBJ,CAAK,SAAS,GAAGK,EAAA;AAAA,MAC3D,GAAGE;AAAA,MAEH,UAAA;AAAA,QAAAJ,uBACE,QAAA,EAAK,WAAWQ,EAAO,MAAM,eAAW,IAAC,UAAA,IAAA,CAE1C;AAAA,QAEF,gBAAAF,EAAC,OAAA,EAAI,WAAWE,EAAO,MACrB,UAAA;AAAA,UAAA,gBAAAC,EAAC,OAAA,EAAI,WAAWD,EAAO,SAAU,UAAAL,GAAS;AAAA,UACzCJ,KAAQ,QACP,gBAAAU,EAAC,UAAA,EAAO,WAAWD,EAAO,MACxB,UAAA,gBAAAC,EAAC,QAAA,EAAM,UAAAV,EAAA,CAAK,EAAA,CACd;AAAA,QAAA,EAAA,CAEJ;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGN,CAAC;"}
@@ -0,0 +1,2 @@
1
+ "use strict";const t=require("react/jsx-runtime"),m=require("react"),u=require("clsx"),b="_blockquote_11r93_3",k="_filled_11r93_16",q="_plain_11r93_19",f="_sm_11r93_24",x="_md_11r93_27",j="_lg_11r93_30",h="_mark_11r93_35",y="_body_11r93_45",g="_content_11r93_54",N="_cite_11r93_59",c={blockquote:b,filled:k,plain:q,sm:f,md:x,lg:j,mark:h,body:y,content:g,cite:N},p=m.forwardRef(function({variant:s="filled",color:o="accent",size:l="md",cite:e,quoteMark:n=!1,className:r,style:_,children:i,...a},d){return t.jsxs("blockquote",{ref:d,className:u.clsx(c.blockquote,c[s],c[l],r),style:{"--tz-btn-rgb":`var(--tz-color-${o}-rgb)`,..._},...a,children:[n&&t.jsx("span",{className:c.mark,"aria-hidden":!0,children:"“"}),t.jsxs("div",{className:c.body,children:[t.jsx("div",{className:c.content,children:i}),e!=null&&t.jsx("footer",{className:c.cite,children:t.jsx("cite",{children:e})})]})]})});exports.Blockquote=p;
2
+ //# sourceMappingURL=Blockquote-CxHwOecc.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Blockquote-CxHwOecc.cjs","sources":["../src/components/Blockquote/Blockquote.tsx"],"sourcesContent":["import {\n forwardRef,\n type BlockquoteHTMLAttributes,\n type CSSProperties,\n type ReactNode,\n} from 'react'\nimport { clsx } from 'clsx'\nimport type { ThemeColor } from '../../theme'\nimport styles from './Blockquote.module.css'\n\nexport type BlockquoteVariant = 'filled' | 'plain'\nexport type BlockquoteSize = 'sm' | 'md' | 'lg'\n\nexport interface BlockquoteProps extends Omit<\n BlockquoteHTMLAttributes<HTMLQuoteElement>,\n 'color' | 'cite'\n> {\n /** Tint style — `filled` (soft tinted background + accent bar) or `plain` (accent bar only). Defaults to `filled`. */\n variant?: BlockquoteVariant\n /** Theme palette token that tints the accent bar, background and quote mark. Defaults to `accent`. */\n color?: ThemeColor\n /** Quote text size. Defaults to `md`. */\n size?: BlockquoteSize\n /** Optional attribution shown below the quote (e.g. an author or source), prefixed with an em dash. */\n cite?: ReactNode\n /** Show the decorative leading quote mark. Defaults to `false`. */\n quoteMark?: boolean\n /** The quoted content. */\n children?: ReactNode\n}\n\n/**\n * A styled quotation block. Renders a semantic `<blockquote>` with a tinted left accent bar, an\n * optional decorative quote mark, and an optional `cite` attribution line. Tinted by `color` via the\n * shared `--tz-btn-rgb` pattern; `variant` toggles a soft tinted background (`filled`) vs. a bare bar\n * (`plain`). The attribution renders in a `<footer>` with a semantic `<cite>`.\n */\nexport const Blockquote = forwardRef<HTMLQuoteElement, BlockquoteProps>(function Blockquote(\n {\n variant = 'filled',\n color = 'accent',\n size = 'md',\n cite,\n quoteMark = false,\n className,\n style,\n children,\n ...props\n },\n ref,\n) {\n return (\n <blockquote\n ref={ref}\n className={clsx(styles.blockquote, styles[variant], styles[size], className)}\n style={{ '--tz-btn-rgb': `var(--tz-color-${color}-rgb)`, ...style } as CSSProperties}\n {...props}\n >\n {quoteMark && (\n <span className={styles.mark} aria-hidden>\n &ldquo;\n </span>\n )}\n <div className={styles.body}>\n <div className={styles.content}>{children}</div>\n {cite != null && (\n <footer className={styles.cite}>\n <cite>{cite}</cite>\n </footer>\n )}\n </div>\n </blockquote>\n )\n})\n"],"names":["Blockquote","forwardRef","variant","color","size","cite","quoteMark","className","style","children","props","ref","jsxs","clsx","styles","jsx"],"mappings":"0WAqCaA,EAAaC,EAAAA,WAA8C,SACtE,CACE,QAAAC,EAAU,SACV,MAAAC,EAAQ,SACR,KAAAC,EAAO,KACP,KAAAC,EACA,UAAAC,EAAY,GACZ,UAAAC,EACA,MAAAC,EACA,SAAAC,EACA,GAAGC,CACL,EACAC,EACA,CACA,OACEC,EAAAA,KAAC,aAAA,CACC,IAAAD,EACA,UAAWE,EAAAA,KAAKC,EAAO,WAAYA,EAAOZ,CAAO,EAAGY,EAAOV,CAAI,EAAGG,CAAS,EAC3E,MAAO,CAAE,eAAgB,kBAAkBJ,CAAK,QAAS,GAAGK,CAAA,EAC3D,GAAGE,EAEH,SAAA,CAAAJ,SACE,OAAA,CAAK,UAAWQ,EAAO,KAAM,cAAW,GAAC,SAAA,GAAA,CAE1C,EAEFF,EAAAA,KAAC,MAAA,CAAI,UAAWE,EAAO,KACrB,SAAA,CAAAC,EAAAA,IAAC,MAAA,CAAI,UAAWD,EAAO,QAAU,SAAAL,EAAS,EACzCJ,GAAQ,MACPU,EAAAA,IAAC,SAAA,CAAO,UAAWD,EAAO,KACxB,SAAAC,EAAAA,IAAC,OAAA,CAAM,SAAAV,CAAA,CAAK,CAAA,CACd,CAAA,CAAA,CAEJ,CAAA,CAAA,CAAA,CAGN,CAAC"}
@@ -0,0 +1,2 @@
1
+ "use strict";const d=require("react/jsx-runtime"),q=require("react"),i=require("clsx"),m="_code_19qlq_3",u="_soft_19qlq_18",f="_outlined_19qlq_21",a="_sm_19qlq_27",g="_md_19qlq_30",x="_lg_19qlq_33",o={code:m,soft:u,outlined:f,sm:a,md:g,lg:x},b=q.forwardRef(function({variant:s="soft",color:t="primary",size:e="md",className:c,style:n,children:l,...r},_){return d.jsx("code",{ref:_,className:i.clsx(o.code,o[s],o[e],c),style:{"--tz-btn-rgb":`var(--tz-color-${t}-rgb)`,...n},...r,children:l})});exports.Code=b;
2
+ //# sourceMappingURL=Code-CUdpWlY5.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Code-CUdpWlY5.cjs","sources":["../src/components/Code/Code.tsx"],"sourcesContent":["import { forwardRef, type CSSProperties, type HTMLAttributes, type ReactNode } from 'react'\nimport { clsx } from 'clsx'\nimport type { ThemeColor } from '../../theme'\nimport styles from './Code.module.css'\n\nexport type CodeVariant = 'soft' | 'outlined'\nexport type CodeSize = 'sm' | 'md' | 'lg'\n\nexport interface CodeProps extends Omit<HTMLAttributes<HTMLElement>, 'color'> {\n /** Tint style — `soft` (subtle tinted background) or `outlined` (hairline border, transparent). Defaults to `soft`. */\n variant?: CodeVariant\n /** Theme palette token that tints the text and background. Defaults to `primary`. */\n color?: ThemeColor\n /** Text size. Defaults to `md`. */\n size?: CodeSize\n /** The inline code content. */\n children?: ReactNode\n}\n\n/**\n * An inline code snippet — a monospace `<code>` element with a soft rounded background, for calling out\n * commands, identifiers or short snippets within body text (e.g. `console.log(\"Hello\")`). Tinted by\n * `color` via the shared `--tz-btn-rgb` pattern; `variant` toggles a subtle tinted fill (`soft`) vs. a\n * hairline border (`outlined`). For a full multi-line, syntax-highlighted block use `CodeBlock` instead.\n */\nexport const Code = forwardRef<HTMLElement, CodeProps>(function Code(\n { variant = 'soft', color = 'primary', size = 'md', className, style, children, ...props },\n ref,\n) {\n return (\n <code\n ref={ref}\n className={clsx(styles.code, styles[variant], styles[size], className)}\n style={{ '--tz-btn-rgb': `var(--tz-color-${color}-rgb)`, ...style } as CSSProperties}\n {...props}\n >\n {children}\n </code>\n )\n})\n"],"names":["Code","forwardRef","variant","color","size","className","style","children","props","ref","jsx","clsx","styles"],"mappings":"kPAyBaA,EAAOC,EAAAA,WAAmC,SACrD,CAAE,QAAAC,EAAU,OAAQ,MAAAC,EAAQ,UAAW,KAAAC,EAAO,KAAM,UAAAC,EAAW,MAAAC,EAAO,SAAAC,EAAU,GAAGC,CAAA,EACnFC,EACA,CACA,OACEC,EAAAA,IAAC,OAAA,CACC,IAAAD,EACA,UAAWE,EAAAA,KAAKC,EAAO,KAAMA,EAAOV,CAAO,EAAGU,EAAOR,CAAI,EAAGC,CAAS,EACrE,MAAO,CAAE,eAAgB,kBAAkBF,CAAK,QAAS,GAAGG,CAAA,EAC3D,GAAGE,EAEH,SAAAD,CAAA,CAAA,CAGP,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { jsx as m } from "react/jsx-runtime";
2
+ import { forwardRef as n } from "react";
3
+ import { clsx as q } from "clsx";
4
+ const f = "_code_19qlq_3", i = "_soft_19qlq_18", a = "_outlined_19qlq_21", g = "_sm_19qlq_27", p = "_md_19qlq_30", u = "_lg_19qlq_33", o = {
5
+ code: f,
6
+ soft: i,
7
+ outlined: a,
8
+ sm: g,
9
+ md: p,
10
+ lg: u
11
+ }, z = n(function({ variant: t = "soft", color: s = "primary", size: _ = "md", className: c, style: l, children: r, ...e }, d) {
12
+ return /* @__PURE__ */ m(
13
+ "code",
14
+ {
15
+ ref: d,
16
+ className: q(o.code, o[t], o[_], c),
17
+ style: { "--tz-btn-rgb": `var(--tz-color-${s}-rgb)`, ...l },
18
+ ...e,
19
+ children: r
20
+ }
21
+ );
22
+ });
23
+ export {
24
+ z as C
25
+ };
26
+ //# sourceMappingURL=Code-DaEpXHfF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Code-DaEpXHfF.js","sources":["../src/components/Code/Code.tsx"],"sourcesContent":["import { forwardRef, type CSSProperties, type HTMLAttributes, type ReactNode } from 'react'\nimport { clsx } from 'clsx'\nimport type { ThemeColor } from '../../theme'\nimport styles from './Code.module.css'\n\nexport type CodeVariant = 'soft' | 'outlined'\nexport type CodeSize = 'sm' | 'md' | 'lg'\n\nexport interface CodeProps extends Omit<HTMLAttributes<HTMLElement>, 'color'> {\n /** Tint style — `soft` (subtle tinted background) or `outlined` (hairline border, transparent). Defaults to `soft`. */\n variant?: CodeVariant\n /** Theme palette token that tints the text and background. Defaults to `primary`. */\n color?: ThemeColor\n /** Text size. Defaults to `md`. */\n size?: CodeSize\n /** The inline code content. */\n children?: ReactNode\n}\n\n/**\n * An inline code snippet — a monospace `<code>` element with a soft rounded background, for calling out\n * commands, identifiers or short snippets within body text (e.g. `console.log(\"Hello\")`). Tinted by\n * `color` via the shared `--tz-btn-rgb` pattern; `variant` toggles a subtle tinted fill (`soft`) vs. a\n * hairline border (`outlined`). For a full multi-line, syntax-highlighted block use `CodeBlock` instead.\n */\nexport const Code = forwardRef<HTMLElement, CodeProps>(function Code(\n { variant = 'soft', color = 'primary', size = 'md', className, style, children, ...props },\n ref,\n) {\n return (\n <code\n ref={ref}\n className={clsx(styles.code, styles[variant], styles[size], className)}\n style={{ '--tz-btn-rgb': `var(--tz-color-${color}-rgb)`, ...style } as CSSProperties}\n {...props}\n >\n {children}\n </code>\n )\n})\n"],"names":["Code","forwardRef","variant","color","size","className","style","children","props","ref","jsx","clsx","styles"],"mappings":";;;;;;;;;;GAyBaA,IAAOC,EAAmC,SACrD,EAAE,SAAAC,IAAU,QAAQ,OAAAC,IAAQ,WAAW,MAAAC,IAAO,MAAM,WAAAC,GAAW,OAAAC,GAAO,UAAAC,GAAU,GAAGC,EAAA,GACnFC,GACA;AACA,SACE,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAAD;AAAA,MACA,WAAWE,EAAKC,EAAO,MAAMA,EAAOV,CAAO,GAAGU,EAAOR,CAAI,GAAGC,CAAS;AAAA,MACrE,OAAO,EAAE,gBAAgB,kBAAkBF,CAAK,SAAS,GAAGG,EAAA;AAAA,MAC3D,GAAGE;AAAA,MAEH,UAAAD;AAAA,IAAA;AAAA,EAAA;AAGP,CAAC;"}
@@ -0,0 +1,96 @@
1
+ import { jsx as o, jsxs as _ } from "react/jsx-runtime";
2
+ import { forwardRef as I, useState as h, useRef as S, useEffect as y } from "react";
3
+ import { clsx as w } from "clsx";
4
+ import { I as g } from "./IconButton-CBusrDa9.js";
5
+ import { I as z } from "./Icon-CophpkHo.js";
6
+ import { T as E } from "./Tooltip-Cb_FKxT9.js";
7
+ import { a as R } from "./ConfigProvider-2c94kUOk.js";
8
+ const j = "_root_floau_4", M = "_header_floau_15", D = "_title_floau_24", H = "_code_floau_34", L = "_plain_floau_50", O = "_wrap_floau_56", P = "_lineNumbers_floau_62", q = "_copy_floau_77", e = {
9
+ root: j,
10
+ header: M,
11
+ title: D,
12
+ code: H,
13
+ plain: L,
14
+ wrap: O,
15
+ lineNumbers: P,
16
+ copy: q
17
+ }, A = 1600, X = I(function({
18
+ code: l,
19
+ language: a = "tsx",
20
+ title: i,
21
+ copyable: b = !0,
22
+ showLineNumbers: N = !0,
23
+ maxHeight: d,
24
+ wrap: T = !1,
25
+ className: C,
26
+ style: k,
27
+ ...v
28
+ }, B) {
29
+ const c = R(), [p, u] = h(null), [n, m] = h(!1), r = S(void 0);
30
+ y(() => {
31
+ let t = !1;
32
+ return import("shiki").then(
33
+ ({ codeToHtml: s }) => s(l, {
34
+ lang: a,
35
+ // always VS Code's dark theme — the block stays dark in both app modes
36
+ theme: "dark-plus"
37
+ })
38
+ ).then((s) => {
39
+ t || u(s);
40
+ }).catch(() => {
41
+ t || u(null);
42
+ }), () => {
43
+ t = !0;
44
+ };
45
+ }, [l, a]), y(() => () => window.clearTimeout(r.current), []);
46
+ const x = () => {
47
+ var t;
48
+ (t = navigator.clipboard) == null || t.writeText(l).then(() => {
49
+ m(!0), window.clearTimeout(r.current), r.current = window.setTimeout(() => m(!1), A);
50
+ }).catch(() => {
51
+ });
52
+ }, f = b && // the tooltip is hover/focus-driven; clicking flips `copied`, so its content updates to "Copied!"
53
+ // while it's still open (the pointer is on the button). `left` keeps it inside the block's
54
+ // `overflow: hidden` bounds.
55
+ /* @__PURE__ */ o(
56
+ E,
57
+ {
58
+ content: c(n ? "codeBlock.copied" : "codeBlock.copy"),
59
+ placement: "left",
60
+ style: { "--tz-btn-rgb": "255, 255, 255", "--tz-btn-on": "#1e1e1e" },
61
+ children: /* @__PURE__ */ o(
62
+ g,
63
+ {
64
+ variant: "text",
65
+ size: "sm",
66
+ className: e.copy,
67
+ style: { "--tz-btn-rgb": "255, 255, 255" },
68
+ "aria-label": c(n ? "codeBlock.copied" : "codeBlock.copy"),
69
+ onClick: x,
70
+ children: /* @__PURE__ */ o(z, { name: n ? "CopySuccess" : "Copy" })
71
+ }
72
+ )
73
+ }
74
+ );
75
+ return /* @__PURE__ */ _("div", { ref: B, className: w(e.root, C), style: k, ...v, children: [
76
+ (i != null || f) && /* @__PURE__ */ _("div", { className: e.header, children: [
77
+ /* @__PURE__ */ o("span", { className: e.title, children: i }),
78
+ f
79
+ ] }),
80
+ /* @__PURE__ */ o(
81
+ "div",
82
+ {
83
+ className: w(e.code, T && e.wrap, N && e.lineNumbers),
84
+ style: d != null ? { maxHeight: d } : void 0,
85
+ children: p != null ? (
86
+ // shiki output is trusted markup — it escapes the source code itself
87
+ /* @__PURE__ */ o("div", { className: e.highlighted, dangerouslySetInnerHTML: { __html: p } })
88
+ ) : /* @__PURE__ */ o("pre", { className: e.plain, children: /* @__PURE__ */ o("code", { children: l }) })
89
+ }
90
+ )
91
+ ] });
92
+ });
93
+ export {
94
+ X as C
95
+ };
96
+ //# sourceMappingURL=CodeBlock-BWxQyKRo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CodeBlock-BWxQyKRo.js","sources":["../src/components/CodeBlock/CodeBlock.tsx"],"sourcesContent":["import {\n forwardRef,\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n type ReactNode,\n} from 'react'\nimport { clsx } from 'clsx'\nimport { useT } from '../../theme'\nimport { IconButton } from '../IconButton'\nimport { Icon } from '../Icon'\nimport { Tooltip } from '../Tooltip'\nimport styles from './CodeBlock.module.css'\n\n// how long the copy button shows the \"copied\" tick before reverting\nconst COPIED_RESET_MS = 1600\n\nexport interface CodeBlockProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'title'> {\n /** The source code to render. */\n code: string\n /** Shiki language id (`'tsx'`, `'json'`, `'bash'`, `'css'`, …). Defaults to `'tsx'`. */\n language?: string\n /**\n * Optional label shown on the left of the header bar (e.g. a filename). The header bar itself always\n * renders (whenever `copyable`), so the copy button lives in it with or without a `title`.\n */\n title?: ReactNode\n /** Show a copy-to-clipboard button (in the header bar). Defaults to `true`. */\n copyable?: boolean\n /** Show line numbers. Defaults to `true`. */\n showLineNumbers?: boolean\n /** Cap the block height in px — taller code scrolls inside. */\n maxHeight?: number\n /** Soft-wrap long lines instead of scrolling horizontally. Defaults to `false`. */\n wrap?: boolean\n}\n\n/**\n * A syntax-highlighted code block with **VS Code colors** — powered by **Shiki** (an optional peer,\n * `npm i shiki`), the actual VS Code highlighting engine, using its **`dark-plus`** theme. The block\n * is **always dark** (in both app modes) — code reads best on a dark surface, and the deep background\n * anchors it regardless of the surrounding light/dark theme. Highlighting is **async and lazy**\n * (`import('shiki')` on first render); until it lands — or if `shiki` isn't installed / the language\n * is unknown — the block shows the plain code (on the same dark surface), so nothing breaks. A header\n * bar always renders (whenever `copyable`, default `true`) and holds the copy button — which flips to a\n * tick on click — with an optional `title` (e.g. a filename) on its left; `showLineNumbers` (default\n * `true`), `maxHeight` (scrolls inside) and `wrap` shape the body. The whole block\n * keeps VS Code's own dark colors (a deliberate literal-color exception, like the Modal scrim); only\n * the outer border/radius uses `--tz-*` tokens.\n */\nexport const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(function CodeBlock(\n {\n code,\n language = 'tsx',\n title,\n copyable = true,\n showLineNumbers = true,\n maxHeight,\n wrap = false,\n className,\n style,\n ...props\n },\n ref,\n) {\n const t = useT()\n const [html, setHtml] = useState<string | null>(null)\n const [copied, setCopied] = useState(false)\n const copyTimer = useRef<number | undefined>(undefined)\n\n // highlight asynchronously — shiki is an optional peer, loaded lazily on first use; a missing\n // package or an unknown language just leaves the plain (unhighlighted) fallback in place\n useEffect(() => {\n let cancelled = false\n import('shiki')\n .then(({ codeToHtml }) =>\n codeToHtml(code, {\n lang: language,\n // always VS Code's dark theme — the block stays dark in both app modes\n theme: 'dark-plus',\n }),\n )\n .then((out) => {\n if (!cancelled) setHtml(out)\n })\n .catch(() => {\n if (!cancelled) setHtml(null)\n })\n return () => {\n cancelled = true\n }\n }, [code, language])\n\n useEffect(() => () => window.clearTimeout(copyTimer.current), [])\n\n const handleCopy = () => {\n navigator.clipboard\n ?.writeText(code)\n .then(() => {\n setCopied(true)\n window.clearTimeout(copyTimer.current)\n copyTimer.current = window.setTimeout(() => setCopied(false), COPIED_RESET_MS)\n })\n .catch(() => {})\n }\n\n const copyButton = copyable && (\n // the tooltip is hover/focus-driven; clicking flips `copied`, so its content updates to \"Copied!\"\n // while it's still open (the pointer is on the button). `left` keeps it inside the block's\n // `overflow: hidden` bounds.\n <Tooltip\n content={copied ? t('codeBlock.copied') : t('codeBlock.copy')}\n placement=\"left\"\n // always a white pill with dark text over the (always-dark) block — set via `style` so it beats\n // Tooltip's own color-derived --tz-btn-rgb (which is spread before the consumer `style`)\n style={{ '--tz-btn-rgb': '255, 255, 255', '--tz-btn-on': '#1e1e1e' } as CSSProperties}\n >\n <IconButton\n variant=\"text\"\n size=\"sm\"\n className={styles.copy}\n // force the button light over the dark surface — passed via `style` so it beats IconButton's\n // own inline `--tz-btn-rgb` (which is spread before the consumer `style`, so this wins)\n style={{ '--tz-btn-rgb': '255, 255, 255' } as CSSProperties}\n aria-label={copied ? t('codeBlock.copied') : t('codeBlock.copy')}\n onClick={handleCopy}\n >\n <Icon name={copied ? 'CopySuccess' : 'Copy'} />\n </IconButton>\n </Tooltip>\n )\n\n return (\n <div ref={ref} className={clsx(styles.root, className)} style={style} {...props}>\n {/* the header bar always renders whenever there's a title or a copy button — so the copy icon\n lives in the bar (top-right), never floating over the code */}\n {(title != null || copyButton) && (\n <div className={styles.header}>\n <span className={styles.title}>{title}</span>\n {copyButton}\n </div>\n )}\n <div\n className={clsx(styles.code, wrap && styles.wrap, showLineNumbers && styles.lineNumbers)}\n style={maxHeight != null ? ({ maxHeight } as CSSProperties) : undefined}\n >\n {html != null ? (\n // shiki output is trusted markup — it escapes the source code itself\n <div className={styles.highlighted} dangerouslySetInnerHTML={{ __html: html }} />\n ) : (\n <pre className={styles.plain}>\n <code>{code}</code>\n </pre>\n )}\n </div>\n </div>\n )\n})\n"],"names":["COPIED_RESET_MS","CodeBlock","forwardRef","code","language","title","copyable","showLineNumbers","maxHeight","wrap","className","style","props","ref","t","useT","html","setHtml","useState","copied","setCopied","copyTimer","useRef","useEffect","cancelled","codeToHtml","out","handleCopy","_a","copyButton","jsx","Tooltip","IconButton","styles","Icon","jsxs","clsx"],"mappings":";;;;;;;;;;;;;;;;GAiBMA,IAAkB,MAmCXC,IAAYC,EAA2C,SAClE;AAAA,EACE,MAAAC;AAAA,EACA,UAAAC,IAAW;AAAA,EACX,OAAAC;AAAA,EACA,UAAAC,IAAW;AAAA,EACX,iBAAAC,IAAkB;AAAA,EAClB,WAAAC;AAAA,EACA,MAAAC,IAAO;AAAA,EACP,WAAAC;AAAA,EACA,OAAAC;AAAA,EACA,GAAGC;AACL,GACAC,GACA;AACA,QAAMC,IAAIC,EAAA,GACJ,CAACC,GAAMC,CAAO,IAAIC,EAAwB,IAAI,GAC9C,CAACC,GAAQC,CAAS,IAAIF,EAAS,EAAK,GACpCG,IAAYC,EAA2B,MAAS;AAItD,EAAAC,EAAU,MAAM;AACd,QAAIC,IAAY;AAChB,kBAAO,OAAO,EACX;AAAA,MAAK,CAAC,EAAE,YAAAC,EAAA,MACPA,EAAWtB,GAAM;AAAA,QACf,MAAMC;AAAA;AAAA,QAEN,OAAO;AAAA,MAAA,CACR;AAAA,IAAA,EAEF,KAAK,CAACsB,MAAQ;AACb,MAAKF,KAAWP,EAAQS,CAAG;AAAA,IAC7B,CAAC,EACA,MAAM,MAAM;AACX,MAAKF,KAAWP,EAAQ,IAAI;AAAA,IAC9B,CAAC,GACI,MAAM;AACX,MAAAO,IAAY;AAAA,IACd;AAAA,EACF,GAAG,CAACrB,GAAMC,CAAQ,CAAC,GAEnBmB,EAAU,MAAM,MAAM,OAAO,aAAaF,EAAU,OAAO,GAAG,EAAE;AAEhE,QAAMM,IAAa,MAAM;;AACvB,KAAAC,IAAA,UAAU,cAAV,QAAAA,EACI,UAAUzB,GACX,KAAK,MAAM;AACV,MAAAiB,EAAU,EAAI,GACd,OAAO,aAAaC,EAAU,OAAO,GACrCA,EAAU,UAAU,OAAO,WAAW,MAAMD,EAAU,EAAK,GAAGpB,CAAe;AAAA,IAC/E,GACC,MAAM,MAAM;AAAA,IAAC;AAAA,EAClB,GAEM6B,IAAavB;AAAA;AAAA;AAAA,EAIjB,gBAAAwB;AAAA,IAACC;AAAA,IAAA;AAAA,MACC,SAAkBjB,EAATK,IAAW,qBAAwB,gBAAN;AAAA,MACtC,WAAU;AAAA,MAGV,OAAO,EAAE,gBAAgB,iBAAiB,eAAe,UAAA;AAAA,MAEzD,UAAA,gBAAAW;AAAA,QAACE;AAAA,QAAA;AAAA,UACC,SAAQ;AAAA,UACR,MAAK;AAAA,UACL,WAAWC,EAAO;AAAA,UAGlB,OAAO,EAAE,gBAAgB,gBAAA;AAAA,UACzB,cAAqBnB,EAATK,IAAW,qBAAwB,gBAAN;AAAA,UACzC,SAASQ;AAAA,UAET,UAAA,gBAAAG,EAACI,GAAA,EAAK,MAAMf,IAAS,gBAAgB,OAAA,CAAQ;AAAA,QAAA;AAAA,MAAA;AAAA,IAC/C;AAAA,EAAA;AAIJ,SACE,gBAAAgB,EAAC,OAAA,EAAI,KAAAtB,GAAU,WAAWuB,EAAKH,EAAO,MAAMvB,CAAS,GAAG,OAAAC,GAAe,GAAGC,GAGtE,UAAA;AAAA,KAAAP,KAAS,QAAQwB,MACjB,gBAAAM,EAAC,OAAA,EAAI,WAAWF,EAAO,QACrB,UAAA;AAAA,MAAA,gBAAAH,EAAC,QAAA,EAAK,WAAWG,EAAO,OAAQ,UAAA5B,GAAM;AAAA,MACrCwB;AAAA,IAAA,GACH;AAAA,IAEF,gBAAAC;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAWM,EAAKH,EAAO,MAAMxB,KAAQwB,EAAO,MAAM1B,KAAmB0B,EAAO,WAAW;AAAA,QACvF,OAAOzB,KAAa,OAAQ,EAAE,WAAAA,MAAgC;AAAA,QAE7D,UAAAQ,KAAQ;AAAA;AAAA,UAEP,gBAAAc,EAAC,SAAI,WAAWG,EAAO,aAAa,yBAAyB,EAAE,QAAQjB,IAAK,CAAG;AAAA,YAE/E,gBAAAc,EAAC,SAAI,WAAWG,EAAO,OACrB,UAAA,gBAAAH,EAAC,QAAA,EAAM,aAAK,EAAA,CACd;AAAA,MAAA;AAAA,IAAA;AAAA,EAEJ,GACF;AAEJ,CAAC;"}
@@ -0,0 +1,2 @@
1
+ "use strict";var v=Object.create;var y=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var S=Object.getPrototypeOf,g=Object.prototype.hasOwnProperty;var E=(t,e,s,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of I(e))!g.call(t,n)&&n!==s&&y(t,n,{get:()=>e[n],enumerable:!(i=q(e,n))||i.enumerable});return t};var z=(t,e,s)=>(s=t!=null?v(S(t)):{},E(e||!t||!t.__esModule?y(s,"default",{value:t,enumerable:!0}):s,t));const o=require("react/jsx-runtime"),r=require("react"),w=require("clsx"),R=require("./IconButton-D6TenQZ_.cjs"),M=require("./Icon-CXTl_QAP.cjs"),P=require("./Tooltip-Ao7az09p.cjs"),D=require("./ConfigProvider-ByOFXVyP.cjs"),H="_root_floau_4",L="_header_floau_15",O="_title_floau_24",A="_code_floau_34",F="_plain_floau_50",G="_wrap_floau_56",J="_lineNumbers_floau_62",K="_copy_floau_77",c={root:H,header:L,title:O,code:A,plain:F,wrap:G,lineNumbers:J,copy:K},Q=1600,U=r.forwardRef(function({code:e,language:s="tsx",title:i,copyable:n=!0,showLineNumbers:b=!0,maxHeight:_,wrap:j=!1,className:N,style:B,...C},T){const a=D.useT(),[f,h]=r.useState(null),[u,m]=r.useState(!1),d=r.useRef(void 0);r.useEffect(()=>{let l=!1;return import("shiki").then(({codeToHtml:p})=>p(e,{lang:s,theme:"dark-plus"})).then(p=>{l||h(p)}).catch(()=>{l||h(null)}),()=>{l=!0}},[e,s]),r.useEffect(()=>()=>window.clearTimeout(d.current),[]);const k=()=>{var l;(l=navigator.clipboard)==null||l.writeText(e).then(()=>{m(!0),window.clearTimeout(d.current),d.current=window.setTimeout(()=>m(!1),Q)}).catch(()=>{})},x=n&&o.jsx(P.Tooltip,{content:a(u?"codeBlock.copied":"codeBlock.copy"),placement:"left",style:{"--tz-btn-rgb":"255, 255, 255","--tz-btn-on":"#1e1e1e"},children:o.jsx(R.IconButton,{variant:"text",size:"sm",className:c.copy,style:{"--tz-btn-rgb":"255, 255, 255"},"aria-label":a(u?"codeBlock.copied":"codeBlock.copy"),onClick:k,children:o.jsx(M.Icon,{name:u?"CopySuccess":"Copy"})})});return o.jsxs("div",{ref:T,className:w.clsx(c.root,N),style:B,...C,children:[(i!=null||x)&&o.jsxs("div",{className:c.header,children:[o.jsx("span",{className:c.title,children:i}),x]}),o.jsx("div",{className:w.clsx(c.code,j&&c.wrap,b&&c.lineNumbers),style:_!=null?{maxHeight:_}:void 0,children:f!=null?o.jsx("div",{className:c.highlighted,dangerouslySetInnerHTML:{__html:f}}):o.jsx("pre",{className:c.plain,children:o.jsx("code",{children:e})})})]})});exports.CodeBlock=U;
2
+ //# sourceMappingURL=CodeBlock-CgXOjKS-.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CodeBlock-CgXOjKS-.cjs","sources":["../src/components/CodeBlock/CodeBlock.tsx"],"sourcesContent":["import {\n forwardRef,\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n type ReactNode,\n} from 'react'\nimport { clsx } from 'clsx'\nimport { useT } from '../../theme'\nimport { IconButton } from '../IconButton'\nimport { Icon } from '../Icon'\nimport { Tooltip } from '../Tooltip'\nimport styles from './CodeBlock.module.css'\n\n// how long the copy button shows the \"copied\" tick before reverting\nconst COPIED_RESET_MS = 1600\n\nexport interface CodeBlockProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'title'> {\n /** The source code to render. */\n code: string\n /** Shiki language id (`'tsx'`, `'json'`, `'bash'`, `'css'`, …). Defaults to `'tsx'`. */\n language?: string\n /**\n * Optional label shown on the left of the header bar (e.g. a filename). The header bar itself always\n * renders (whenever `copyable`), so the copy button lives in it with or without a `title`.\n */\n title?: ReactNode\n /** Show a copy-to-clipboard button (in the header bar). Defaults to `true`. */\n copyable?: boolean\n /** Show line numbers. Defaults to `true`. */\n showLineNumbers?: boolean\n /** Cap the block height in px — taller code scrolls inside. */\n maxHeight?: number\n /** Soft-wrap long lines instead of scrolling horizontally. Defaults to `false`. */\n wrap?: boolean\n}\n\n/**\n * A syntax-highlighted code block with **VS Code colors** — powered by **Shiki** (an optional peer,\n * `npm i shiki`), the actual VS Code highlighting engine, using its **`dark-plus`** theme. The block\n * is **always dark** (in both app modes) — code reads best on a dark surface, and the deep background\n * anchors it regardless of the surrounding light/dark theme. Highlighting is **async and lazy**\n * (`import('shiki')` on first render); until it lands — or if `shiki` isn't installed / the language\n * is unknown — the block shows the plain code (on the same dark surface), so nothing breaks. A header\n * bar always renders (whenever `copyable`, default `true`) and holds the copy button — which flips to a\n * tick on click — with an optional `title` (e.g. a filename) on its left; `showLineNumbers` (default\n * `true`), `maxHeight` (scrolls inside) and `wrap` shape the body. The whole block\n * keeps VS Code's own dark colors (a deliberate literal-color exception, like the Modal scrim); only\n * the outer border/radius uses `--tz-*` tokens.\n */\nexport const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(function CodeBlock(\n {\n code,\n language = 'tsx',\n title,\n copyable = true,\n showLineNumbers = true,\n maxHeight,\n wrap = false,\n className,\n style,\n ...props\n },\n ref,\n) {\n const t = useT()\n const [html, setHtml] = useState<string | null>(null)\n const [copied, setCopied] = useState(false)\n const copyTimer = useRef<number | undefined>(undefined)\n\n // highlight asynchronously — shiki is an optional peer, loaded lazily on first use; a missing\n // package or an unknown language just leaves the plain (unhighlighted) fallback in place\n useEffect(() => {\n let cancelled = false\n import('shiki')\n .then(({ codeToHtml }) =>\n codeToHtml(code, {\n lang: language,\n // always VS Code's dark theme — the block stays dark in both app modes\n theme: 'dark-plus',\n }),\n )\n .then((out) => {\n if (!cancelled) setHtml(out)\n })\n .catch(() => {\n if (!cancelled) setHtml(null)\n })\n return () => {\n cancelled = true\n }\n }, [code, language])\n\n useEffect(() => () => window.clearTimeout(copyTimer.current), [])\n\n const handleCopy = () => {\n navigator.clipboard\n ?.writeText(code)\n .then(() => {\n setCopied(true)\n window.clearTimeout(copyTimer.current)\n copyTimer.current = window.setTimeout(() => setCopied(false), COPIED_RESET_MS)\n })\n .catch(() => {})\n }\n\n const copyButton = copyable && (\n // the tooltip is hover/focus-driven; clicking flips `copied`, so its content updates to \"Copied!\"\n // while it's still open (the pointer is on the button). `left` keeps it inside the block's\n // `overflow: hidden` bounds.\n <Tooltip\n content={copied ? t('codeBlock.copied') : t('codeBlock.copy')}\n placement=\"left\"\n // always a white pill with dark text over the (always-dark) block — set via `style` so it beats\n // Tooltip's own color-derived --tz-btn-rgb (which is spread before the consumer `style`)\n style={{ '--tz-btn-rgb': '255, 255, 255', '--tz-btn-on': '#1e1e1e' } as CSSProperties}\n >\n <IconButton\n variant=\"text\"\n size=\"sm\"\n className={styles.copy}\n // force the button light over the dark surface — passed via `style` so it beats IconButton's\n // own inline `--tz-btn-rgb` (which is spread before the consumer `style`, so this wins)\n style={{ '--tz-btn-rgb': '255, 255, 255' } as CSSProperties}\n aria-label={copied ? t('codeBlock.copied') : t('codeBlock.copy')}\n onClick={handleCopy}\n >\n <Icon name={copied ? 'CopySuccess' : 'Copy'} />\n </IconButton>\n </Tooltip>\n )\n\n return (\n <div ref={ref} className={clsx(styles.root, className)} style={style} {...props}>\n {/* the header bar always renders whenever there's a title or a copy button — so the copy icon\n lives in the bar (top-right), never floating over the code */}\n {(title != null || copyButton) && (\n <div className={styles.header}>\n <span className={styles.title}>{title}</span>\n {copyButton}\n </div>\n )}\n <div\n className={clsx(styles.code, wrap && styles.wrap, showLineNumbers && styles.lineNumbers)}\n style={maxHeight != null ? ({ maxHeight } as CSSProperties) : undefined}\n >\n {html != null ? (\n // shiki output is trusted markup — it escapes the source code itself\n <div className={styles.highlighted} dangerouslySetInnerHTML={{ __html: html }} />\n ) : (\n <pre className={styles.plain}>\n <code>{code}</code>\n </pre>\n )}\n </div>\n </div>\n )\n})\n"],"names":["COPIED_RESET_MS","CodeBlock","forwardRef","code","language","title","copyable","showLineNumbers","maxHeight","wrap","className","style","props","ref","t","useT","html","setHtml","useState","copied","setCopied","copyTimer","useRef","useEffect","cancelled","codeToHtml","out","handleCopy","_a","copyButton","jsx","Tooltip","IconButton","styles","Icon","jsxs","clsx"],"mappings":"k6BAiBMA,EAAkB,KAmCXC,EAAYC,EAAAA,WAA2C,SAClE,CACE,KAAAC,EACA,SAAAC,EAAW,MACX,MAAAC,EACA,SAAAC,EAAW,GACX,gBAAAC,EAAkB,GAClB,UAAAC,EACA,KAAAC,EAAO,GACP,UAAAC,EACA,MAAAC,EACA,GAAGC,CACL,EACAC,EACA,CACA,MAAMC,EAAIC,EAAAA,KAAA,EACJ,CAACC,EAAMC,CAAO,EAAIC,EAAAA,SAAwB,IAAI,EAC9C,CAACC,EAAQC,CAAS,EAAIF,EAAAA,SAAS,EAAK,EACpCG,EAAYC,EAAAA,OAA2B,MAAS,EAItDC,EAAAA,UAAU,IAAM,CACd,IAAIC,EAAY,GAChB,cAAO,OAAO,EACX,KAAK,CAAC,CAAE,WAAAC,CAAA,IACPA,EAAWtB,EAAM,CACf,KAAMC,EAEN,MAAO,WAAA,CACR,CAAA,EAEF,KAAMsB,GAAQ,CACRF,GAAWP,EAAQS,CAAG,CAC7B,CAAC,EACA,MAAM,IAAM,CACNF,GAAWP,EAAQ,IAAI,CAC9B,CAAC,EACI,IAAM,CACXO,EAAY,EACd,CACF,EAAG,CAACrB,EAAMC,CAAQ,CAAC,EAEnBmB,EAAAA,UAAU,IAAM,IAAM,OAAO,aAAaF,EAAU,OAAO,EAAG,EAAE,EAEhE,MAAMM,EAAa,IAAM,QACvBC,EAAA,UAAU,YAAV,MAAAA,EACI,UAAUzB,GACX,KAAK,IAAM,CACViB,EAAU,EAAI,EACd,OAAO,aAAaC,EAAU,OAAO,EACrCA,EAAU,QAAU,OAAO,WAAW,IAAMD,EAAU,EAAK,EAAGpB,CAAe,CAC/E,GACC,MAAM,IAAM,CAAC,EAClB,EAEM6B,EAAavB,GAIjBwB,EAAAA,IAACC,EAAAA,QAAA,CACC,QAAkBjB,EAATK,EAAW,mBAAwB,gBAAN,EACtC,UAAU,OAGV,MAAO,CAAE,eAAgB,gBAAiB,cAAe,SAAA,EAEzD,SAAAW,EAAAA,IAACE,EAAAA,WAAA,CACC,QAAQ,OACR,KAAK,KACL,UAAWC,EAAO,KAGlB,MAAO,CAAE,eAAgB,eAAA,EACzB,aAAqBnB,EAATK,EAAW,mBAAwB,gBAAN,EACzC,QAASQ,EAET,SAAAG,EAAAA,IAACI,OAAA,CAAK,KAAMf,EAAS,cAAgB,MAAA,CAAQ,CAAA,CAAA,CAC/C,CAAA,EAIJ,OACEgB,EAAAA,KAAC,MAAA,CAAI,IAAAtB,EAAU,UAAWuB,EAAAA,KAAKH,EAAO,KAAMvB,CAAS,EAAG,MAAAC,EAAe,GAAGC,EAGtE,SAAA,EAAAP,GAAS,MAAQwB,IACjBM,EAAAA,KAAC,MAAA,CAAI,UAAWF,EAAO,OACrB,SAAA,CAAAH,EAAAA,IAAC,OAAA,CAAK,UAAWG,EAAO,MAAQ,SAAA5B,EAAM,EACrCwB,CAAA,EACH,EAEFC,EAAAA,IAAC,MAAA,CACC,UAAWM,EAAAA,KAAKH,EAAO,KAAMxB,GAAQwB,EAAO,KAAM1B,GAAmB0B,EAAO,WAAW,EACvF,MAAOzB,GAAa,KAAQ,CAAE,UAAAA,GAAgC,OAE7D,SAAAQ,GAAQ,KAEPc,MAAC,OAAI,UAAWG,EAAO,YAAa,wBAAyB,CAAE,OAAQjB,EAAK,CAAG,EAE/Ec,EAAAA,IAAC,OAAI,UAAWG,EAAO,MACrB,SAAAH,EAAAA,IAAC,OAAA,CAAM,WAAK,CAAA,CACd,CAAA,CAAA,CAEJ,EACF,CAEJ,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { type BlockquoteHTMLAttributes, type ReactNode } from 'react';
2
+ import type { ThemeColor } from '../../theme';
3
+ export type BlockquoteVariant = 'filled' | 'plain';
4
+ export type BlockquoteSize = 'sm' | 'md' | 'lg';
5
+ export interface BlockquoteProps extends Omit<BlockquoteHTMLAttributes<HTMLQuoteElement>, 'color' | 'cite'> {
6
+ /** Tint style — `filled` (soft tinted background + accent bar) or `plain` (accent bar only). Defaults to `filled`. */
7
+ variant?: BlockquoteVariant;
8
+ /** Theme palette token that tints the accent bar, background and quote mark. Defaults to `accent`. */
9
+ color?: ThemeColor;
10
+ /** Quote text size. Defaults to `md`. */
11
+ size?: BlockquoteSize;
12
+ /** Optional attribution shown below the quote (e.g. an author or source), prefixed with an em dash. */
13
+ cite?: ReactNode;
14
+ /** Show the decorative leading quote mark. Defaults to `false`. */
15
+ quoteMark?: boolean;
16
+ /** The quoted content. */
17
+ children?: ReactNode;
18
+ }
19
+ /**
20
+ * A styled quotation block. Renders a semantic `<blockquote>` with a tinted left accent bar, an
21
+ * optional decorative quote mark, and an optional `cite` attribution line. Tinted by `color` via the
22
+ * shared `--tz-btn-rgb` pattern; `variant` toggles a soft tinted background (`filled`) vs. a bare bar
23
+ * (`plain`). The attribution renders in a `<footer>` with a semantic `<cite>`.
24
+ */
25
+ export declare const Blockquote: import("react").ForwardRefExoticComponent<BlockquoteProps & import("react").RefAttributes<HTMLQuoteElement>>;
@@ -0,0 +1,25 @@
1
+ import { type BlockquoteHTMLAttributes, type ReactNode } from 'react';
2
+ import type { ThemeColor } from '../../theme';
3
+ export type BlockquoteVariant = 'filled' | 'plain';
4
+ export type BlockquoteSize = 'sm' | 'md' | 'lg';
5
+ export interface BlockquoteProps extends Omit<BlockquoteHTMLAttributes<HTMLQuoteElement>, 'color' | 'cite'> {
6
+ /** Tint style — `filled` (soft tinted background + accent bar) or `plain` (accent bar only). Defaults to `filled`. */
7
+ variant?: BlockquoteVariant;
8
+ /** Theme palette token that tints the accent bar, background and quote mark. Defaults to `accent`. */
9
+ color?: ThemeColor;
10
+ /** Quote text size. Defaults to `md`. */
11
+ size?: BlockquoteSize;
12
+ /** Optional attribution shown below the quote (e.g. an author or source), prefixed with an em dash. */
13
+ cite?: ReactNode;
14
+ /** Show the decorative leading quote mark. Defaults to `false`. */
15
+ quoteMark?: boolean;
16
+ /** The quoted content. */
17
+ children?: ReactNode;
18
+ }
19
+ /**
20
+ * A styled quotation block. Renders a semantic `<blockquote>` with a tinted left accent bar, an
21
+ * optional decorative quote mark, and an optional `cite` attribution line. Tinted by `color` via the
22
+ * shared `--tz-btn-rgb` pattern; `variant` toggles a soft tinted background (`filled`) vs. a bare bar
23
+ * (`plain`). The attribution renders in a `<footer>` with a semantic `<cite>`.
24
+ */
25
+ export declare const Blockquote: import("react").ForwardRefExoticComponent<BlockquoteProps & import("react").RefAttributes<HTMLQuoteElement>>;
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("../../Blockquote-CxHwOecc.cjs");exports.Blockquote=e.Blockquote;exports.default=e.Blockquote;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export { Blockquote, Blockquote as default } from './Blockquote';
2
+ export type { BlockquoteProps, BlockquoteVariant, BlockquoteSize } from './Blockquote';
@@ -0,0 +1,2 @@
1
+ export { Blockquote, Blockquote as default } from './Blockquote';
2
+ export type { BlockquoteProps, BlockquoteVariant, BlockquoteSize } from './Blockquote';
@@ -0,0 +1,6 @@
1
+ import { B as e, B as t } from "../../Blockquote-CNIuJFHq.js";
2
+ export {
3
+ e as Blockquote,
4
+ t as default
5
+ };
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -0,0 +1,21 @@
1
+ import { type HTMLAttributes, type ReactNode } from 'react';
2
+ import type { ThemeColor } from '../../theme';
3
+ export type CodeVariant = 'soft' | 'outlined';
4
+ export type CodeSize = 'sm' | 'md' | 'lg';
5
+ export interface CodeProps extends Omit<HTMLAttributes<HTMLElement>, 'color'> {
6
+ /** Tint style — `soft` (subtle tinted background) or `outlined` (hairline border, transparent). Defaults to `soft`. */
7
+ variant?: CodeVariant;
8
+ /** Theme palette token that tints the text and background. Defaults to `primary`. */
9
+ color?: ThemeColor;
10
+ /** Text size. Defaults to `md`. */
11
+ size?: CodeSize;
12
+ /** The inline code content. */
13
+ children?: ReactNode;
14
+ }
15
+ /**
16
+ * An inline code snippet — a monospace `<code>` element with a soft rounded background, for calling out
17
+ * commands, identifiers or short snippets within body text (e.g. `console.log("Hello")`). Tinted by
18
+ * `color` via the shared `--tz-btn-rgb` pattern; `variant` toggles a subtle tinted fill (`soft`) vs. a
19
+ * hairline border (`outlined`). For a full multi-line, syntax-highlighted block use `CodeBlock` instead.
20
+ */
21
+ export declare const Code: import("react").ForwardRefExoticComponent<CodeProps & import("react").RefAttributes<HTMLElement>>;
@@ -0,0 +1,21 @@
1
+ import { type HTMLAttributes, type ReactNode } from 'react';
2
+ import type { ThemeColor } from '../../theme';
3
+ export type CodeVariant = 'soft' | 'outlined';
4
+ export type CodeSize = 'sm' | 'md' | 'lg';
5
+ export interface CodeProps extends Omit<HTMLAttributes<HTMLElement>, 'color'> {
6
+ /** Tint style — `soft` (subtle tinted background) or `outlined` (hairline border, transparent). Defaults to `soft`. */
7
+ variant?: CodeVariant;
8
+ /** Theme palette token that tints the text and background. Defaults to `primary`. */
9
+ color?: ThemeColor;
10
+ /** Text size. Defaults to `md`. */
11
+ size?: CodeSize;
12
+ /** The inline code content. */
13
+ children?: ReactNode;
14
+ }
15
+ /**
16
+ * An inline code snippet — a monospace `<code>` element with a soft rounded background, for calling out
17
+ * commands, identifiers or short snippets within body text (e.g. `console.log("Hello")`). Tinted by
18
+ * `color` via the shared `--tz-btn-rgb` pattern; `variant` toggles a subtle tinted fill (`soft`) vs. a
19
+ * hairline border (`outlined`). For a full multi-line, syntax-highlighted block use `CodeBlock` instead.
20
+ */
21
+ export declare const Code: import("react").ForwardRefExoticComponent<CodeProps & import("react").RefAttributes<HTMLElement>>;
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("../../Code-CUdpWlY5.cjs");exports.Code=e.Code;exports.default=e.Code;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export { Code, Code as default } from './Code';
2
+ export type { CodeProps, CodeVariant, CodeSize } from './Code';
@@ -0,0 +1,2 @@
1
+ export { Code, Code as default } from './Code';
2
+ export type { CodeProps, CodeVariant, CodeSize } from './Code';
@@ -0,0 +1,6 @@
1
+ import { C as o, C } from "../../Code-DaEpXHfF.js";
2
+ export {
3
+ o as Code,
4
+ C as default
5
+ };
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -4,11 +4,14 @@ export interface CodeBlockProps extends Omit<HTMLAttributes<HTMLDivElement>, 'ch
4
4
  code: string;
5
5
  /** Shiki language id (`'tsx'`, `'json'`, `'bash'`, `'css'`, …). Defaults to `'tsx'`. */
6
6
  language?: string;
7
- /** Header line (e.g. a filename) — adds a header bar above the code with the copy button in it. */
7
+ /**
8
+ * Optional label shown on the left of the header bar (e.g. a filename). The header bar itself always
9
+ * renders (whenever `copyable`), so the copy button lives in it with or without a `title`.
10
+ */
8
11
  title?: ReactNode;
9
- /** Show a copy-to-clipboard button. Defaults to `true`. */
12
+ /** Show a copy-to-clipboard button (in the header bar). Defaults to `true`. */
10
13
  copyable?: boolean;
11
- /** Show line numbers. Defaults to `false`. */
14
+ /** Show line numbers. Defaults to `true`. */
12
15
  showLineNumbers?: boolean;
13
16
  /** Cap the block height in px — taller code scrolls inside. */
14
17
  maxHeight?: number;
@@ -21,9 +24,10 @@ export interface CodeBlockProps extends Omit<HTMLAttributes<HTMLDivElement>, 'ch
21
24
  * is **always dark** (in both app modes) — code reads best on a dark surface, and the deep background
22
25
  * anchors it regardless of the surrounding light/dark theme. Highlighting is **async and lazy**
23
26
  * (`import('shiki')` on first render); until it lands — or if `shiki` isn't installed / the language
24
- * is unknown — the block shows the plain code (on the same dark surface), so nothing breaks. `title`
25
- * adds a header bar (e.g. a filename); `copyable` (default `true`) shows a copy button that flips to a
26
- * tick; `showLineNumbers`, `maxHeight` (scrolls inside) and `wrap` shape the body. The whole block
27
+ * is unknown — the block shows the plain code (on the same dark surface), so nothing breaks. A header
28
+ * bar always renders (whenever `copyable`, default `true`) and holds the copy button which flips to a
29
+ * tick on click with an optional `title` (e.g. a filename) on its left; `showLineNumbers` (default
30
+ * `true`), `maxHeight` (scrolls inside) and `wrap` shape the body. The whole block
27
31
  * keeps VS Code's own dark colors (a deliberate literal-color exception, like the Modal scrim); only
28
32
  * the outer border/radius uses `--tz-*` tokens.
29
33
  */