@razorwind/storybook 0.0.59 → 0.0.61
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/dist/index.cjs +160 -66
- package/dist/index.d.cts +5 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +5 -0
- package/dist/index.mjs +157 -63
- package/package.json +8 -8
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});let e=require("@razorwind/core/plugin"),t=require("@razorwind/core/lib/fonts"),n=require("@razorwind/core/utils"),r=require("@stryke/path/join");function i(e,t={}){let r=t.cssVarPrefix??`rw
|
|
2
|
-
`,`\\n`).replaceAll(`\r`,`\\r`)}function
|
|
1
|
+
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});let e=require("@razorwind/core/plugin"),t=require("@razorwind/core/lib/fonts"),n=require("@razorwind/core/utils"),r=require("@stryke/path/join");function i(e){return e===!0||e===`true`||e===1}function a(e,t){return e?`${e}:${t}`:t}function o(e){let t=e.theme??e.$theme;return typeof t==`string`&&t.length>0?t:void 0}function s(e,t){return e===`palette`||e===`$palette`?typeof t==`boolean`||typeof t==`number`||typeof t==`string`:(e===`theme`||e===`$theme`)&&typeof t==`string`}function c(e){let t=new Map;function r(e,c,l,u,d){if(!(0,n.isObject)(e))return;let f=u||i(e.palette)||i(e.$palette),p=o(e)??d;if((0,n.isTokenLeaf)(e)){(f||p)&&t.set(a(l,c.join(`.`)),{...f&&{palette:!0},...p&&{childTheme:p}});return}for(let[t,n]of Object.entries(e))t.startsWith(`$`)&&t!==`$palette`&&t!==`$theme`||s(t,n)||r(n,[...c,t],l,f,p)}for(let t of(0,n.resolveTokenSets)(e)){let e=t.id==="default"?void 0:t.id;r(t.tokens,[],e,!1,void 0)}return t}function l(e,t={}){let r=t.cssVarPrefix??`rw`,i=c(e);return(0,n.flattenTokens)(e,{includeTypes:t.includeTypes,shouldIncludeToken:e=>e.skipDocs!==!0,enrichToken:e=>{let t=i.get(a(e.theme,e.path));return{...e,cssVar:(0,n.toCssVar)(e.path,r),...e.type===`color`&&t}}})}function u(e){return e.replaceAll(`\\`,`\\\\`).replaceAll(`"`,`\\"`).replaceAll(`
|
|
2
|
+
`,`\\n`).replaceAll(`\r`,`\\r`)}function d(e){return typeof e==`string`?`"${u(e)}"`:typeof e==`number`||typeof e==`boolean`?String(e):e==null?`undefined`:JSON.stringify(e)}function f(e){let t=e.titlePrefix??`Design Tokens`,n=e.themeFiles?.[0],r=e.themeNames??[],i=e.tokenThemeNames??[],a=i.length>1?`
|
|
3
|
+
## Token-doc variants
|
|
4
|
+
|
|
5
|
+
Generated token blocks follow Storybook's \`theme\` global when it matches one
|
|
6
|
+
of these token-set names: ${i.map(e=>`\`${e}\``).join(`, `)}.
|
|
7
|
+
Pass \`theme\` directly to a block to override that selection.
|
|
8
|
+
`:``,o=n?r.length>1?`
|
|
3
9
|
## Storybook UI theme
|
|
4
10
|
|
|
5
11
|
Generated theme module:
|
|
@@ -40,7 +46,7 @@ Generated by \`@razorwind/storybook\`.
|
|
|
40
46
|
- \`${e.outputPath}/Tokens.mdx\` — token overview (${t})
|
|
41
47
|
- \`${e.outputPath}/blocks/\` — React doc blocks (ColorPalette, Typeset, …)
|
|
42
48
|
- \`${e.outputPath}/tokens.json\` — flattened token manifest
|
|
43
|
-
${
|
|
49
|
+
${o}${a}
|
|
44
50
|
## Setup
|
|
45
51
|
|
|
46
52
|
1. Add the generated MDX pages to Storybook (paths depend on your \`stories\` config):
|
|
@@ -65,46 +71,106 @@ import { ColorPaletteBlock } from "./blocks";
|
|
|
65
71
|
## Regenerate
|
|
66
72
|
|
|
67
73
|
Re-run Razorwind generate when tokens change to refresh docs under \`${e.outputPath}/\`.
|
|
68
|
-
`}function
|
|
74
|
+
`}const p=new Set([`fontFamily`,`fontWeight`,`typography`]),m=/font|typography|type|text|line[-_.]?height|letter[-_.]?spacing/i;function h(e){return p.has(e.type??``)?!0:(e.type===`dimension`||e.type===`number`)&&m.test(e.path)}function g(e,t){return e.some(e=>t.some(t=>e.path===t||e.path.startsWith(`${t}.`)))}const _=/^\{([^{}]+)\}$/,v=/^var\((--[^),\s]+)(?:\s*,[^)]*)?\)$/;function y(e){if(typeof e==`string`)return _.exec(e.trim())?.[1]?.trim()}function b(e){if(typeof e==`string`)return v.exec(e.trim())?.[1]}function x(e){let t=new Map(e.map(e=>[e.path,e])),r=new Map(e.map(e=>[(0,n.toThemeCssVar)(e.path),e])),i=e=>{let n=e,i=new Set;for(let a=0;a<8;a++){if(i.has(n.path))return e.cssValue;i.add(n.path);let a=t.get(y(n.value)??``)??r.get(b(n.value)??b(n.cssValue)??``);if(!a||a.path===n.path)return n.cssValue;n=a}return e.cssValue};return e.map(e=>e.type===`color`?{...e,cssValue:i(e)}:e)}function S(e){return Object.keys(e).length>1}function ee(){return`import { useGlobals } from "storybook/preview-api";
|
|
69
75
|
|
|
70
76
|
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* @see https://storybook.js.org/docs/api/doc-blocks/doc-block-colorpalette
|
|
77
|
+
* Select a generated token variant. By default this follows Storybook's
|
|
78
|
+
* \`theme\` global; callers may pass \`theme\` to override it.
|
|
74
79
|
*/
|
|
75
|
-
export function
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
export function useThemeVariant<T extends Record<string, unknown>>(
|
|
81
|
+
variants: T,
|
|
82
|
+
fallback: keyof T & string,
|
|
83
|
+
theme?: string
|
|
84
|
+
): keyof T & string {
|
|
85
|
+
const [globals] = useGlobals();
|
|
86
|
+
const candidate = theme ?? globals.theme;
|
|
87
|
+
|
|
88
|
+
return typeof candidate === "string" && candidate in variants
|
|
89
|
+
? (candidate as keyof T & string)
|
|
90
|
+
: fallback;
|
|
91
|
+
}
|
|
92
|
+
`}function C(e,t){let r=(0,n.resolveTokenSets)(e),i=r.filter(e=>!(0,n.isSharedThemeId)(e.id));return i.length<=1?{default:x(l(e,t))}:Object.fromEntries(i.map(e=>[e.id,x(l(B(r,e),t))]))}function w(e,t){let n=new Map;for(let r of e){let e=r.path.split(`.`).slice(0,Math.max(t,1)).join(`.`)||r.path,i=n.get(e)??[];i.push(r),n.set(e,i)}return n}function T(e,t){return e===t?e.split(`.`).at(-1)??e:e.startsWith(`${t}.`)?e.slice(t.length+1):e.split(`.`).at(-1)??e}function E(e,t={}){let n=t.colorGroupBy??2,r=Object.entries(e),i=r[0]?.[0]??`default`,a=S(e),o=(e,t=` `)=>[...w(e,n).entries()].toSorted(([e],[t])=>e.localeCompare(t)).map(([e,n])=>{let r=n.map(n=>`${t} ${d(T(n.path,e))}: ${d(n.cssValue)}`).join(`,
|
|
93
|
+
`),i=n.find(e=>e.description)?.description??`${n.length} token${n.length===1?``:`s`}`;return`${t}<ColorItem
|
|
94
|
+
title={${d(e)}}
|
|
95
|
+
subtitle={${d(i)}}
|
|
82
96
|
colors={{
|
|
83
|
-
${
|
|
97
|
+
${r}
|
|
84
98
|
}}
|
|
85
99
|
/>`}).join(`
|
|
86
|
-
`)
|
|
87
|
-
|
|
88
|
-
|
|
100
|
+
`),s=e=>{let t=e.filter(e=>e.type===`color`),n=t.filter(e=>e.palette),r=t.filter(e=>!e.palette&&e.childTheme),i=t.filter(e=>!e.palette&&!e.childTheme),a=n.length>0||r.length>0,s=o(t),c=[[`Color palettes`,n],[`Semantic colors`,r],[`Colors`,i]].filter(e=>!!e[1]?.length).map(([e,t])=>` <section>
|
|
101
|
+
<h2>${e}</h2>
|
|
102
|
+
<ColorPalette>
|
|
103
|
+
${o(t,` `)}
|
|
104
|
+
</ColorPalette>
|
|
105
|
+
</section>`).join(`
|
|
106
|
+
`);return a?`<>\n${c}\n </>`:`<ColorPalette>\n${s||` {/* No color tokens */}`}\n </ColorPalette>`},c=r.map(([e,t])=>` ${d(e)}: (\n ${s(t)}\n )`).join(`,
|
|
107
|
+
`);return`import { ColorPalette, ColorItem } from "@storybook/addon-docs/blocks";
|
|
108
|
+
${a?`import { useThemeVariant } from "./ThemeVariant";
|
|
109
|
+
`:``}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Color tokens rendered with Storybook's ColorPalette doc block.
|
|
113
|
+
*
|
|
114
|
+
* @see https://storybook.js.org/docs/api/doc-blocks/doc-block-colorpalette
|
|
115
|
+
*/
|
|
116
|
+
const COLOR_VARIANTS = {
|
|
117
|
+
${c}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export interface ColorPaletteBlockProps {
|
|
121
|
+
/** Generated token-set name. Defaults to Storybook's \`theme\` global. */
|
|
122
|
+
theme?: string;
|
|
89
123
|
}
|
|
90
|
-
|
|
124
|
+
|
|
125
|
+
export function ColorPaletteBlock({ theme }: ColorPaletteBlockProps = {}) {
|
|
126
|
+
const activeTheme = ${a?`useThemeVariant(COLOR_VARIANTS, ${d(i)}, theme)`:d(i)};
|
|
127
|
+
|
|
128
|
+
return COLOR_VARIANTS[activeTheme];
|
|
129
|
+
}
|
|
130
|
+
`}function D(e,n={}){let r=n.sampleText??`The quick brown fox jumps over the lazy dog`,i=S(e),a=Object.entries(e),o=a[0]?.[0]??`default`,s=e=>{let i=e.filter(e=>e.type===`dimension`&&/(?:font|type|text).*size|size.*(?:font|type|text)/i.test(e.path)).map(e=>{let t=/^(\d+(?:\.\d+)?)/.exec(e.cssValue);return t?Number(t[1]):e.cssValue}),a=[...new Set(i)],o=(0,t.pickFontByRole)(n.fonts,t.SANS_ROLES),s=(o?(0,t.cssFontFamily)(o):void 0)??e.find(e=>e.type===`fontFamily`)?.cssValue??`system-ui, sans-serif`,c=e.find(e=>e.type===`fontWeight`),l=c&&Number.parseFloat(c.cssValue)||400,u=a.length>0?`[${a.map(e=>d(e)).join(`, `)}]`:`[12, 14, 16, 20, 24, 32]`;return`<Typeset
|
|
131
|
+
fontFamily={${d(s)}}
|
|
132
|
+
fontSizes={${u}}
|
|
133
|
+
fontWeight={${d(l)}}
|
|
134
|
+
sampleText={${d(r)}}
|
|
135
|
+
/>`},c=a.map(([e,t])=>` ${d(e)}: (${s(t)})`).join(`,
|
|
136
|
+
`);return`import { Typeset } from "@storybook/addon-docs/blocks";
|
|
137
|
+
${i?`import { useThemeVariant } from "./ThemeVariant";
|
|
138
|
+
`:``}
|
|
91
139
|
|
|
92
140
|
/**
|
|
93
141
|
* Typography tokens rendered with Storybook's Typeset doc block.
|
|
94
142
|
*
|
|
95
143
|
* @see https://storybook.js.org/docs/api/doc-blocks/doc-block-typeset
|
|
96
144
|
*/
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
/>
|
|
105
|
-
);
|
|
145
|
+
const TYPESET_VARIANTS = {
|
|
146
|
+
${c}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
export interface TypesetBlockProps {
|
|
150
|
+
/** Generated token-set name. Defaults to Storybook's \`theme\` global. */
|
|
151
|
+
theme?: string;
|
|
106
152
|
}
|
|
107
|
-
|
|
153
|
+
|
|
154
|
+
export function TypesetBlock({ theme }: TypesetBlockProps = {}) {
|
|
155
|
+
const activeTheme = ${i?`useThemeVariant(TYPESET_VARIANTS, ${d(o)}, theme)`:d(o)};
|
|
156
|
+
|
|
157
|
+
return TYPESET_VARIANTS[activeTheme];
|
|
158
|
+
}
|
|
159
|
+
`}function O(e){let t=S(e),n=Object.entries(e),r=n[0]?.[0]??`default`,i=e=>e.map(e=>{let t=e.theme?d(e.theme):`undefined`;return` {
|
|
160
|
+
path: ${d(e.path)},
|
|
161
|
+
type: ${e.type?d(e.type):`undefined`},
|
|
162
|
+
value: ${d(e.cssValue)},
|
|
163
|
+
cssVar: ${d(e.cssVar)},
|
|
164
|
+
description: ${e.description?d(e.description):`undefined`},
|
|
165
|
+
theme: ${t},
|
|
166
|
+
typography: ${h(e)}
|
|
167
|
+
}`}).join(`,
|
|
168
|
+
`),a=n.map(([e,t])=>` ${d(e)}: [
|
|
169
|
+
${i(t)}
|
|
170
|
+
]`).join(`,
|
|
171
|
+
`);return`import type { CSSProperties, ReactElement } from "react";
|
|
172
|
+
${t?`import { useThemeVariant } from "./ThemeVariant";
|
|
173
|
+
`:``}
|
|
108
174
|
|
|
109
175
|
export interface TokenTableRow {
|
|
110
176
|
path: string;
|
|
@@ -113,19 +179,12 @@ export interface TokenTableRow {
|
|
|
113
179
|
cssVar: string;
|
|
114
180
|
description?: string;
|
|
115
181
|
theme?: string;
|
|
182
|
+
typography: boolean;
|
|
116
183
|
}
|
|
117
184
|
|
|
118
|
-
const
|
|
119
|
-
${
|
|
120
|
-
|
|
121
|
-
type: ${e.type?o(e.type):`undefined`},
|
|
122
|
-
value: ${o(e.cssValue)},
|
|
123
|
-
cssVar: ${o(e.cssVar)},
|
|
124
|
-
description: ${e.description?o(e.description):`undefined`},
|
|
125
|
-
theme: ${t}
|
|
126
|
-
}`}).join(`,
|
|
127
|
-
`)||``}
|
|
128
|
-
];
|
|
185
|
+
const TOKEN_VARIANTS: Record<string, TokenTableRow[]> = {
|
|
186
|
+
${a}
|
|
187
|
+
};
|
|
129
188
|
|
|
130
189
|
const tableStyle: CSSProperties = {
|
|
131
190
|
width: "100%",
|
|
@@ -152,10 +211,14 @@ const swatchStyle = (value: string): CSSProperties => ({
|
|
|
152
211
|
});
|
|
153
212
|
|
|
154
213
|
export interface TokenTableBlockProps {
|
|
155
|
-
/** Optional path prefix filter (e.g. \`color\`). */
|
|
156
|
-
filter?: string;
|
|
214
|
+
/** Optional path prefix filter or filters (e.g. \`color\`). */
|
|
215
|
+
filter?: string | string[];
|
|
157
216
|
/** Optional DTCG \`$type\` filter. */
|
|
158
217
|
type?: string;
|
|
218
|
+
/** Restrict rows to tokens used to define typography. */
|
|
219
|
+
typography?: boolean;
|
|
220
|
+
/** Generated token-set name. Defaults to Storybook's \`theme\` global. */
|
|
221
|
+
theme?: string;
|
|
159
222
|
}
|
|
160
223
|
|
|
161
224
|
/**
|
|
@@ -163,15 +226,27 @@ export interface TokenTableBlockProps {
|
|
|
163
226
|
*/
|
|
164
227
|
export function TokenTableBlock({
|
|
165
228
|
filter,
|
|
166
|
-
type
|
|
229
|
+
type,
|
|
230
|
+
typography,
|
|
231
|
+
theme
|
|
167
232
|
}: TokenTableBlockProps = {}): ReactElement {
|
|
168
|
-
const
|
|
169
|
-
|
|
233
|
+
const activeTheme = ${t?`useThemeVariant(TOKEN_VARIANTS, ${d(r)}, theme)`:d(r)};
|
|
234
|
+
const rows = TOKEN_VARIANTS[activeTheme].filter(token => {
|
|
235
|
+
const filters = typeof filter === "string" ? [filter] : filter;
|
|
236
|
+
if (
|
|
237
|
+
filters &&
|
|
238
|
+
!filters.some(prefix =>
|
|
239
|
+
token.path === prefix || token.path.startsWith(\`\${prefix}.\`)
|
|
240
|
+
)
|
|
241
|
+
) {
|
|
170
242
|
return false;
|
|
171
243
|
}
|
|
172
244
|
if (type && token.type !== type) {
|
|
173
245
|
return false;
|
|
174
246
|
}
|
|
247
|
+
if (typography && !token.typography) {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
175
250
|
return true;
|
|
176
251
|
});
|
|
177
252
|
|
|
@@ -208,7 +283,7 @@ export function TokenTableBlock({
|
|
|
208
283
|
</table>
|
|
209
284
|
);
|
|
210
285
|
}
|
|
211
|
-
`}function
|
|
286
|
+
`}function k(e){let t=e.titlePrefix??`Design Tokens`,n=e.hasColors?`
|
|
212
287
|
## Colors
|
|
213
288
|
|
|
214
289
|
<ColorPaletteBlock />
|
|
@@ -216,37 +291,53 @@ export function TokenTableBlock({
|
|
|
216
291
|
## Typography
|
|
217
292
|
|
|
218
293
|
<TypesetBlock />
|
|
294
|
+
`:``,i=e.hasSizes?`
|
|
295
|
+
## Size
|
|
296
|
+
|
|
297
|
+
<TokenTableBlock filter="size" />
|
|
298
|
+
`:``,a=e.hasSpaces?`
|
|
299
|
+
## Space
|
|
300
|
+
|
|
301
|
+
<TokenTableBlock filter={[
|
|
302
|
+
"space",
|
|
303
|
+
"spacing"
|
|
304
|
+
]} />
|
|
219
305
|
`:``;return`import { Meta } from "@storybook/addon-docs/blocks";
|
|
220
306
|
import { ColorPaletteBlock } from "./blocks/ColorPalette";
|
|
221
307
|
import { TokenTableBlock } from "./blocks/TokenTable";
|
|
222
308
|
import { TypesetBlock } from "./blocks/Typeset";
|
|
223
309
|
|
|
224
|
-
<Meta title="${
|
|
310
|
+
<Meta title="${u(t)}/Overview" />
|
|
225
311
|
|
|
226
312
|
# ${t}
|
|
227
313
|
|
|
228
314
|
Design tokens generated by \`@razorwind/storybook\` for Storybook MDX docs.
|
|
229
|
-
${n}${r}
|
|
315
|
+
${n}${r}${i}${a}
|
|
230
316
|
## All tokens
|
|
231
317
|
|
|
232
318
|
<TokenTableBlock />
|
|
233
|
-
`}function
|
|
319
|
+
`}function A(e={}){return`import { Meta } from "@storybook/addon-docs/blocks";
|
|
234
320
|
import { ColorPaletteBlock } from "./blocks/ColorPalette";
|
|
235
321
|
|
|
236
|
-
<Meta title="${
|
|
322
|
+
<Meta title="${u(e.titlePrefix??`Design Tokens`)}/Colors" />
|
|
237
323
|
|
|
238
324
|
# Colors
|
|
239
325
|
|
|
240
326
|
<ColorPaletteBlock />
|
|
241
|
-
`}function
|
|
327
|
+
`}function j(e={}){return`import { Meta } from "@storybook/addon-docs/blocks";
|
|
242
328
|
import { TypesetBlock } from "./blocks/Typeset";
|
|
329
|
+
import { TokenTableBlock } from "./blocks/TokenTable";
|
|
243
330
|
|
|
244
|
-
<Meta title="${
|
|
331
|
+
<Meta title="${u(e.titlePrefix??`Design Tokens`)}/Typography" />
|
|
245
332
|
|
|
246
333
|
# Typography
|
|
247
334
|
|
|
248
335
|
<TypesetBlock />
|
|
249
|
-
|
|
336
|
+
|
|
337
|
+
## Typography tokens
|
|
338
|
+
|
|
339
|
+
<TokenTableBlock typography />
|
|
340
|
+
`}function M(e,t){let n=e[t];return typeof n==`string`?n:void 0}function N(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function te(e){return`import { IconGallery, IconItem } from "@storybook/addon-docs/blocks";
|
|
250
341
|
|
|
251
342
|
/**
|
|
252
343
|
* Icons rendered with Storybook's IconGallery doc block.
|
|
@@ -256,39 +347,42 @@ import { TypesetBlock } from "./blocks/Typeset";
|
|
|
256
347
|
export function IconGalleryBlock() {
|
|
257
348
|
return (
|
|
258
349
|
<IconGallery>
|
|
259
|
-
${(
|
|
350
|
+
${(N(e)?Object.values(e).filter(N).toSorted((e,t)=>(M(e,`name`)??``).localeCompare(M(t,`name`)??``)):[]).map(e=>{let t=M(e,`name`)??`unknown`,n=(Array.isArray(e.files)?e.files:[]).find(e=>N(e)&&M(e,`type`)===`svg`&&typeof e.content==`string`),r=N(n)&&typeof n.content==`string`?`<span
|
|
260
351
|
style={{ display: "inline-flex", width: 24, height: 24 }}
|
|
261
|
-
dangerouslySetInnerHTML={{ __html: ${
|
|
262
|
-
/>`:`<code>${
|
|
352
|
+
dangerouslySetInnerHTML={{ __html: ${d(n.content)} }}
|
|
353
|
+
/>`:`<code>${u(t)}</code>`;return` <IconItem name={${d(t)}}>
|
|
263
354
|
${r}
|
|
264
355
|
</IconItem>`}).join(`
|
|
265
356
|
`)||` {/* No icons */}`}
|
|
266
357
|
</IconGallery>
|
|
267
358
|
);
|
|
268
359
|
}
|
|
269
|
-
`}function
|
|
360
|
+
`}function ne(e={}){return`import { Meta } from "@storybook/addon-docs/blocks";
|
|
270
361
|
import { IconGalleryBlock } from "./blocks/IconGallery";
|
|
271
362
|
|
|
272
|
-
<Meta title="${
|
|
363
|
+
<Meta title="${u(e.titlePrefix??`Design Tokens`)}/Icons" />
|
|
273
364
|
|
|
274
365
|
# Icons
|
|
275
366
|
|
|
276
367
|
<IconGalleryBlock />
|
|
277
|
-
`}function
|
|
278
|
-
export {
|
|
368
|
+
`}function re(e=!0){return`export { ColorPaletteBlock } from "./ColorPalette";
|
|
369
|
+
export type { ColorPaletteBlockProps } from "./ColorPalette";
|
|
370
|
+
${e?`export { IconGalleryBlock } from "./IconGallery";
|
|
371
|
+
`:``}
|
|
279
372
|
export { TokenTableBlock } from "./TokenTable";
|
|
280
373
|
export type { TokenTableBlockProps, TokenTableRow } from "./TokenTable";
|
|
281
374
|
export { TypesetBlock } from "./Typeset";
|
|
282
|
-
|
|
283
|
-
`)}
|
|
375
|
+
export type { TypesetBlockProps } from "./Typeset";
|
|
376
|
+
`}const P=new Set(`base.colorPrimary.colorSecondary.appBg.appContentBg.appHoverBg.appPreviewBg.appBorderColor.appBorderRadius.fontBase.fontCode.textColor.textInverseColor.textMutedColor.barTextColor.barHoverColor.barSelectedColor.barBg.buttonBg.buttonBorder.booleanBg.booleanSelectedBg.inputBg.inputBorder.inputTextColor.inputBorderRadius.brandTitle.brandUrl.brandImage.brandTarget.gridCellSize`.split(`.`));function F(e){return N(e)?e.base===`light`||e.base===`dark`:!1}function I(e){return N(e)?F(e)?!0:Object.keys(e).some(e=>P.has(e)):!1}function L(e){if(!N(e)||I(e))return!1;let t=Object.values(e);return t.length>0&&t.every(I)}function R(e,t,n){return t.base===`light`||t.base===`dark`?t.base:`${e} ${n??``}`.toLowerCase().includes(`light`)?`light`:`dark`}function z(e){return/^[a-z_$][\w$]*$/i.test(e)?e:d(e)}function B(e,t){let r=e.find(e=>e.id===n.SHARED_THEME_ID);return!r||(0,n.isSharedThemeId)(t.id)?t.tokens:(0,n.mergeTokenTrees)(t.tokens,r.tokens)}function V(e){return N(e)&&`$value`in e&&!(`colorSpace`in e)&&!(`hex`in e)?e.$value:e}function H(e,t){let n=V(e),r=new Set;for(let e=0;e<8;e++){let e=y(n);if(!e||r.has(e))break;r.add(e);let i=t.get(e);if(!i)break;n=i.value}return n}function U(e){return e==null||typeof e==`string`||typeof e==`number`||typeof e==`boolean`?e:(0,n.formatTokenValue)(e)}function W(e,t){let r=(0,n.resolveTokenSets)(e),i=r.find(e=>e.id===t)??(r.length===1?r[0]:void 0),a=i?B(r,i):e,o=new Map;for(let e of l(a))(!o.get(e.path)||e.theme===t)&&o.set(e.path,e);return o}function G(e,t){let n={...e};for(let[r,i]of Object.entries(e))r!==`base`&&i!==void 0&&(n[r]=U(H(i,t)));return n}function K(e,t,n,r,i){let a=G(t,W(i.tokens,e));return q(R(e,a,i.theme),a,n,r)}function q(e,n,r,i){let a=(0,t.pickFontByRole)(i,t.SANS_ROLES),o=(0,t.pickFontByRole)(i,t.MONO_ROLES);return{brandTarget:`_blank`,base:e||`light`,...n,brandTitle:n.brandTitle??r.title,brandUrl:n.brandUrl??r.homepage,brandImage:n.brandImage??r.logo,fontBase:n.fontBase??(a?(0,t.cssFontFamily)(a):void 0),fontCode:n.fontCode??(o?(0,t.cssFontFamily)(o):void 0)}}function J(e,t=``){return`create({\n${Object.entries(e).filter(([,e])=>e!==void 0).map(([e,n])=>`${t} ${e}: ${d(n)}`).join(`,
|
|
377
|
+
`)}\n${t}})`}function Y(e){return`import { create } from "storybook/theming";
|
|
284
378
|
|
|
285
379
|
/**
|
|
286
380
|
* Storybook UI theme generated by \`@razorwind/storybook\`.
|
|
287
381
|
*
|
|
288
382
|
* @see https://storybook.js.org/docs/configure/user-interface/theming
|
|
289
383
|
*/
|
|
290
|
-
export default ${
|
|
291
|
-
`}function
|
|
384
|
+
export default ${J(e)};
|
|
385
|
+
`}function X(e){return`import { create } from "storybook/theming";
|
|
292
386
|
|
|
293
387
|
/**
|
|
294
388
|
* Storybook UI themes generated by \`@razorwind/storybook\`.
|
|
@@ -296,7 +390,7 @@ export default ${L(e)};
|
|
|
296
390
|
* @see https://storybook.js.org/docs/configure/user-interface/theming
|
|
297
391
|
*/
|
|
298
392
|
export default {
|
|
299
|
-
${Object.entries(e).map(([e,t])=>` ${
|
|
393
|
+
${Object.entries(e).map(([e,t])=>` ${z(e)}: ${J(t,` `)}`).join(`,
|
|
300
394
|
`)}
|
|
301
395
|
};
|
|
302
|
-
`}function
|
|
396
|
+
`}function Z(e){if(F(e))return Y(e);let t=Object.entries(e);if(t.length!==1)return X(e);let[,n]=t[0]??[];return n?Y(n):X(e)}function Q(e,t,r,i){let a=(e,n)=>K(e,n,r,t.fonts,t);if(L(e))return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,a(e,t)]));let o=(0,n.resolveTokenSets)(t.tokens),s=o.filter(e=>!(0,n.isSharedThemeId)(e.id));if(s.length>1){let e={};for(let t of s){let n=i(B(o,t));if(L(n)){let r=n[t.id];if(r){e[t.id]=a(t.id,r);continue}for(let[t,r]of Object.entries(n))e[t]=a(t,r);continue}I(n)&&(e[t.id]=a(t.id,n))}return e}if(I(e)){let n=s[0]&&s[0].id!=="default"?s[0].id:t.theme??`default`;return{[n]:a(n,e)}}return{}}const ie=e=>(t,i,a)=>(0,n.createDocument)((0,r.joinPaths)(e,t),i,{name:`razorwind-storybook`},!1,a);function $(e,t={}){let i=t.outputPath??`storybook/tokens`,a=(0,n.resolveSchemaIdentity)(e),o=t.titlePrefix??a.title??`Design Tokens`,s={...t,titlePrefix:o},c=l(e.tokens,t),u=C(e.tokens,t),d=c.some(e=>e.type===`color`),p=e.fonts&&Object.keys(e.fonts).length>0||c.some(h),m=g(c,[`size`]),_=g(c,[`space`,`spacing`]),v=!t.skipIcons&&N(e.icons)&&Object.keys(e.icons).length>0,y=ie(i),b={[(0,r.joinPaths)(i,`blocks/ColorPalette.tsx`)]:y(`blocks/ColorPalette.tsx`,E(u,s),`tsx`),[(0,r.joinPaths)(i,`blocks/Typeset.tsx`)]:y(`blocks/Typeset.tsx`,D(u,{...s,fonts:e.fonts}),`tsx`),[(0,r.joinPaths)(i,`blocks/TokenTable.tsx`)]:y(`blocks/TokenTable.tsx`,O(u),`tsx`),[(0,r.joinPaths)(i,`blocks/index.ts`)]:y(`blocks/index.ts`,re(v),`typescript`),[(0,r.joinPaths)(i,`Tokens.mdx`)]:y(`Tokens.mdx`,k({titlePrefix:o,hasColors:d,hasTypography:p,hasSizes:m,hasSpaces:_}),`mdx`)};v&&(b[(0,r.joinPaths)(i,`blocks/IconGallery.tsx`)]=y(`blocks/IconGallery.tsx`,te(e.icons),`tsx`)),S(u)&&(b[(0,r.joinPaths)(i,`blocks/ThemeVariant.ts`)]=y(`blocks/ThemeVariant.ts`,ee(),`typescript`)),d&&(b[(0,r.joinPaths)(i,`Colors.mdx`)]=y(`Colors.mdx`,A(s),`mdx`)),p&&(b[(0,r.joinPaths)(i,`Typography.mdx`)]=y(`Typography.mdx`,j(s),`mdx`)),v&&(b[(0,r.joinPaths)(i,`Icons.mdx`)]=y(`Icons.mdx`,ne(s),`mdx`)),b[(0,r.joinPaths)(i,`tokens.json`)]=y(`tokens.json`,`${JSON.stringify(c,null,2)}\n`,`json`);let x;if(t.mapTheme){let n=Q(t.mapTheme(e.tokens),e,a,t.mapTheme);Object.keys(n).length>0&&(x=Object.keys(n),b[(0,r.joinPaths)(i,`theme.ts`)]=y(`theme.ts`,Z(n),`typescript`))}let w=`INSTALL.md`;return b[(0,r.joinPaths)(i,w)]=y(w,t.installGuide??f({outputPath:i,titlePrefix:o,themeFiles:x?[`theme.ts`]:void 0,themeNames:x,tokenThemeNames:Object.keys(u)}),`markdown`),b}var ae=(0,e.definePlugin)(e=>({name:`storybook`,themeGeneration:`combined`,generate:async t=>$(t,e??{})}));exports.applyBrandDefaults=q,exports.default=ae,exports.flattenTokens=l,Object.defineProperty(exports,"formatTokenValue",{enumerable:!0,get:function(){return n.formatTokenValue}}),exports.generateTokenDocs=$,exports.normalizeThemes=Q,exports.renderInstallMd=f,exports.renderThemeFile=Z,exports.resolveTokenSets=n.resolveTokenSets,Object.defineProperty(exports,"toCssVar",{enumerable:!0,get:function(){return n.toCssVar}});
|
package/dist/index.d.cts
CHANGED
|
@@ -22,6 +22,10 @@ interface FlatToken {
|
|
|
22
22
|
description?: string;
|
|
23
23
|
/** Theme / set id when tokens are a `Record<string, Tokens>`. */
|
|
24
24
|
theme?: string;
|
|
25
|
+
/** True when this color belongs to an ancestor group marked `palette: true`. */
|
|
26
|
+
palette?: boolean;
|
|
27
|
+
/** Semantic child-theme name from a token or ancestor `theme` property. */
|
|
28
|
+
childTheme?: string;
|
|
25
29
|
}
|
|
26
30
|
/**
|
|
27
31
|
* Storybook theme variables passed to `create()` from `storybook/theming`.
|
|
@@ -153,6 +157,7 @@ declare function renderInstallMd(options: {
|
|
|
153
157
|
titlePrefix?: string;
|
|
154
158
|
themeFiles?: string[];
|
|
155
159
|
themeNames?: string[];
|
|
160
|
+
tokenThemeNames?: string[];
|
|
156
161
|
}): string;
|
|
157
162
|
//#endregion
|
|
158
163
|
//#region src/generate.d.ts
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/flatten.ts","../src/install.ts","../src/generate.ts","../src/index.ts"],"mappings":";;;;;;;;;UAwBiB;;EAEf;;EAEA,OAAO;;EAEP;;EAEA;;EAEA;;EAEA;;EAEA;;;;;;;UAQe;;EAEf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;KAGU,wBAAwB,YAAY;KAEpC,uBACV,wBAAwB,eAAe;;;;;;KAO7B,0BACV,QAAQ,SAAS,eAAe,YAC7B;;;;UAKY;;;;;;EAMf;;;;;;EAOA;;;;;;EAOA;;;;;;EAOA;;;;;EAMA,eAAe;;;;;;EAOf;;;;;;;;;;;;;;EAeA,WAAW;;;;;;EAOX;;;;;EAMA;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/flatten.ts","../src/install.ts","../src/generate.ts","../src/index.ts"],"mappings":";;;;;;;;;UAwBiB;;EAEf;;EAEA,OAAO;;EAEP;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;;;;;;UAQe;;EAEf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;KAGU,wBAAwB,YAAY;KAEpC,uBACV,wBAAwB,eAAe;;;;;;KAO7B,0BACV,QAAQ,SAAS,eAAe,YAC7B;;;;UAKY;;;;;;EAMf;;;;;;EAOA;;;;;;EAOA;;;;;;EAOA;;;;;EAMA,eAAe;;;;;;EAOf;;;;;;;;;;;;;;EAeA,WAAW;;;;;;EAOX;;;;;EAMA;;;;;;;iBC3Dc,cACd,QAAQ,WAAS,eAAe,WAChC,UAAS,KAAK,2DACb;;;;;;;;iBC3Fa,gBAAgB;EAC9B;EACA;EACA;EACA;EACA;;;;;;;iBC86Bc,mBACd,wBACA,OAAO,YAAY,yBACnB;EAAY;EAAgB;EAAmB;GAC/C,QAAQ,QACP;;;;;;;;;iBAkEa,gBACd,OAAO,iBAAiB,eAAe;;;;;;iBAwBzB,gBACd,iBACA,MAAM,KAAK,uCACX;EAAY;EAAgB;EAAmB;GAC/C,UAAU,YAAY,sCACrB,eAAe;;;;iBAwEF,kBACd,MAAM,QACN,UAAS,yBACR,wBAAwB,QAAQ"}
|
package/dist/index.d.mts
CHANGED
|
@@ -22,6 +22,10 @@ interface FlatToken {
|
|
|
22
22
|
description?: string;
|
|
23
23
|
/** Theme / set id when tokens are a `Record<string, Tokens>`. */
|
|
24
24
|
theme?: string;
|
|
25
|
+
/** True when this color belongs to an ancestor group marked `palette: true`. */
|
|
26
|
+
palette?: boolean;
|
|
27
|
+
/** Semantic child-theme name from a token or ancestor `theme` property. */
|
|
28
|
+
childTheme?: string;
|
|
25
29
|
}
|
|
26
30
|
/**
|
|
27
31
|
* Storybook theme variables passed to `create()` from `storybook/theming`.
|
|
@@ -153,6 +157,7 @@ declare function renderInstallMd(options: {
|
|
|
153
157
|
titlePrefix?: string;
|
|
154
158
|
themeFiles?: string[];
|
|
155
159
|
themeNames?: string[];
|
|
160
|
+
tokenThemeNames?: string[];
|
|
156
161
|
}): string;
|
|
157
162
|
//#endregion
|
|
158
163
|
//#region src/generate.d.ts
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import{definePlugin as e}from"@razorwind/core/plugin";import{MONO_ROLES as t,SANS_ROLES as n,cssFontFamily as r,pickFontByRole as i}from"@razorwind/core/lib/fonts";import{SHARED_THEME_ID as a,createDocument as o,flattenTokens as s,formatTokenValue as c,
|
|
2
|
-
`,`\\n`).replaceAll(`\r`,`\\r`)}function
|
|
1
|
+
import{definePlugin as e}from"@razorwind/core/plugin";import{MONO_ROLES as t,SANS_ROLES as n,cssFontFamily as r,pickFontByRole as i}from"@razorwind/core/lib/fonts";import{SHARED_THEME_ID as a,createDocument as o,flattenTokens as s,formatTokenValue as c,isObject as l,isSharedThemeId as u,isTokenLeaf as d,mergeTokenTrees as f,resolveSchemaIdentity as ee,resolveTokenSets as p,toCssVar as m,toCssVar as h,toThemeCssVar as g}from"@razorwind/core/utils";import{joinPaths as _}from"@stryke/path/join";function v(e){return e===!0||e===`true`||e===1}function y(e,t){return e?`${e}:${t}`:t}function te(e){let t=e.theme??e.$theme;return typeof t==`string`&&t.length>0?t:void 0}function b(e,t){return e===`palette`||e===`$palette`?typeof t==`boolean`||typeof t==`number`||typeof t==`string`:(e===`theme`||e===`$theme`)&&typeof t==`string`}function x(e){let t=new Map;function n(e,r,i,a,o){if(!l(e))return;let s=a||v(e.palette)||v(e.$palette),c=te(e)??o;if(d(e)){(s||c)&&t.set(y(i,r.join(`.`)),{...s&&{palette:!0},...c&&{childTheme:c}});return}for(let[t,a]of Object.entries(e))t.startsWith(`$`)&&t!==`$palette`&&t!==`$theme`||b(t,a)||n(a,[...r,t],i,s,c)}for(let t of p(e)){let e=t.id==="default"?void 0:t.id;n(t.tokens,[],e,!1,void 0)}return t}function S(e,t={}){let n=t.cssVarPrefix??`rw`,r=x(e);return s(e,{includeTypes:t.includeTypes,shouldIncludeToken:e=>e.skipDocs!==!0,enrichToken:e=>{let t=r.get(y(e.theme,e.path));return{...e,cssVar:h(e.path,n),...e.type===`color`&&t}}})}function C(e){return e.replaceAll(`\\`,`\\\\`).replaceAll(`"`,`\\"`).replaceAll(`
|
|
2
|
+
`,`\\n`).replaceAll(`\r`,`\\r`)}function w(e){return typeof e==`string`?`"${C(e)}"`:typeof e==`number`||typeof e==`boolean`?String(e):e==null?`undefined`:JSON.stringify(e)}function T(e){let t=e.titlePrefix??`Design Tokens`,n=e.themeFiles?.[0],r=e.themeNames??[],i=e.tokenThemeNames??[],a=i.length>1?`
|
|
3
|
+
## Token-doc variants
|
|
4
|
+
|
|
5
|
+
Generated token blocks follow Storybook's \`theme\` global when it matches one
|
|
6
|
+
of these token-set names: ${i.map(e=>`\`${e}\``).join(`, `)}.
|
|
7
|
+
Pass \`theme\` directly to a block to override that selection.
|
|
8
|
+
`:``,o=n?r.length>1?`
|
|
3
9
|
## Storybook UI theme
|
|
4
10
|
|
|
5
11
|
Generated theme module:
|
|
@@ -40,7 +46,7 @@ Generated by \`@razorwind/storybook\`.
|
|
|
40
46
|
- \`${e.outputPath}/Tokens.mdx\` — token overview (${t})
|
|
41
47
|
- \`${e.outputPath}/blocks/\` — React doc blocks (ColorPalette, Typeset, …)
|
|
42
48
|
- \`${e.outputPath}/tokens.json\` — flattened token manifest
|
|
43
|
-
${
|
|
49
|
+
${o}${a}
|
|
44
50
|
## Setup
|
|
45
51
|
|
|
46
52
|
1. Add the generated MDX pages to Storybook (paths depend on your \`stories\` config):
|
|
@@ -65,46 +71,106 @@ import { ColorPaletteBlock } from "./blocks";
|
|
|
65
71
|
## Regenerate
|
|
66
72
|
|
|
67
73
|
Re-run Razorwind generate when tokens change to refresh docs under \`${e.outputPath}/\`.
|
|
68
|
-
`}
|
|
74
|
+
`}const E=new Set([`fontFamily`,`fontWeight`,`typography`]),ne=/font|typography|type|text|line[-_.]?height|letter[-_.]?spacing/i;function D(e){return E.has(e.type??``)?!0:(e.type===`dimension`||e.type===`number`)&&ne.test(e.path)}function O(e,t){return e.some(e=>t.some(t=>e.path===t||e.path.startsWith(`${t}.`)))}const re=/^\{([^{}]+)\}$/,ie=/^var\((--[^),\s]+)(?:\s*,[^)]*)?\)$/;function k(e){if(typeof e==`string`)return re.exec(e.trim())?.[1]?.trim()}function A(e){if(typeof e==`string`)return ie.exec(e.trim())?.[1]}function j(e){let t=new Map(e.map(e=>[e.path,e])),n=new Map(e.map(e=>[g(e.path),e])),r=e=>{let r=e,i=new Set;for(let a=0;a<8;a++){if(i.has(r.path))return e.cssValue;i.add(r.path);let a=t.get(k(r.value)??``)??n.get(A(r.value)??A(r.cssValue)??``);if(!a||a.path===r.path)return r.cssValue;r=a}return e.cssValue};return e.map(e=>e.type===`color`?{...e,cssValue:r(e)}:e)}function M(e){return Object.keys(e).length>1}function ae(){return`import { useGlobals } from "storybook/preview-api";
|
|
69
75
|
|
|
70
76
|
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* @see https://storybook.js.org/docs/api/doc-blocks/doc-block-colorpalette
|
|
77
|
+
* Select a generated token variant. By default this follows Storybook's
|
|
78
|
+
* \`theme\` global; callers may pass \`theme\` to override it.
|
|
74
79
|
*/
|
|
75
|
-
export function
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
export function useThemeVariant<T extends Record<string, unknown>>(
|
|
81
|
+
variants: T,
|
|
82
|
+
fallback: keyof T & string,
|
|
83
|
+
theme?: string
|
|
84
|
+
): keyof T & string {
|
|
85
|
+
const [globals] = useGlobals();
|
|
86
|
+
const candidate = theme ?? globals.theme;
|
|
87
|
+
|
|
88
|
+
return typeof candidate === "string" && candidate in variants
|
|
89
|
+
? (candidate as keyof T & string)
|
|
90
|
+
: fallback;
|
|
91
|
+
}
|
|
92
|
+
`}function oe(e,t){let n=p(e),r=n.filter(e=>!u(e.id));return r.length<=1?{default:j(S(e,t))}:Object.fromEntries(r.map(e=>[e.id,j(S(K(n,e),t))]))}function se(e,t){let n=new Map;for(let r of e){let e=r.path.split(`.`).slice(0,Math.max(t,1)).join(`.`)||r.path,i=n.get(e)??[];i.push(r),n.set(e,i)}return n}function ce(e,t){return e===t?e.split(`.`).at(-1)??e:e.startsWith(`${t}.`)?e.slice(t.length+1):e.split(`.`).at(-1)??e}function le(e,t={}){let n=t.colorGroupBy??2,r=Object.entries(e),i=r[0]?.[0]??`default`,a=M(e),o=(e,t=` `)=>[...se(e,n).entries()].toSorted(([e],[t])=>e.localeCompare(t)).map(([e,n])=>{let r=n.map(n=>`${t} ${w(ce(n.path,e))}: ${w(n.cssValue)}`).join(`,
|
|
93
|
+
`),i=n.find(e=>e.description)?.description??`${n.length} token${n.length===1?``:`s`}`;return`${t}<ColorItem
|
|
94
|
+
title={${w(e)}}
|
|
95
|
+
subtitle={${w(i)}}
|
|
82
96
|
colors={{
|
|
83
|
-
${
|
|
97
|
+
${r}
|
|
84
98
|
}}
|
|
85
99
|
/>`}).join(`
|
|
86
|
-
`)
|
|
87
|
-
|
|
88
|
-
|
|
100
|
+
`),s=e=>{let t=e.filter(e=>e.type===`color`),n=t.filter(e=>e.palette),r=t.filter(e=>!e.palette&&e.childTheme),i=t.filter(e=>!e.palette&&!e.childTheme),a=n.length>0||r.length>0,s=o(t),c=[[`Color palettes`,n],[`Semantic colors`,r],[`Colors`,i]].filter(e=>!!e[1]?.length).map(([e,t])=>` <section>
|
|
101
|
+
<h2>${e}</h2>
|
|
102
|
+
<ColorPalette>
|
|
103
|
+
${o(t,` `)}
|
|
104
|
+
</ColorPalette>
|
|
105
|
+
</section>`).join(`
|
|
106
|
+
`);return a?`<>\n${c}\n </>`:`<ColorPalette>\n${s||` {/* No color tokens */}`}\n </ColorPalette>`},c=r.map(([e,t])=>` ${w(e)}: (\n ${s(t)}\n )`).join(`,
|
|
107
|
+
`);return`import { ColorPalette, ColorItem } from "@storybook/addon-docs/blocks";
|
|
108
|
+
${a?`import { useThemeVariant } from "./ThemeVariant";
|
|
109
|
+
`:``}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Color tokens rendered with Storybook's ColorPalette doc block.
|
|
113
|
+
*
|
|
114
|
+
* @see https://storybook.js.org/docs/api/doc-blocks/doc-block-colorpalette
|
|
115
|
+
*/
|
|
116
|
+
const COLOR_VARIANTS = {
|
|
117
|
+
${c}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export interface ColorPaletteBlockProps {
|
|
121
|
+
/** Generated token-set name. Defaults to Storybook's \`theme\` global. */
|
|
122
|
+
theme?: string;
|
|
89
123
|
}
|
|
90
|
-
|
|
124
|
+
|
|
125
|
+
export function ColorPaletteBlock({ theme }: ColorPaletteBlockProps = {}) {
|
|
126
|
+
const activeTheme = ${a?`useThemeVariant(COLOR_VARIANTS, ${w(i)}, theme)`:w(i)};
|
|
127
|
+
|
|
128
|
+
return COLOR_VARIANTS[activeTheme];
|
|
129
|
+
}
|
|
130
|
+
`}function ue(e,t={}){let a=t.sampleText??`The quick brown fox jumps over the lazy dog`,o=M(e),s=Object.entries(e),c=s[0]?.[0]??`default`,l=e=>{let o=e.filter(e=>e.type===`dimension`&&/(?:font|type|text).*size|size.*(?:font|type|text)/i.test(e.path)).map(e=>{let t=/^(\d+(?:\.\d+)?)/.exec(e.cssValue);return t?Number(t[1]):e.cssValue}),s=[...new Set(o)],c=i(t.fonts,n),l=(c?r(c):void 0)??e.find(e=>e.type===`fontFamily`)?.cssValue??`system-ui, sans-serif`,u=e.find(e=>e.type===`fontWeight`),d=u&&Number.parseFloat(u.cssValue)||400,f=s.length>0?`[${s.map(e=>w(e)).join(`, `)}]`:`[12, 14, 16, 20, 24, 32]`;return`<Typeset
|
|
131
|
+
fontFamily={${w(l)}}
|
|
132
|
+
fontSizes={${f}}
|
|
133
|
+
fontWeight={${w(d)}}
|
|
134
|
+
sampleText={${w(a)}}
|
|
135
|
+
/>`},u=s.map(([e,t])=>` ${w(e)}: (${l(t)})`).join(`,
|
|
136
|
+
`);return`import { Typeset } from "@storybook/addon-docs/blocks";
|
|
137
|
+
${o?`import { useThemeVariant } from "./ThemeVariant";
|
|
138
|
+
`:``}
|
|
91
139
|
|
|
92
140
|
/**
|
|
93
141
|
* Typography tokens rendered with Storybook's Typeset doc block.
|
|
94
142
|
*
|
|
95
143
|
* @see https://storybook.js.org/docs/api/doc-blocks/doc-block-typeset
|
|
96
144
|
*/
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
/>
|
|
105
|
-
);
|
|
145
|
+
const TYPESET_VARIANTS = {
|
|
146
|
+
${u}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
export interface TypesetBlockProps {
|
|
150
|
+
/** Generated token-set name. Defaults to Storybook's \`theme\` global. */
|
|
151
|
+
theme?: string;
|
|
106
152
|
}
|
|
107
|
-
|
|
153
|
+
|
|
154
|
+
export function TypesetBlock({ theme }: TypesetBlockProps = {}) {
|
|
155
|
+
const activeTheme = ${o?`useThemeVariant(TYPESET_VARIANTS, ${w(c)}, theme)`:w(c)};
|
|
156
|
+
|
|
157
|
+
return TYPESET_VARIANTS[activeTheme];
|
|
158
|
+
}
|
|
159
|
+
`}function N(e){let t=M(e),n=Object.entries(e),r=n[0]?.[0]??`default`,i=e=>e.map(e=>{let t=e.theme?w(e.theme):`undefined`;return` {
|
|
160
|
+
path: ${w(e.path)},
|
|
161
|
+
type: ${e.type?w(e.type):`undefined`},
|
|
162
|
+
value: ${w(e.cssValue)},
|
|
163
|
+
cssVar: ${w(e.cssVar)},
|
|
164
|
+
description: ${e.description?w(e.description):`undefined`},
|
|
165
|
+
theme: ${t},
|
|
166
|
+
typography: ${D(e)}
|
|
167
|
+
}`}).join(`,
|
|
168
|
+
`),a=n.map(([e,t])=>` ${w(e)}: [
|
|
169
|
+
${i(t)}
|
|
170
|
+
]`).join(`,
|
|
171
|
+
`);return`import type { CSSProperties, ReactElement } from "react";
|
|
172
|
+
${t?`import { useThemeVariant } from "./ThemeVariant";
|
|
173
|
+
`:``}
|
|
108
174
|
|
|
109
175
|
export interface TokenTableRow {
|
|
110
176
|
path: string;
|
|
@@ -113,19 +179,12 @@ export interface TokenTableRow {
|
|
|
113
179
|
cssVar: string;
|
|
114
180
|
description?: string;
|
|
115
181
|
theme?: string;
|
|
182
|
+
typography: boolean;
|
|
116
183
|
}
|
|
117
184
|
|
|
118
|
-
const
|
|
119
|
-
${
|
|
120
|
-
|
|
121
|
-
type: ${e.type?v(e.type):`undefined`},
|
|
122
|
-
value: ${v(e.cssValue)},
|
|
123
|
-
cssVar: ${v(e.cssVar)},
|
|
124
|
-
description: ${e.description?v(e.description):`undefined`},
|
|
125
|
-
theme: ${t}
|
|
126
|
-
}`}).join(`,
|
|
127
|
-
`)||``}
|
|
128
|
-
];
|
|
185
|
+
const TOKEN_VARIANTS: Record<string, TokenTableRow[]> = {
|
|
186
|
+
${a}
|
|
187
|
+
};
|
|
129
188
|
|
|
130
189
|
const tableStyle: CSSProperties = {
|
|
131
190
|
width: "100%",
|
|
@@ -152,10 +211,14 @@ const swatchStyle = (value: string): CSSProperties => ({
|
|
|
152
211
|
});
|
|
153
212
|
|
|
154
213
|
export interface TokenTableBlockProps {
|
|
155
|
-
/** Optional path prefix filter (e.g. \`color\`). */
|
|
156
|
-
filter?: string;
|
|
214
|
+
/** Optional path prefix filter or filters (e.g. \`color\`). */
|
|
215
|
+
filter?: string | string[];
|
|
157
216
|
/** Optional DTCG \`$type\` filter. */
|
|
158
217
|
type?: string;
|
|
218
|
+
/** Restrict rows to tokens used to define typography. */
|
|
219
|
+
typography?: boolean;
|
|
220
|
+
/** Generated token-set name. Defaults to Storybook's \`theme\` global. */
|
|
221
|
+
theme?: string;
|
|
159
222
|
}
|
|
160
223
|
|
|
161
224
|
/**
|
|
@@ -163,15 +226,27 @@ export interface TokenTableBlockProps {
|
|
|
163
226
|
*/
|
|
164
227
|
export function TokenTableBlock({
|
|
165
228
|
filter,
|
|
166
|
-
type
|
|
229
|
+
type,
|
|
230
|
+
typography,
|
|
231
|
+
theme
|
|
167
232
|
}: TokenTableBlockProps = {}): ReactElement {
|
|
168
|
-
const
|
|
169
|
-
|
|
233
|
+
const activeTheme = ${t?`useThemeVariant(TOKEN_VARIANTS, ${w(r)}, theme)`:w(r)};
|
|
234
|
+
const rows = TOKEN_VARIANTS[activeTheme].filter(token => {
|
|
235
|
+
const filters = typeof filter === "string" ? [filter] : filter;
|
|
236
|
+
if (
|
|
237
|
+
filters &&
|
|
238
|
+
!filters.some(prefix =>
|
|
239
|
+
token.path === prefix || token.path.startsWith(\`\${prefix}.\`)
|
|
240
|
+
)
|
|
241
|
+
) {
|
|
170
242
|
return false;
|
|
171
243
|
}
|
|
172
244
|
if (type && token.type !== type) {
|
|
173
245
|
return false;
|
|
174
246
|
}
|
|
247
|
+
if (typography && !token.typography) {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
175
250
|
return true;
|
|
176
251
|
});
|
|
177
252
|
|
|
@@ -208,7 +283,7 @@ export function TokenTableBlock({
|
|
|
208
283
|
</table>
|
|
209
284
|
);
|
|
210
285
|
}
|
|
211
|
-
`}function
|
|
286
|
+
`}function P(e){let t=e.titlePrefix??`Design Tokens`,n=e.hasColors?`
|
|
212
287
|
## Colors
|
|
213
288
|
|
|
214
289
|
<ColorPaletteBlock />
|
|
@@ -216,37 +291,53 @@ export function TokenTableBlock({
|
|
|
216
291
|
## Typography
|
|
217
292
|
|
|
218
293
|
<TypesetBlock />
|
|
294
|
+
`:``,i=e.hasSizes?`
|
|
295
|
+
## Size
|
|
296
|
+
|
|
297
|
+
<TokenTableBlock filter="size" />
|
|
298
|
+
`:``,a=e.hasSpaces?`
|
|
299
|
+
## Space
|
|
300
|
+
|
|
301
|
+
<TokenTableBlock filter={[
|
|
302
|
+
"space",
|
|
303
|
+
"spacing"
|
|
304
|
+
]} />
|
|
219
305
|
`:``;return`import { Meta } from "@storybook/addon-docs/blocks";
|
|
220
306
|
import { ColorPaletteBlock } from "./blocks/ColorPalette";
|
|
221
307
|
import { TokenTableBlock } from "./blocks/TokenTable";
|
|
222
308
|
import { TypesetBlock } from "./blocks/Typeset";
|
|
223
309
|
|
|
224
|
-
<Meta title="${
|
|
310
|
+
<Meta title="${C(t)}/Overview" />
|
|
225
311
|
|
|
226
312
|
# ${t}
|
|
227
313
|
|
|
228
314
|
Design tokens generated by \`@razorwind/storybook\` for Storybook MDX docs.
|
|
229
|
-
${n}${r}
|
|
315
|
+
${n}${r}${i}${a}
|
|
230
316
|
## All tokens
|
|
231
317
|
|
|
232
318
|
<TokenTableBlock />
|
|
233
|
-
`}function
|
|
319
|
+
`}function F(e={}){return`import { Meta } from "@storybook/addon-docs/blocks";
|
|
234
320
|
import { ColorPaletteBlock } from "./blocks/ColorPalette";
|
|
235
321
|
|
|
236
|
-
<Meta title="${
|
|
322
|
+
<Meta title="${C(e.titlePrefix??`Design Tokens`)}/Colors" />
|
|
237
323
|
|
|
238
324
|
# Colors
|
|
239
325
|
|
|
240
326
|
<ColorPaletteBlock />
|
|
241
|
-
`}function
|
|
327
|
+
`}function I(e={}){return`import { Meta } from "@storybook/addon-docs/blocks";
|
|
242
328
|
import { TypesetBlock } from "./blocks/Typeset";
|
|
329
|
+
import { TokenTableBlock } from "./blocks/TokenTable";
|
|
243
330
|
|
|
244
|
-
<Meta title="${
|
|
331
|
+
<Meta title="${C(e.titlePrefix??`Design Tokens`)}/Typography" />
|
|
245
332
|
|
|
246
333
|
# Typography
|
|
247
334
|
|
|
248
335
|
<TypesetBlock />
|
|
249
|
-
|
|
336
|
+
|
|
337
|
+
## Typography tokens
|
|
338
|
+
|
|
339
|
+
<TokenTableBlock typography />
|
|
340
|
+
`}function L(e,t){let n=e[t];return typeof n==`string`?n:void 0}function R(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function z(e){return`import { IconGallery, IconItem } from "@storybook/addon-docs/blocks";
|
|
250
341
|
|
|
251
342
|
/**
|
|
252
343
|
* Icons rendered with Storybook's IconGallery doc block.
|
|
@@ -256,30 +347,33 @@ import { TypesetBlock } from "./blocks/Typeset";
|
|
|
256
347
|
export function IconGalleryBlock() {
|
|
257
348
|
return (
|
|
258
349
|
<IconGallery>
|
|
259
|
-
${(
|
|
350
|
+
${(R(e)?Object.values(e).filter(R).toSorted((e,t)=>(L(e,`name`)??``).localeCompare(L(t,`name`)??``)):[]).map(e=>{let t=L(e,`name`)??`unknown`,n=(Array.isArray(e.files)?e.files:[]).find(e=>R(e)&&L(e,`type`)===`svg`&&typeof e.content==`string`),r=R(n)&&typeof n.content==`string`?`<span
|
|
260
351
|
style={{ display: "inline-flex", width: 24, height: 24 }}
|
|
261
|
-
dangerouslySetInnerHTML={{ __html: ${
|
|
262
|
-
/>`:`<code>${
|
|
352
|
+
dangerouslySetInnerHTML={{ __html: ${w(n.content)} }}
|
|
353
|
+
/>`:`<code>${C(t)}</code>`;return` <IconItem name={${w(t)}}>
|
|
263
354
|
${r}
|
|
264
355
|
</IconItem>`}).join(`
|
|
265
356
|
`)||` {/* No icons */}`}
|
|
266
357
|
</IconGallery>
|
|
267
358
|
);
|
|
268
359
|
}
|
|
269
|
-
`}function
|
|
360
|
+
`}function B(e={}){return`import { Meta } from "@storybook/addon-docs/blocks";
|
|
270
361
|
import { IconGalleryBlock } from "./blocks/IconGallery";
|
|
271
362
|
|
|
272
|
-
<Meta title="${
|
|
363
|
+
<Meta title="${C(e.titlePrefix??`Design Tokens`)}/Icons" />
|
|
273
364
|
|
|
274
365
|
# Icons
|
|
275
366
|
|
|
276
367
|
<IconGalleryBlock />
|
|
277
|
-
`}function
|
|
278
|
-
export {
|
|
368
|
+
`}function V(e=!0){return`export { ColorPaletteBlock } from "./ColorPalette";
|
|
369
|
+
export type { ColorPaletteBlockProps } from "./ColorPalette";
|
|
370
|
+
${e?`export { IconGalleryBlock } from "./IconGallery";
|
|
371
|
+
`:``}
|
|
279
372
|
export { TokenTableBlock } from "./TokenTable";
|
|
280
373
|
export type { TokenTableBlockProps, TokenTableRow } from "./TokenTable";
|
|
281
374
|
export { TypesetBlock } from "./Typeset";
|
|
282
|
-
|
|
375
|
+
export type { TypesetBlockProps } from "./Typeset";
|
|
376
|
+
`}const H=new Set(`base.colorPrimary.colorSecondary.appBg.appContentBg.appHoverBg.appPreviewBg.appBorderColor.appBorderRadius.fontBase.fontCode.textColor.textInverseColor.textMutedColor.barTextColor.barHoverColor.barSelectedColor.barBg.buttonBg.buttonBorder.booleanBg.booleanSelectedBg.inputBg.inputBorder.inputTextColor.inputBorderRadius.brandTitle.brandUrl.brandImage.brandTarget.gridCellSize`.split(`.`));function U(e){return R(e)?e.base===`light`||e.base===`dark`:!1}function W(e){return R(e)?U(e)?!0:Object.keys(e).some(e=>H.has(e)):!1}function G(e){if(!R(e)||W(e))return!1;let t=Object.values(e);return t.length>0&&t.every(W)}function de(e,t,n){return t.base===`light`||t.base===`dark`?t.base:`${e} ${n??``}`.toLowerCase().includes(`light`)?`light`:`dark`}function fe(e){return/^[a-z_$][\w$]*$/i.test(e)?e:w(e)}function K(e,t){let n=e.find(e=>e.id===a);return!n||u(t.id)?t.tokens:f(t.tokens,n.tokens)}function pe(e){return R(e)&&`$value`in e&&!(`colorSpace`in e)&&!(`hex`in e)?e.$value:e}function me(e,t){let n=pe(e),r=new Set;for(let e=0;e<8;e++){let e=k(n);if(!e||r.has(e))break;r.add(e);let i=t.get(e);if(!i)break;n=i.value}return n}function he(e){return e==null||typeof e==`string`||typeof e==`number`||typeof e==`boolean`?e:c(e)}function ge(e,t){let n=p(e),r=n.find(e=>e.id===t)??(n.length===1?n[0]:void 0),i=r?K(n,r):e,a=new Map;for(let e of S(i))(!a.get(e.path)||e.theme===t)&&a.set(e.path,e);return a}function _e(e,t){let n={...e};for(let[r,i]of Object.entries(e))r!==`base`&&i!==void 0&&(n[r]=he(me(i,t)));return n}function ve(e,t,n,r,i){let a=_e(t,ge(i.tokens,e));return q(de(e,a,i.theme),a,n,r)}function q(e,a,o,s){let c=i(s,n),l=i(s,t);return{brandTarget:`_blank`,base:e||`light`,...a,brandTitle:a.brandTitle??o.title,brandUrl:a.brandUrl??o.homepage,brandImage:a.brandImage??o.logo,fontBase:a.fontBase??(c?r(c):void 0),fontCode:a.fontCode??(l?r(l):void 0)}}function J(e,t=``){return`create({\n${Object.entries(e).filter(([,e])=>e!==void 0).map(([e,n])=>`${t} ${e}: ${w(n)}`).join(`,
|
|
283
377
|
`)}\n${t}})`}function Y(e){return`import { create } from "storybook/theming";
|
|
284
378
|
|
|
285
379
|
/**
|
|
@@ -296,8 +390,8 @@ export default ${J(e)};
|
|
|
296
390
|
* @see https://storybook.js.org/docs/configure/user-interface/theming
|
|
297
391
|
*/
|
|
298
392
|
export default {
|
|
299
|
-
${Object.entries(e).map(([e,t])=>` ${
|
|
393
|
+
${Object.entries(e).map(([e,t])=>` ${fe(e)}: ${J(t,` `)}`).join(`,
|
|
300
394
|
`)}
|
|
301
395
|
};
|
|
302
|
-
`}function Z(e){if(
|
|
396
|
+
`}function Z(e){if(U(e))return Y(e);let t=Object.entries(e);if(t.length!==1)return X(e);let[,n]=t[0]??[];return n?Y(n):X(e)}function Q(e,t,n,r){let i=(e,r)=>ve(e,r,n,t.fonts,t);if(G(e))return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,i(e,t)]));let a=p(t.tokens),o=a.filter(e=>!u(e.id));if(o.length>1){let e={};for(let t of o){let n=r(K(a,t));if(G(n)){let r=n[t.id];if(r){e[t.id]=i(t.id,r);continue}for(let[t,r]of Object.entries(n))e[t]=i(t,r);continue}W(n)&&(e[t.id]=i(t.id,n))}return e}if(W(e)){let n=o[0]&&o[0].id!=="default"?o[0].id:t.theme??`default`;return{[n]:i(n,e)}}return{}}const ye=e=>(t,n,r)=>o(_(e,t),n,{name:`razorwind-storybook`},!1,r);function $(e,t={}){let n=t.outputPath??`storybook/tokens`,r=ee(e),i=t.titlePrefix??r.title??`Design Tokens`,a={...t,titlePrefix:i},o=S(e.tokens,t),s=oe(e.tokens,t),c=o.some(e=>e.type===`color`),l=e.fonts&&Object.keys(e.fonts).length>0||o.some(D),u=O(o,[`size`]),d=O(o,[`space`,`spacing`]),f=!t.skipIcons&&R(e.icons)&&Object.keys(e.icons).length>0,p=ye(n),m={[_(n,`blocks/ColorPalette.tsx`)]:p(`blocks/ColorPalette.tsx`,le(s,a),`tsx`),[_(n,`blocks/Typeset.tsx`)]:p(`blocks/Typeset.tsx`,ue(s,{...a,fonts:e.fonts}),`tsx`),[_(n,`blocks/TokenTable.tsx`)]:p(`blocks/TokenTable.tsx`,N(s),`tsx`),[_(n,`blocks/index.ts`)]:p(`blocks/index.ts`,V(f),`typescript`),[_(n,`Tokens.mdx`)]:p(`Tokens.mdx`,P({titlePrefix:i,hasColors:c,hasTypography:l,hasSizes:u,hasSpaces:d}),`mdx`)};f&&(m[_(n,`blocks/IconGallery.tsx`)]=p(`blocks/IconGallery.tsx`,z(e.icons),`tsx`)),M(s)&&(m[_(n,`blocks/ThemeVariant.ts`)]=p(`blocks/ThemeVariant.ts`,ae(),`typescript`)),c&&(m[_(n,`Colors.mdx`)]=p(`Colors.mdx`,F(a),`mdx`)),l&&(m[_(n,`Typography.mdx`)]=p(`Typography.mdx`,I(a),`mdx`)),f&&(m[_(n,`Icons.mdx`)]=p(`Icons.mdx`,B(a),`mdx`)),m[_(n,`tokens.json`)]=p(`tokens.json`,`${JSON.stringify(o,null,2)}\n`,`json`);let h;if(t.mapTheme){let i=Q(t.mapTheme(e.tokens),e,r,t.mapTheme);Object.keys(i).length>0&&(h=Object.keys(i),m[_(n,`theme.ts`)]=p(`theme.ts`,Z(i),`typescript`))}let g=`INSTALL.md`;return m[_(n,g)]=p(g,t.installGuide??T({outputPath:n,titlePrefix:i,themeFiles:h?[`theme.ts`]:void 0,themeNames:h,tokenThemeNames:Object.keys(s)}),`markdown`),m}var be=e(e=>({name:`storybook`,themeGeneration:`combined`,generate:async t=>$(t,e??{})}));export{q as applyBrandDefaults,be as default,S as flattenTokens,c as formatTokenValue,$ as generateTokenDocs,Q as normalizeThemes,T as renderInstallMd,Z as renderThemeFile,p as resolveTokenSets,m as toCssVar};
|
|
303
397
|
//# sourceMappingURL=index.mjs.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@razorwind/storybook",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.61",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Razorwind plugin that creates Storybook MDX token doc blocks from Razorwind design tokens.",
|
|
6
6
|
"repository": {
|
|
@@ -83,11 +83,11 @@
|
|
|
83
83
|
"types": "./dist/index.d.cts",
|
|
84
84
|
"typings": "dist/index.d.mts",
|
|
85
85
|
"dependencies": {
|
|
86
|
-
"@power-plant/core": "^0.0.
|
|
86
|
+
"@power-plant/core": "^0.0.92",
|
|
87
87
|
"@power-plant/dtcg-schema": "^0.0.1",
|
|
88
|
-
"@razorwind/core": "^0.0.
|
|
89
|
-
"@stryke/path": "^0.29.
|
|
90
|
-
"@stryke/type-checks": "^0.6.
|
|
88
|
+
"@razorwind/core": "^0.0.64",
|
|
89
|
+
"@stryke/path": "^0.29.41",
|
|
90
|
+
"@stryke/type-checks": "^0.6.47"
|
|
91
91
|
},
|
|
92
92
|
"peerDependencies": {
|
|
93
93
|
"@storybook/addon-docs": ">=8",
|
|
@@ -100,11 +100,11 @@
|
|
|
100
100
|
"storybook": { "optional": true }
|
|
101
101
|
},
|
|
102
102
|
"devDependencies": {
|
|
103
|
-
"@powerlines/plugin-tsdown": "^0.1.
|
|
104
|
-
"@stryke/types": "^0.12.
|
|
103
|
+
"@powerlines/plugin-tsdown": "^0.1.613",
|
|
104
|
+
"@stryke/types": "^0.12.42",
|
|
105
105
|
"@types/node": "^25.9.5",
|
|
106
106
|
"typescript": "^6.0.3"
|
|
107
107
|
},
|
|
108
108
|
"publishConfig": { "access": "public" },
|
|
109
|
-
"gitHead": "
|
|
109
|
+
"gitHead": "49873beaca1bec96f7d6869f9dcd8c20efd2be41"
|
|
110
110
|
}
|