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.
- package/README.md +201 -0
- package/bin/inkhouse.mjs +171 -0
- package/code.js +11802 -0
- package/manifest.json +30 -0
- package/package.json +45 -0
- package/scanner/blob-placement-regression.ts +132 -0
- package/scanner/class-collector.ts +69 -0
- package/scanner/cli.ts +336 -0
- package/scanner/component-scanner.ts +2876 -0
- package/scanner/css-patch-regression.ts +112 -0
- package/scanner/css-token-reader-regression.ts +92 -0
- package/scanner/css-token-reader.ts +477 -0
- package/scanner/font-style-resolver-regression.ts +32 -0
- package/scanner/index.ts +9 -0
- package/scanner/radial-gradient-regression.ts +53 -0
- package/scanner/style-map.ts +145 -0
- package/scanner/tailwind-parser.ts +644 -0
- package/scanner/transform-math-regression.ts +42 -0
- package/scanner/types.ts +298 -0
- package/src/blob-placement.ts +111 -0
- package/src/change-detection.ts +204 -0
- package/src/class-utils.ts +105 -0
- package/src/clip-path-decorative.ts +194 -0
- package/src/color-resolver.ts +98 -0
- package/src/colors.ts +196 -0
- package/src/component-defs.ts +54 -0
- package/src/component-gen.ts +561 -0
- package/src/component-lookup.ts +82 -0
- package/src/config.ts +115 -0
- package/src/design-system.ts +59 -0
- package/src/dev-server.ts +173 -0
- package/src/figma-globals.d.ts +3 -0
- package/src/font-style-resolver.ts +171 -0
- package/src/github.ts +1465 -0
- package/src/icon-builder.ts +607 -0
- package/src/image-cache.ts +22 -0
- package/src/inline-text.ts +271 -0
- package/src/layout-parser.ts +667 -0
- package/src/layout-utils.ts +155 -0
- package/src/main.ts +687 -0
- package/src/node-ir.ts +595 -0
- package/src/pack-provider.ts +148 -0
- package/src/packs.ts +126 -0
- package/src/radial-gradient.ts +84 -0
- package/src/render-context.ts +138 -0
- package/src/responsive-analyzer.ts +139 -0
- package/src/state-analyzer.ts +143 -0
- package/src/story-builder.ts +1706 -0
- package/src/story-layout.ts +38 -0
- package/src/tailwind.ts +2379 -0
- package/src/text-builder.ts +116 -0
- package/src/text-line.ts +42 -0
- package/src/token-source.ts +43 -0
- package/src/tokens.ts +717 -0
- package/src/transform-math.ts +44 -0
- package/src/ui-builder.ts +1996 -0
- package/src/utility-resolver.ts +125 -0
- package/src/variables.ts +1042 -0
- package/src/width-solver.ts +466 -0
- package/templates/patch-tokens-route.ts +165 -0
- package/templates/scan-components-route.ts +57 -0
- 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
|
+
}
|