inkhouse 0.1.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +201 -0
  2. package/bin/inkhouse.mjs +171 -0
  3. package/code.js +11802 -0
  4. package/manifest.json +30 -0
  5. package/package.json +45 -0
  6. package/scanner/blob-placement-regression.ts +132 -0
  7. package/scanner/class-collector.ts +69 -0
  8. package/scanner/cli.ts +336 -0
  9. package/scanner/component-scanner.ts +2876 -0
  10. package/scanner/css-patch-regression.ts +112 -0
  11. package/scanner/css-token-reader-regression.ts +92 -0
  12. package/scanner/css-token-reader.ts +477 -0
  13. package/scanner/font-style-resolver-regression.ts +32 -0
  14. package/scanner/index.ts +9 -0
  15. package/scanner/radial-gradient-regression.ts +53 -0
  16. package/scanner/style-map.ts +145 -0
  17. package/scanner/tailwind-parser.ts +644 -0
  18. package/scanner/transform-math-regression.ts +42 -0
  19. package/scanner/types.ts +298 -0
  20. package/src/blob-placement.ts +111 -0
  21. package/src/change-detection.ts +204 -0
  22. package/src/class-utils.ts +105 -0
  23. package/src/clip-path-decorative.ts +194 -0
  24. package/src/color-resolver.ts +98 -0
  25. package/src/colors.ts +196 -0
  26. package/src/component-defs.ts +54 -0
  27. package/src/component-gen.ts +561 -0
  28. package/src/component-lookup.ts +82 -0
  29. package/src/config.ts +115 -0
  30. package/src/design-system.ts +59 -0
  31. package/src/dev-server.ts +173 -0
  32. package/src/figma-globals.d.ts +3 -0
  33. package/src/font-style-resolver.ts +171 -0
  34. package/src/github.ts +1465 -0
  35. package/src/icon-builder.ts +607 -0
  36. package/src/image-cache.ts +22 -0
  37. package/src/inline-text.ts +271 -0
  38. package/src/layout-parser.ts +667 -0
  39. package/src/layout-utils.ts +155 -0
  40. package/src/main.ts +687 -0
  41. package/src/node-ir.ts +595 -0
  42. package/src/pack-provider.ts +148 -0
  43. package/src/packs.ts +126 -0
  44. package/src/radial-gradient.ts +84 -0
  45. package/src/render-context.ts +138 -0
  46. package/src/responsive-analyzer.ts +139 -0
  47. package/src/state-analyzer.ts +143 -0
  48. package/src/story-builder.ts +1706 -0
  49. package/src/story-layout.ts +38 -0
  50. package/src/tailwind.ts +2379 -0
  51. package/src/text-builder.ts +116 -0
  52. package/src/text-line.ts +42 -0
  53. package/src/token-source.ts +43 -0
  54. package/src/tokens.ts +717 -0
  55. package/src/transform-math.ts +44 -0
  56. package/src/ui-builder.ts +1996 -0
  57. package/src/utility-resolver.ts +125 -0
  58. package/src/variables.ts +1042 -0
  59. package/src/width-solver.ts +466 -0
  60. package/templates/patch-tokens-route.ts +165 -0
  61. package/templates/scan-components-route.ts +57 -0
  62. package/ui.html +1222 -0
@@ -0,0 +1,155 @@
1
+ import { applyFullWidthIfPossible, applyGridColumnsIfPossible, getGridColumnsNode, hasGridColumnsNode, getColSpanNode } from './tailwind';
2
+ import { getBaseClass } from './width-solver';
3
+
4
+ declare const figma: any;
5
+
6
+ export type LayoutWrapContext = {
7
+ maxWidth?: number;
8
+ parentLayout?: 'HORIZONTAL' | 'VERTICAL';
9
+ };
10
+
11
+ export function shouldClipContent(classes: string[]): boolean {
12
+ let hasOverflowHidden = false;
13
+ let hasBlur = false;
14
+ for (const cls of classes) {
15
+ const base = getBaseClass(cls) || cls;
16
+ if (base === 'overflow-hidden' || base === 'overflow-clip') hasOverflowHidden = true;
17
+ if (base === 'overflow-visible') return false;
18
+ if (/^blur(-[a-z0-9]+)?$/.test(base)) hasBlur = true;
19
+ }
20
+ // In CSS, overflow:hidden clips children but NOT the element's own filter:blur —
21
+ // the blur bleeds past the frame's bounds. In Figma, clipsContent=true would
22
+ // clip the blur too. Skip clipsContent when both are present so blur can spread
23
+ // naturally (the inner gradient-blob-mask still clips the polygon itself).
24
+ if (hasOverflowHidden && hasBlur) return false;
25
+ return hasOverflowHidden;
26
+ }
27
+
28
+ export function applyClipBehavior(frame: FrameNode, classes: string[] | undefined): void {
29
+ const list = classes || [];
30
+ frame.clipsContent = shouldClipContent(list);
31
+ }
32
+
33
+ export function resolveGridWidthOverride(frame: FrameNode, contentWidth?: number | null): number | undefined {
34
+ if (contentWidth == null || !Number.isFinite(contentWidth)) return undefined;
35
+ const padding = (frame.paddingLeft || 0) + (frame.paddingRight || 0);
36
+ const width = contentWidth + padding;
37
+ if (!Number.isFinite(width) || width <= 0) return undefined;
38
+ return width;
39
+ }
40
+
41
+ function reflowFullWidthInSubtree(parent: FrameNode): void {
42
+ const padding = (parent.paddingLeft || 0) + (parent.paddingRight || 0);
43
+ const widthOverride = Number.isFinite(parent.width) ? Math.max(0, parent.width - padding) : null;
44
+ const options = widthOverride != null ? { widthOverride: widthOverride } : undefined;
45
+
46
+ for (const child of parent.children) {
47
+ applyFullWidthIfPossible(child, parent, options);
48
+ if ('layoutMode' in child) {
49
+ const childFrame = child as FrameNode;
50
+ if (hasGridColumnsNode(childFrame)) {
51
+ applyGridColumnsIfPossible(childFrame);
52
+ }
53
+ reflowFullWidthInSubtree(childFrame);
54
+ }
55
+ }
56
+ }
57
+
58
+ export function applyGridColumnsWithReflow(
59
+ frame: FrameNode,
60
+ widthOverride?: number,
61
+ colsOverride?: number
62
+ ): void {
63
+ applyGridColumnsIfPossible(frame, widthOverride, colsOverride);
64
+ if (!hasGridColumnsNode(frame)) return;
65
+ for (const child of frame.children) {
66
+ if ('layoutMode' in child) {
67
+ reflowFullWidthInSubtree(child as FrameNode);
68
+ }
69
+ }
70
+ stretchGridRowHeights(frame, colsOverride);
71
+ }
72
+
73
+ function stretchGridRowHeights(frame: FrameNode, colsOverride?: number): void {
74
+ const cols = colsOverride != null ? colsOverride : getGridColumnsNode(frame);
75
+ if (!cols || cols <= 0) return;
76
+ const flowChildren: SceneNode[] = [];
77
+ for (const child of frame.children) {
78
+ if ((child as any).layoutPositioning === 'ABSOLUTE') continue;
79
+ if (!('resize' in child)) continue;
80
+ flowChildren.push(child);
81
+ }
82
+ if (flowChildren.length === 0) return;
83
+
84
+ // Track row index per child, accounting for col-spans
85
+ const rowIndices: number[] = [];
86
+ const rowHeights: number[] = [];
87
+ let slotIndex = 0;
88
+ for (let i = 0; i < flowChildren.length; i++) {
89
+ const span = Math.min(getColSpanNode(flowChildren[i]), cols);
90
+ const rowIndex = Math.floor(slotIndex / cols);
91
+ rowIndices.push(rowIndex);
92
+ const height = (flowChildren[i] as any).height;
93
+ if (Number.isFinite(height)) {
94
+ const current = rowHeights[rowIndex];
95
+ if (current == null || height > current) rowHeights[rowIndex] = height;
96
+ }
97
+ slotIndex += span;
98
+ }
99
+
100
+ for (let i = 0; i < flowChildren.length; i++) {
101
+ const rowIndex = rowIndices[i];
102
+ const targetHeight = rowHeights[rowIndex];
103
+ if (!Number.isFinite(targetHeight) || targetHeight <= 0) continue;
104
+ const child = flowChildren[i] as any;
105
+ try {
106
+ child.resize(child.width, targetHeight);
107
+ if ('layoutMode' in child) {
108
+ if (child.layoutMode === 'VERTICAL' && 'primaryAxisSizingMode' in child) {
109
+ child.primaryAxisSizingMode = 'FIXED';
110
+ } else if (child.layoutMode === 'HORIZONTAL' && 'counterAxisSizingMode' in child) {
111
+ child.counterAxisSizingMode = 'FIXED';
112
+ }
113
+ } else if ('primaryAxisSizingMode' in child) {
114
+ child.primaryAxisSizingMode = 'FIXED';
115
+ }
116
+ } catch (_err) {
117
+ // ignore
118
+ }
119
+ }
120
+ }
121
+
122
+ export function maybeWrapMxAuto(
123
+ node: SceneNode | null,
124
+ classes: string[],
125
+ context: LayoutWrapContext
126
+ ): SceneNode | null {
127
+ if (!node) return null;
128
+ if (!classes.includes('mx-auto')) return node;
129
+ if (context.parentLayout !== 'VERTICAL') return node;
130
+ const wrapper = figma.createFrame();
131
+ wrapper.name = 'mx-auto';
132
+ wrapper.layoutMode = 'HORIZONTAL';
133
+ wrapper.primaryAxisSizingMode = 'AUTO';
134
+ wrapper.counterAxisSizingMode = 'AUTO';
135
+ wrapper.primaryAxisAlignItems = 'CENTER';
136
+ wrapper.counterAxisAlignItems = 'CENTER';
137
+ wrapper.fills = [];
138
+ wrapper.strokes = [];
139
+ wrapper.clipsContent = false;
140
+ if ('layoutAlign' in wrapper) (wrapper as any).layoutAlign = 'STRETCH';
141
+ wrapper.appendChild(node);
142
+ if (typeof context.maxWidth === 'number' && context.maxWidth > 0 && 'resize' in wrapper) {
143
+ try {
144
+ if ((wrapper as any).width < context.maxWidth) {
145
+ (wrapper as any).resize(context.maxWidth, (wrapper as any).height);
146
+ if ('primaryAxisSizingMode' in wrapper) {
147
+ (wrapper as any).primaryAxisSizingMode = 'FIXED';
148
+ }
149
+ }
150
+ } catch (_err) {
151
+ // ignore resize errors
152
+ }
153
+ }
154
+ return wrapper;
155
+ }