@surrealdb/ui 1.2.16 → 1.2.18
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/assets/1299e4695ad0bd61.svg +5 -0
- package/dist/assets/408117e904653548.svg +48 -0
- package/dist/assets/b516b020352f6369.svg +1 -0
- package/dist/assets/dfde8de566c3c2b9.svg +11 -0
- package/dist/assets/e9eae7bc863f211b.svg +1 -0
- package/dist/assets/f74ae3707a5d418f.svg +1 -0
- package/dist/icons.d.ts +6 -0
- package/dist/icons.js +198 -195
- package/dist/icons.js.map +1 -1
- package/dist/ui.d.ts +18 -0
- package/dist/ui.js +1441 -1426
- package/dist/ui.js.map +1 -1
- package/package.json +8 -2
- package/.claude/launch.json +0 -17
- package/.oxfmtrc.json +0 -27
- package/.oxlintrc.json +0 -23
- package/.picto-fix-png/Cursor.png +0 -0
- package/.picto-fix-png/DocumentGradient.png +0 -0
- package/.picto-fix-png/FileGradient.png +0 -0
- package/.picto-fix-png/HealthBubbleGradient.png +0 -0
- package/.picto-fix-png/LearnGradient.png +0 -0
- package/.picto-fix-png/MediaFileGradient.png +0 -0
- package/.zed/settings.json +0 -36
- package/AGENTS.md +0 -111
- package/CLAUDE.md +0 -111
- package/REVIEW.md +0 -81
- package/doc/code-style.md +0 -264
- package/doc/release.md +0 -102
- package/doc/stacker-plan.md +0 -219
- package/release-notes.md +0 -39
- package/tests/_setup/e2e-helpers.tsx +0 -169
- package/tests/_setup/markdown-classes.ts +0 -3
- package/tests/_setup/portable-stories.ts +0 -33
- package/tests/e2e/MarkdownEditor/__screenshots__/content-blocks.test.tsx/MarkdownEditor---content-blocks-continues-an-ordered-list-when-Enter-is-pressed-at-the-end-of-a-numbered-line-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/content-blocks.test.tsx/MarkdownEditor---content-blocks-renders-a-diagram-for-the-fenced-mermaid-block-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/content-blocks.test.tsx/MarkdownEditor---content-blocks-renders-a-horizontal-rule-as-a-separator-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/content-blocks.test.tsx/MarkdownEditor---content-blocks-renders-a-read-only-table-preview-when-the-table-block-is-inactive-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/content-blocks.test.tsx/MarkdownEditor---content-blocks-shows-blockquote-text-from-the-sample-document-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/content-blocks.test.tsx/MarkdownEditor---content-blocks-shows-fenced-TypeScript-sample-code-in-the-document-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/edits.test.tsx/MarkdownEditor---edits-auto-continues-a-bullet-list-when-Enter-is-pressed-at-the-end-of-a-list-item-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/edits.test.tsx/MarkdownEditor---edits-expands-triple-backtick-into-a-fenced-code-block-with-the-caret-on-the-empty-body-line-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/edits.test.tsx/MarkdownEditor---edits-reflects-typed-characters-in-the-underlying-document-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/heading-fold.test.tsx/MarkdownEditor---heading-folds-viewer--folds-and-unfolds-a-heading-section-via-the-margin-control-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/hybrid-widgets.test.tsx/MarkdownEditor---hybrid-widgets-clicking-a-callout-header-focuses-the-editor-and-parks-the-caret-inside-the-callout--REASONING--4--1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/hybrid-widgets.test.tsx/MarkdownEditor---hybrid-widgets-keeps-list-bullets-consistent-after-hopping-the-caret-across-items-several-times-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/hybrid-widgets.test.tsx/MarkdownEditor---hybrid-widgets-reveals-heading-marks-when-the-caret-enters-the-heading-line-and-hides-them-when-it-leaves-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/hybrid-widgets.test.tsx/MarkdownEditor---hybrid-widgets-shows-the-right-callout-title-when-moving-focus-between-consecutive-callouts-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/hybrid-widgets.test.tsx/MarkdownEditor---preview-widgets-clicking-a-callout-header-focuses-the-editor-with-the-caret-in-that-callout-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/hybrid-widgets.test.tsx/MarkdownEditor---preview-widgets-keeps-list-bullets-consistent-after-hopping-the-caret-across-items-several-times-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/hybrid-widgets.test.tsx/MarkdownEditor---preview-widgets-shows-the-right-callout-title-when-moving-focus-between-consecutive-callouts-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/jsx-block-content.test.tsx/MarkdownEditor---block-JSX-components-clicking-the-edit-source-action-selects-the-component-source-range-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/jsx-block-content.test.tsx/MarkdownEditor---block-JSX-components-renders-block-components-via-JsxBlockWidget-with-an-edit-source-action-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/jsx-block-content.test.tsx/MarkdownEditor---inline-JSX-rendering-block-content-keeps-multiple-inline-JSX-widgets-on-the-same-line-side-by-side-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/jsx-highlight.test.tsx/MarkdownEditor---JSX-attribute-highlighting-does-not-let-the-nested-HTML-parser-mis-highlight-attributes-after-an-expression-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/jsx-inline-click.test.tsx/MarkdownEditor---inline-JSX-click-reveal-reveals-the-raw-source-when-an-inline-component-badge-is-clicked-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/jsx-inline-click.test.tsx/MarkdownEditor---inline-JSX-click-reveal-reveals-the-raw-source-when-an-inline-component-badge-is-clicked-2.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/jsx-inline-click.test.tsx/MarkdownEditor---inline-JSX-click-reveal-reveals-the-raw-source-when-an-inline-component-badge-is-clicked-3.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/jsx-selection.test.tsx/MarkdownEditor---JSX-selection-reveal-shows-JSX-widget-when-inactive-and-raw-source-when-caret-is-inside-the-tag-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/media-sizing.test.tsx/MarkdownEditor---media-sizing-images-and-videos-do-not-exceed-the-editor-content-width-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/media-sizing.test.tsx/MarkdownEditor---media-sizing-matches-MarkdownViewer-image-width-in-the-split-playground-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/modes.test.tsx/MarkdownEditor---modes-standalone-MarkdownViewer-is-not-a-CodeMirror-surface-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/modes.test.tsx/MarkdownEditor---modes-toggling-the-SegmentedControl-swaps-mode-without-remounting-the-editor-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/regressions.test.tsx/MarkdownEditor---regressions-repeated-checkbox-toggles-do-not-duplicate-or-drift-task-markers-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/regressions.test.tsx/MarkdownEditor---regressions-scroll-position-is-preserved-when-the-document-is-edited--chat-92584463--1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/regressions.test.tsx/MarkdownEditor---regressions-task-checkbox-toggles-between-checked-and-unchecked-in-the-source-document-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/regressions.test.tsx/MarkdownEditor---regressions-toggle-via-checkbox-preserves-task-marker-widgets-visible-from-the-previous-caret-position-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/slash-commands.test.tsx/MarkdownEditor---slash-commands-dismisses-on-Escape-and-leaves-the-slash-as-literal-text-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/slash-commands.test.tsx/MarkdownEditor---slash-commands-highlights-the-slash-and-shows-an-Enter-command-placeholder-until-text-is-typed-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/slash-commands.test.tsx/MarkdownEditor---slash-commands-opens-a-filtered--keyboard-navigable-menu-and-inserts-boilerplate-on-Enter-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/undo-redo.test.tsx/MarkdownEditor---undo-and-redo-restores-undone-document-edits-via-redo-1.png +0 -0
- package/tests/e2e/MarkdownEditor/content-blocks.test.tsx +0 -153
- package/tests/e2e/MarkdownEditor/edits.test.tsx +0 -112
- package/tests/e2e/MarkdownEditor/heading-fold.test.tsx +0 -45
- package/tests/e2e/MarkdownEditor/hybrid-widgets.test.tsx +0 -193
- package/tests/e2e/MarkdownEditor/image-on-image.test.tsx +0 -73
- package/tests/e2e/MarkdownEditor/jsx-block-content.test.tsx +0 -243
- package/tests/e2e/MarkdownEditor/jsx-highlight.test.tsx +0 -69
- package/tests/e2e/MarkdownEditor/jsx-inline-badges.test.tsx +0 -60
- package/tests/e2e/MarkdownEditor/jsx-inline-click.test.tsx +0 -54
- package/tests/e2e/MarkdownEditor/jsx-selection.test.tsx +0 -44
- package/tests/e2e/MarkdownEditor/link-placeholder.test.tsx +0 -68
- package/tests/e2e/MarkdownEditor/media-align.test.tsx +0 -58
- package/tests/e2e/MarkdownEditor/media-edit.test.tsx +0 -64
- package/tests/e2e/MarkdownEditor/media-sizing.test.tsx +0 -124
- package/tests/e2e/MarkdownEditor/modes.test.tsx +0 -94
- package/tests/e2e/MarkdownEditor/onimage-parity.test.tsx +0 -50
- package/tests/e2e/MarkdownEditor/regressions.test.tsx +0 -223
- package/tests/e2e/MarkdownEditor/slash-commands.test.tsx +0 -100
- package/tests/e2e/MarkdownEditor/table-click.test.tsx +0 -48
- package/tests/e2e/MarkdownEditor/table-controls.test.tsx +0 -57
- package/tests/e2e/MarkdownEditor/table-format.test.tsx +0 -42
- package/tests/e2e/MarkdownEditor/undo-redo.test.tsx +0 -39
- package/tests/e2e/MarkdownViewer/__screenshots__/parity.test.tsx/MarkdownViewer---editor-parity-matches-computed-font-size-on-first-heading-1.png +0 -0
- package/tests/e2e/MarkdownViewer/__screenshots__/parity.test.tsx/MarkdownViewer---editor-parity-matches-counts-for-shared-structural-classes-1.png +0 -0
- package/tests/e2e/MarkdownViewer/__screenshots__/parity.test.tsx/MarkdownViewer---editor-parity-matches-visible-text-between-preview-editor--blurred--and-MarkdownViewer-1.png +0 -0
- package/tests/e2e/MarkdownViewer/__screenshots__/render.test.tsx/MarkdownViewer---render-exercises-shared-preview-class-names-without-mounting-CodeMirror-1.png +0 -0
- package/tests/e2e/MarkdownViewer/parity.test.tsx +0 -191
- package/tests/e2e/MarkdownViewer/render.test.tsx +0 -36
- package/tests/e2e/Stacker/stacker.test.tsx +0 -304
- package/tests/unit/Editor/helpers.test.ts +0 -42
- package/tests/unit/MarkdownConformance/error-isolation.test.tsx +0 -44
- package/tests/unit/MarkdownConformance/indentation.test.tsx +0 -30
- package/tests/unit/MarkdownConformance/inline-code-fuzz.test.tsx +0 -255
- package/tests/unit/MarkdownConformance/jsx-attributes.test.tsx +0 -57
- package/tests/unit/MarkdownConformance/jsx-fuzz.test.tsx +0 -165
- package/tests/unit/MarkdownConformance/links.test.tsx +0 -95
- package/tests/unit/MarkdownConformance/render-helpers.tsx +0 -88
- package/tests/unit/MarkdownEditor/code-info.test.ts +0 -63
- package/tests/unit/MarkdownEditor/decorations.test.ts +0 -490
- package/tests/unit/MarkdownEditor/editor-ready.test.ts +0 -36
- package/tests/unit/MarkdownEditor/html-descriptors.test.ts +0 -94
- package/tests/unit/MarkdownEditor/indented-fence.test.ts +0 -103
- package/tests/unit/MarkdownEditor/jsx-attr-scan.test.ts +0 -115
- package/tests/unit/MarkdownEditor/jsx-block-widget.test.ts +0 -92
- package/tests/unit/MarkdownEditor/jsx-tag-grammar.test.ts +0 -88
- package/tests/unit/MarkdownEditor/list-indent.test.ts +0 -95
- package/tests/unit/MarkdownEditor/slash-commands.test.ts +0 -213
- package/tests/unit/MarkdownEditor/table-format.test.ts +0 -83
- package/tests/unit/MarkdownEditor/table.test.ts +0 -119
- package/tests/unit/MarkdownEditor/triggers.test.ts +0 -244
- package/tests/unit/MarkdownEditor/widget-store.test.ts +0 -105
- package/tests/unit/MarkdownViewer/callout.test.tsx +0 -60
- package/tests/unit/MarkdownViewer/code-highlight.test.tsx +0 -61
- package/tests/unit/MarkdownViewer/code-title.test.tsx +0 -62
- package/tests/unit/MarkdownViewer/features.test.tsx +0 -164
- package/tests/unit/MarkdownViewer/headings.test.tsx +0 -40
- package/tests/unit/MarkdownViewer/indented-jsx-children.test.tsx +0 -98
- package/tests/unit/MarkdownViewer/jsx-block-nesting.test.tsx +0 -234
- package/tests/unit/MarkdownViewer/jsx.test.tsx +0 -211
- package/tests/unit/MarkdownViewer/list-bullets.test.tsx +0 -49
- package/tests/unit/MarkdownViewer/list-code.test.tsx +0 -65
- package/tests/unit/MarkdownViewer/preserve-newlines.test.tsx +0 -143
- package/tests/unit/MarkdownViewer/read-time.test.ts +0 -21
- package/tests/unit/MarkdownViewer/renderers.test.tsx +0 -95
- package/tests/unit/MarkdownViewer/runnable.test.tsx +0 -69
- package/tests/unit/MarkdownViewer/ssr.test.tsx +0 -94
- package/tests/unit/MarkdownViewer/syntax-highlighting.test.tsx +0 -74
- package/tests/unit/MarkdownViewer/tabs-fenced-code.test.tsx +0 -196
- package/tests/unit/MarkdownViewer/tabs.test.tsx +0 -116
- package/tests/unit/Stacker/drag.test.ts +0 -21
- package/tests/unit/Stacker/helpers.test.ts +0 -105
- package/tests/unit/Stacker/reducer.test.ts +0 -107
- package/tests/unit/Syntax/language.test.ts +0 -63
- package/tools/duplicate-vector-mappings.json +0 -143
- package/tools/glass-export-overrides.json +0 -8
- package/tools/icon-parser.ts +0 -78
- package/tools/integration-gradient-exports.json +0 -57
- package/tools/picto-compare.ts +0 -64
- package/tools/picto-merge-urls.ts +0 -43
- package/tools/picto-sync.ts +0 -630
- package/tools/picto-verify-gradients.ts +0 -86
- package/tools/vector-export-nodes.json +0 -977
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
coveredEphemeral,
|
|
3
|
-
drawerDepthStyle,
|
|
4
|
-
resolveKind,
|
|
5
|
-
resolveLiveKind,
|
|
6
|
-
topOpened,
|
|
7
|
-
} from "@src/lib/stacker";
|
|
8
|
-
import type { StackerItem } from "@src/lib/stacker";
|
|
9
|
-
import { describe, expect, it } from "vitest";
|
|
10
|
-
|
|
11
|
-
describe("resolveKind", () => {
|
|
12
|
-
it("uses the explicit variant when provided", () => {
|
|
13
|
-
expect(resolveKind("drawer", false)).toBe("drawer");
|
|
14
|
-
expect(resolveKind("modal", true)).toBe("modal");
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it("falls back to the viewport when no variant is set", () => {
|
|
18
|
-
expect(resolveKind(undefined, true)).toBe("drawer");
|
|
19
|
-
expect(resolveKind(undefined, false)).toBe("modal");
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
describe("resolveLiveKind", () => {
|
|
24
|
-
const drawerFirst: StackerItem[] = [
|
|
25
|
-
{ id: "a", opened: true, settings: { variant: "drawer" } },
|
|
26
|
-
{ id: "b", opened: true, settings: { variant: "modal" } },
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
it("returns null for an empty stack", () => {
|
|
30
|
-
expect(resolveLiveKind([], false)).toBeNull();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it("follows the first item's variant hint on desktop", () => {
|
|
34
|
-
expect(resolveLiveKind(drawerFirst, false)).toBe("drawer");
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("follows the viewport when the first item has no variant", () => {
|
|
38
|
-
const items: StackerItem[] = [{ id: "a", opened: true, settings: {} }];
|
|
39
|
-
expect(resolveLiveKind(items, true)).toBe("drawer");
|
|
40
|
-
expect(resolveLiveKind(items, false)).toBe("modal");
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("prefers a stack-level forced kind over the first item and viewport", () => {
|
|
44
|
-
const items: StackerItem[] = [{ id: "b", opened: true, settings: { variant: "modal" } }];
|
|
45
|
-
expect(resolveLiveKind(items, false, "drawer")).toBe("drawer");
|
|
46
|
-
expect(resolveLiveKind(items, true, "modal")).toBe("modal");
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
describe("topOpened", () => {
|
|
51
|
-
const items: StackerItem[] = [
|
|
52
|
-
{ id: "a", opened: true, settings: {} },
|
|
53
|
-
{ id: "b", opened: true, settings: {} },
|
|
54
|
-
{ id: "c", opened: false, settings: {} },
|
|
55
|
-
];
|
|
56
|
-
|
|
57
|
-
it("returns the top-most open item, skipping closing ones", () => {
|
|
58
|
-
expect(topOpened(items)?.id).toBe("b");
|
|
59
|
-
expect(topOpened([])).toBeUndefined();
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
describe("coveredEphemeral", () => {
|
|
64
|
-
it("returns open ephemeral items that would be covered by a new top", () => {
|
|
65
|
-
const items: StackerItem[] = [
|
|
66
|
-
{ id: "a", opened: true, settings: { ephemeral: true } },
|
|
67
|
-
{ id: "b", opened: true, settings: {} },
|
|
68
|
-
{ id: "c", opened: false, settings: { ephemeral: true } },
|
|
69
|
-
];
|
|
70
|
-
expect(coveredEphemeral(items, "new").map((i) => i.id)).toEqual(["a"]);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it("excludes the new top id even if that item is ephemeral", () => {
|
|
74
|
-
const items: StackerItem[] = [{ id: "a", opened: true, settings: { ephemeral: true } }];
|
|
75
|
-
expect(coveredEphemeral(items, "a")).toEqual([]);
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
describe("drawerDepthStyle", () => {
|
|
80
|
-
it("applies no transform at depth 0", () => {
|
|
81
|
-
expect(drawerDepthStyle("right", 0)).toEqual({
|
|
82
|
-
transform: "none",
|
|
83
|
-
transformOrigin: "center",
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it("pushes outward from the free edge and scales down per side", () => {
|
|
88
|
-
expect(drawerDepthStyle("right", 1)).toEqual({
|
|
89
|
-
transform: "translateX(-12px) scale(0.96)",
|
|
90
|
-
transformOrigin: "0% 50%",
|
|
91
|
-
});
|
|
92
|
-
expect(drawerDepthStyle("left", 1)).toEqual({
|
|
93
|
-
transform: "translateX(12px) scale(0.96)",
|
|
94
|
-
transformOrigin: "100% 50%",
|
|
95
|
-
});
|
|
96
|
-
expect(drawerDepthStyle("bottom", 2)).toEqual({
|
|
97
|
-
transform: "translateY(-24px) scale(0.92)",
|
|
98
|
-
transformOrigin: "50% 0%",
|
|
99
|
-
});
|
|
100
|
-
expect(drawerDepthStyle("top", 2)).toEqual({
|
|
101
|
-
transform: "translateY(24px) scale(0.92)",
|
|
102
|
-
transformOrigin: "50% 100%",
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
});
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { STACKER_INITIAL_STATE, type StackerState, stackerReducer } from "@src/lib/stacker";
|
|
2
|
-
import type { StackerItem } from "@src/lib/stacker";
|
|
3
|
-
import { describe, expect, it } from "vitest";
|
|
4
|
-
|
|
5
|
-
function item(id: string): StackerItem {
|
|
6
|
-
return { id, opened: true, settings: { title: id } };
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function open(state: StackerState, id: string): StackerState {
|
|
10
|
-
return stackerReducer(state, { type: "OPEN", item: item(id) });
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
describe("stackerReducer", () => {
|
|
14
|
-
it("appends overlays in order", () => {
|
|
15
|
-
let state = open(STACKER_INITIAL_STATE, "a");
|
|
16
|
-
state = open(state, "b");
|
|
17
|
-
expect(state.items.map((i) => i.id)).toEqual(["a", "b"]);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it("no-ops when reopening an already-open id", () => {
|
|
21
|
-
const state = open(STACKER_INITIAL_STATE, "a");
|
|
22
|
-
expect(open(state, "a")).toBe(state);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("revives a closing overlay in place and merges settings", () => {
|
|
26
|
-
let state = open(STACKER_INITIAL_STATE, "a");
|
|
27
|
-
state = stackerReducer(state, { type: "CLOSE", id: "a" });
|
|
28
|
-
expect(state.items[0].opened).toBe(false);
|
|
29
|
-
|
|
30
|
-
state = stackerReducer(state, {
|
|
31
|
-
type: "OPEN",
|
|
32
|
-
item: { id: "a", opened: true, settings: { title: "revived" } },
|
|
33
|
-
});
|
|
34
|
-
expect(state.items).toHaveLength(1);
|
|
35
|
-
expect(state.items[0].opened).toBe(true);
|
|
36
|
-
expect(state.items[0].settings.title).toBe("revived");
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("flips opened on CLOSE and is idempotent", () => {
|
|
40
|
-
const state = open(STACKER_INITIAL_STATE, "a");
|
|
41
|
-
const closed = stackerReducer(state, { type: "CLOSE", id: "a" });
|
|
42
|
-
expect(closed.items[0].opened).toBe(false);
|
|
43
|
-
expect(stackerReducer(closed, { type: "CLOSE", id: "a" })).toBe(closed);
|
|
44
|
-
expect(stackerReducer(state, { type: "CLOSE", id: "missing" })).toBe(state);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it("removes an item only on REMOVE", () => {
|
|
48
|
-
let state = open(STACKER_INITIAL_STATE, "a");
|
|
49
|
-
state = open(state, "b");
|
|
50
|
-
|
|
51
|
-
state = stackerReducer(state, { type: "REMOVE", id: "b" });
|
|
52
|
-
expect(state.items.map((i) => i.id)).toEqual(["a"]);
|
|
53
|
-
|
|
54
|
-
state = stackerReducer(state, { type: "REMOVE", id: "a" });
|
|
55
|
-
expect(state.items).toHaveLength(0);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("locks a founding forced variant until the stack empties", () => {
|
|
59
|
-
let state = stackerReducer(STACKER_INITIAL_STATE, {
|
|
60
|
-
type: "OPEN",
|
|
61
|
-
item: {
|
|
62
|
-
id: "ephemeral",
|
|
63
|
-
opened: true,
|
|
64
|
-
settings: { variant: "drawer", ephemeral: true },
|
|
65
|
-
},
|
|
66
|
-
});
|
|
67
|
-
expect(state.forcedKind).toBe("drawer");
|
|
68
|
-
|
|
69
|
-
state = stackerReducer(state, {
|
|
70
|
-
type: "OPEN",
|
|
71
|
-
item: { id: "next", opened: true, settings: { variant: "modal" } },
|
|
72
|
-
});
|
|
73
|
-
expect(state.forcedKind).toBe("drawer");
|
|
74
|
-
|
|
75
|
-
state = stackerReducer(state, { type: "REMOVE", id: "ephemeral" });
|
|
76
|
-
expect(state.items.map((i) => i.id)).toEqual(["next"]);
|
|
77
|
-
expect(state.forcedKind).toBe("drawer");
|
|
78
|
-
|
|
79
|
-
state = stackerReducer(state, { type: "REMOVE", id: "next" });
|
|
80
|
-
expect(state.forcedKind).toBeNull();
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it("does not lock kind when the founding overlay has no variant", () => {
|
|
84
|
-
const state = open(STACKER_INITIAL_STATE, "a");
|
|
85
|
-
expect(state.forcedKind).toBeNull();
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("closes every open item with CLOSE_ALL", () => {
|
|
89
|
-
let state = open(STACKER_INITIAL_STATE, "a");
|
|
90
|
-
state = open(state, "b");
|
|
91
|
-
const closed = stackerReducer(state, { type: "CLOSE_ALL" });
|
|
92
|
-
expect(closed.items.every((i) => !i.opened)).toBe(true);
|
|
93
|
-
expect(stackerReducer(closed, { type: "CLOSE_ALL" })).toBe(closed);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it("merges settings on UPDATE", () => {
|
|
97
|
-
const state = open(STACKER_INITIAL_STATE, "a");
|
|
98
|
-
const updated = stackerReducer(state, {
|
|
99
|
-
type: "UPDATE",
|
|
100
|
-
id: "a",
|
|
101
|
-
patch: { title: "renamed", withCloseButton: false },
|
|
102
|
-
});
|
|
103
|
-
expect(updated.items[0].settings.title).toBe("renamed");
|
|
104
|
-
expect(updated.items[0].settings.withCloseButton).toBe(false);
|
|
105
|
-
expect(stackerReducer(state, { type: "UPDATE", id: "missing", patch: {} })).toBe(state);
|
|
106
|
-
});
|
|
107
|
-
});
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { getLanguageName, getLanguageParser } from "@src/syntax/language";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Counts the error nodes a registered language produces for a source snippet.
|
|
6
|
-
* A clean parse is the prerequisite for any highlighting at all, so this is
|
|
7
|
-
* the cheapest signal that a grammar is configured for the syntax it claims
|
|
8
|
-
* to support.
|
|
9
|
-
*/
|
|
10
|
-
function errorCount(language: string, source: string): number {
|
|
11
|
-
const parser = getLanguageParser(language);
|
|
12
|
-
|
|
13
|
-
if (!parser) {
|
|
14
|
-
throw new Error(`No parser registered for "${language}"`);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
let errors = 0;
|
|
18
|
-
|
|
19
|
-
parser.parse(source).iterate({
|
|
20
|
-
enter: (node) => {
|
|
21
|
-
if (node.type.isError) errors++;
|
|
22
|
-
},
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
return errors;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const TSX_SOURCE = `export function Greeting({ name }: { name: string }) {
|
|
29
|
-
return <View><Text>{name}</Text></View>;
|
|
30
|
-
}`;
|
|
31
|
-
|
|
32
|
-
const TS_SOURCE = `interface User { id: string; age?: number }
|
|
33
|
-
const user: User = { id: "a" };`;
|
|
34
|
-
|
|
35
|
-
const JS_SOURCE = `const ok = a < b && c > d;
|
|
36
|
-
const shifted = (n) => n << 2 > 1;`;
|
|
37
|
-
|
|
38
|
-
describe("language registry", () => {
|
|
39
|
-
it("registers tsx and jsx", () => {
|
|
40
|
-
expect(getLanguageParser("tsx")).toBeDefined();
|
|
41
|
-
expect(getLanguageParser("jsx")).toBeDefined();
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("resolves tsx and jsx to their base language names", () => {
|
|
45
|
-
expect(getLanguageName("tsx")).toBe("TypeScript");
|
|
46
|
-
expect(getLanguageName("jsx")).toBe("JavaScript");
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it("parses JSX under jsx and tsx", () => {
|
|
50
|
-
expect(errorCount("jsx", "const a = <View><Text>hi</Text></View>;")).toBe(0);
|
|
51
|
-
expect(errorCount("tsx", TSX_SOURCE)).toBe(0);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it("parses TypeScript syntax under ts and tsx", () => {
|
|
55
|
-
expect(errorCount("ts", TS_SOURCE)).toBe(0);
|
|
56
|
-
expect(errorCount("tsx", TS_SOURCE)).toBe(0);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it("does not misread comparison operators as JSX in plain JavaScript", () => {
|
|
60
|
-
expect(errorCount("js", JS_SOURCE)).toBe(0);
|
|
61
|
-
expect(errorCount("ts", JS_SOURCE)).toBe(0);
|
|
62
|
-
});
|
|
63
|
-
});
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
[
|
|
2
|
-
{
|
|
3
|
-
"uiKitFileName": "Memory",
|
|
4
|
-
"symbolId": "25796:1314",
|
|
5
|
-
"exportNodeId": "21128:24795",
|
|
6
|
-
"figmaVectorName": "Memory_vector",
|
|
7
|
-
"figmaFrameName": "Memory"
|
|
8
|
-
},
|
|
9
|
-
{
|
|
10
|
-
"uiKitFileName": "Compute",
|
|
11
|
-
"symbolId": "29295:58314",
|
|
12
|
-
"exportNodeId": "29295:58314",
|
|
13
|
-
"figmaVectorName": "Memory_vector",
|
|
14
|
-
"figmaFrameName": "Memory",
|
|
15
|
-
"figmaLightNodeId": "29295:58316"
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
"uiKitFileName": "HealthChat",
|
|
19
|
-
"symbolId": "25796:1395",
|
|
20
|
-
"exportNodeId": "21128:25857",
|
|
21
|
-
"figmaVectorName": "Chatbot_vector",
|
|
22
|
-
"figmaFrameName": "Chatbot"
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
"uiKitFileName": "Binary",
|
|
26
|
-
"symbolId": "25796:1513",
|
|
27
|
-
"exportNodeId": "25796:1513",
|
|
28
|
-
"figmaVectorName": "Code blob (JSON)_vector",
|
|
29
|
-
"figmaFrameName": "Code blob (JSON)",
|
|
30
|
-
"figmaLightNodeId": "25796:2418"
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
"uiKitFileName": "Clock",
|
|
34
|
-
"symbolId": "25796:1318",
|
|
35
|
-
"exportNodeId": "25796:1318",
|
|
36
|
-
"figmaVectorName": "Flexible_vector",
|
|
37
|
-
"figmaFrameName": "Flexible",
|
|
38
|
-
"figmaLightNodeId": "25796:2536"
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
"uiKitFileName": "CommitQueue",
|
|
42
|
-
"symbolId": "25796:1323",
|
|
43
|
-
"exportNodeId": "25796:1323",
|
|
44
|
-
"figmaVectorName": "Hands On_vector",
|
|
45
|
-
"figmaFrameName": "Hands On",
|
|
46
|
-
"figmaLightNodeId": "25796:2600"
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
"uiKitFileName": "Expand",
|
|
50
|
-
"symbolId": "25796:1502",
|
|
51
|
-
"exportNodeId": "25796:1502",
|
|
52
|
-
"figmaVectorName": "Scale_vector",
|
|
53
|
-
"figmaFrameName": "Scale",
|
|
54
|
-
"figmaLightNodeId": "25796:2001"
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
"uiKitFileName": "Swatch",
|
|
58
|
-
"symbolId": "25796:1413",
|
|
59
|
-
"exportNodeId": "25796:1413",
|
|
60
|
-
"figmaVectorName": "Palette_vector",
|
|
61
|
-
"figmaFrameName": "Palette",
|
|
62
|
-
"figmaLightNodeId": "25796:3530"
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
"uiKitFileName": "File",
|
|
66
|
-
"symbolId": "25796:1511",
|
|
67
|
-
"exportNodeId": "21128:25051",
|
|
68
|
-
"figmaVectorName": "Document_vector",
|
|
69
|
-
"figmaFrameName": "Document",
|
|
70
|
-
"figmaLightNodeId": "25796:2030"
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
"uiKitFileName": "Document",
|
|
74
|
-
"symbolId": "25796:1375",
|
|
75
|
-
"exportNodeId": "25796:1375",
|
|
76
|
-
"figmaVectorName": "Document_vector",
|
|
77
|
-
"figmaFrameName": "Document",
|
|
78
|
-
"figmaLightNodeId": "25796:2030"
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
"uiKitFileName": "Learn",
|
|
82
|
-
"symbolId": "25796:1493",
|
|
83
|
-
"exportNodeId": "21128:25729",
|
|
84
|
-
"figmaVectorName": "Learn_vector",
|
|
85
|
-
"figmaFrameName": "Learn",
|
|
86
|
-
"figmaLightNodeId": "25796:2836"
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
"uiKitFileName": "Tutorials",
|
|
90
|
-
"symbolId": "25796:1397",
|
|
91
|
-
"exportNodeId": "21128:25910",
|
|
92
|
-
"figmaVectorName": "Chat Heart_vector",
|
|
93
|
-
"figmaFrameName": "tutorials",
|
|
94
|
-
"figmaLightNodeId": "25796:2725"
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
"uiKitFileName": "MediaFile",
|
|
98
|
-
"symbolId": "25796:1452",
|
|
99
|
-
"exportNodeId": "25796:1452",
|
|
100
|
-
"figmaVectorName": "Video file_vector",
|
|
101
|
-
"figmaFrameName": "Video file",
|
|
102
|
-
"figmaLightNodeId": "25796:2498"
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
"uiKitFileName": "MergeQueue",
|
|
106
|
-
"symbolId": "25796:1356",
|
|
107
|
-
"exportNodeId": "25796:1356",
|
|
108
|
-
"figmaVectorName": "Flexible_vector",
|
|
109
|
-
"figmaFrameName": "Merge queue",
|
|
110
|
-
"figmaLightNodeId": "25796:3507"
|
|
111
|
-
},
|
|
112
|
-
{
|
|
113
|
-
"uiKitFileName": "Monitoring",
|
|
114
|
-
"symbolId": "25796:1527",
|
|
115
|
-
"exportNodeId": "25796:1527",
|
|
116
|
-
"figmaVectorName": "_vector",
|
|
117
|
-
"figmaFrameName": "Monitoring",
|
|
118
|
-
"figmaLightNodeId": "25796:3056"
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
"uiKitFileName": "Sun",
|
|
122
|
-
"symbolId": "25796:1363",
|
|
123
|
-
"exportNodeId": "25796:1363",
|
|
124
|
-
"figmaVectorName": "Union_vector",
|
|
125
|
-
"figmaFrameName": "Sun",
|
|
126
|
-
"figmaLightNodeId": "25796:3526"
|
|
127
|
-
},
|
|
128
|
-
{
|
|
129
|
-
"uiKitFileName": "Timeline",
|
|
130
|
-
"symbolId": "25796:1365",
|
|
131
|
-
"exportNodeId": "25796:1365",
|
|
132
|
-
"figmaVectorName": "Bars_vector",
|
|
133
|
-
"figmaFrameName": "Bars"
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
"uiKitFileName": "HealthBubble",
|
|
137
|
-
"symbolId": "25796:1366",
|
|
138
|
-
"exportNodeId": "21128:26006",
|
|
139
|
-
"figmaVectorName": "Health Chat_vector",
|
|
140
|
-
"figmaFrameName": "Health Bubble",
|
|
141
|
-
"figmaLightNodeId": "25796:3522"
|
|
142
|
-
}
|
|
143
|
-
]
|
package/tools/icon-parser.ts
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { execSync } from "node:child_process";
|
|
2
|
-
import { exists, mkdtemp, readdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join, parse } from "node:path";
|
|
5
|
-
|
|
6
|
-
import { JSDOM } from "jsdom";
|
|
7
|
-
import { optimize } from "svgo";
|
|
8
|
-
|
|
9
|
-
if (!Bun.which("picosvg")) {
|
|
10
|
-
console.error(
|
|
11
|
-
"🚨 picosvg is not installed. Please install it: https://github.com/googlefonts/picosvg",
|
|
12
|
-
);
|
|
13
|
-
process.exit(1);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
if (!(await exists("icons.zip"))) {
|
|
17
|
-
console.error("🚨 icons.zip is not found. Please place it in the root of the project");
|
|
18
|
-
process.exit(2);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const iconsDir = await mkdtemp(join(tmpdir(), "icons-"));
|
|
22
|
-
const interDir = await mkdtemp(join(tmpdir(), "inter-"));
|
|
23
|
-
|
|
24
|
-
// Unzip the icons.zip file
|
|
25
|
-
try {
|
|
26
|
-
execSync(`unzip icons.zip -d "${iconsDir}"`);
|
|
27
|
-
} catch (error) {
|
|
28
|
-
console.error("Error unzipping icons.zip:", error.message);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Read all SVG files in the icons directory
|
|
32
|
-
const files = (await readdir(iconsDir)).filter((file) => file.endsWith(".svg")).sort();
|
|
33
|
-
|
|
34
|
-
let bundleCode = "// Generated by SurrealDB UI Kit. Do not modify manually.\n\n";
|
|
35
|
-
let pureCode = "// Generated by SurrealDB UI Kit. Do not modify manually.\n\n";
|
|
36
|
-
|
|
37
|
-
for (const file of files) {
|
|
38
|
-
const inputPath = join(iconsDir, file);
|
|
39
|
-
const outputPath = join(interDir, file);
|
|
40
|
-
execSync(`picosvg "${inputPath}" --output_file "${outputPath}"`);
|
|
41
|
-
|
|
42
|
-
const { name } = parse(file);
|
|
43
|
-
const svg = await readFile(outputPath, "utf8");
|
|
44
|
-
const optimized = optimize(svg, {
|
|
45
|
-
multipass: true,
|
|
46
|
-
plugins: [
|
|
47
|
-
{
|
|
48
|
-
name: "mergePaths",
|
|
49
|
-
params: {
|
|
50
|
-
force: true,
|
|
51
|
-
noSpaceAfterFlags: false,
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
],
|
|
55
|
-
}).data;
|
|
56
|
-
|
|
57
|
-
const dom = new JSDOM(optimized);
|
|
58
|
-
const paths = dom.window.document.querySelectorAll("path");
|
|
59
|
-
|
|
60
|
-
if (paths.length !== 1) {
|
|
61
|
-
throw new Error(`Icon ${name} has ${paths.length} paths, expected 1`);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const iconPath = paths[0].getAttribute("d");
|
|
65
|
-
|
|
66
|
-
bundleCode += `export const icon${name}: string = "${iconPath}";\n`;
|
|
67
|
-
pureCode += `export { icon${name} as ${name} } from "./icons";\n`;
|
|
68
|
-
|
|
69
|
-
console.log(`🔄 Converted ${name}`);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
await writeFile("./src/constants/icons.ts", bundleCode);
|
|
73
|
-
await writeFile("./src/constants/icons-pure.ts", pureCode);
|
|
74
|
-
|
|
75
|
-
console.log("✅ Updated icons.ts and icons-pure.ts!");
|
|
76
|
-
|
|
77
|
-
await rm(iconsDir, { recursive: true });
|
|
78
|
-
await rm(interDir, { recursive: true });
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
[
|
|
2
|
-
{ "uiKitFileName": "Sidekick", "exportNodeId": "22777:30583", "figmaFrameName": "Sidekick" },
|
|
3
|
-
{ "uiKitFileName": "Development", "exportNodeId": "25796:1453", "figmaFrameName": "Settings" },
|
|
4
|
-
{
|
|
5
|
-
"uiKitFileName": "CloudNodes",
|
|
6
|
-
"exportNodeId": "25796:1426",
|
|
7
|
-
"figmaFrameName": "cloud nodes"
|
|
8
|
-
},
|
|
9
|
-
{ "uiKitFileName": "Bot", "exportNodeId": "25796:1428", "figmaFrameName": "Agent / Bot" },
|
|
10
|
-
{ "uiKitFileName": "Agno", "exportNodeId": "21128:26168", "figmaFrameName": "Agno" },
|
|
11
|
-
{ "uiKitFileName": "Claude", "exportNodeId": "21128:26209", "figmaFrameName": "Claude" },
|
|
12
|
-
{ "uiKitFileName": "GitHub", "exportNodeId": "21128:26223", "figmaFrameName": "GitHub" },
|
|
13
|
-
{ "uiKitFileName": "Medium", "exportNodeId": "21128:26218", "figmaFrameName": "Medium" },
|
|
14
|
-
{ "uiKitFileName": "LangChain", "exportNodeId": "21128:26198", "figmaFrameName": "LangChain" },
|
|
15
|
-
{ "uiKitFileName": "Reddit", "exportNodeId": "21128:26190", "figmaFrameName": "Reddit" },
|
|
16
|
-
{ "uiKitFileName": "X", "exportNodeId": "21128:26194", "figmaFrameName": "X" },
|
|
17
|
-
{ "uiKitFileName": "MCP", "exportNodeId": "24452:1002", "figmaFrameName": "MCP" },
|
|
18
|
-
{ "uiKitFileName": "RAG", "exportNodeId": "21128:26228", "figmaFrameName": "RAG" },
|
|
19
|
-
{ "uiKitFileName": "Windsurf", "exportNodeId": "21128:26240", "figmaFrameName": "Windsurf" },
|
|
20
|
-
{ "uiKitFileName": "Instagram", "exportNodeId": "21128:26248", "figmaFrameName": "Instagram" },
|
|
21
|
-
{ "uiKitFileName": "Discord", "exportNodeId": "21128:26252", "figmaFrameName": "Discord" },
|
|
22
|
-
{ "uiKitFileName": "Zapier", "exportNodeId": "21128:26256", "figmaFrameName": "Zapier" },
|
|
23
|
-
{ "uiKitFileName": "Python", "exportNodeId": "21128:26096", "figmaFrameName": "Python" },
|
|
24
|
-
{ "uiKitFileName": "Cline", "exportNodeId": "21128:26114", "figmaFrameName": "Cline" },
|
|
25
|
-
{ "uiKitFileName": "Dev", "exportNodeId": "21128:26118", "figmaFrameName": "Dev" },
|
|
26
|
-
{ "uiKitFileName": "Rust", "exportNodeId": "21128:24875", "figmaFrameName": "Rust" },
|
|
27
|
-
{ "uiKitFileName": "Threads", "exportNodeId": "21128:26146", "figmaFrameName": "Threads" },
|
|
28
|
-
{ "uiKitFileName": "Zed", "exportNodeId": "21128:26150", "figmaFrameName": "Zed" },
|
|
29
|
-
{
|
|
30
|
-
"uiKitFileName": "StackOverflow",
|
|
31
|
-
"exportNodeId": "21128:26164",
|
|
32
|
-
"figmaFrameName": "Stack Overflow"
|
|
33
|
-
},
|
|
34
|
-
{ "uiKitFileName": "LinkedIn", "exportNodeId": "21128:26270", "figmaFrameName": "LinkedIn" },
|
|
35
|
-
{ "uiKitFileName": "Youtube", "exportNodeId": "21128:26274", "figmaFrameName": "YouTube" },
|
|
36
|
-
{ "uiKitFileName": "N8n", "exportNodeId": "21128:26131", "figmaFrameName": "n8n" },
|
|
37
|
-
{ "uiKitFileName": "Wasm", "exportNodeId": "21128:26139", "figmaFrameName": "WebAssembly" },
|
|
38
|
-
{
|
|
39
|
-
"uiKitFileName": "VisualStudioCode",
|
|
40
|
-
"exportNodeId": "21128:26178",
|
|
41
|
-
"figmaFrameName": "Visual Studio Code"
|
|
42
|
-
},
|
|
43
|
-
{ "uiKitFileName": "PDF", "exportNodeId": "25796:1324", "figmaFrameName": "Document / PDF" },
|
|
44
|
-
{
|
|
45
|
-
"uiKitFileName": "Javascript",
|
|
46
|
-
"exportNodeId": "23768:22323",
|
|
47
|
-
"figmaFrameName": "Javascript"
|
|
48
|
-
},
|
|
49
|
-
{ "uiKitFileName": "OpenAI", "exportNodeId": "24452:982", "figmaFrameName": "OpenAI" },
|
|
50
|
-
{
|
|
51
|
-
"uiKitFileName": "SurrealMCP",
|
|
52
|
-
"exportNodeId": "24452:1007",
|
|
53
|
-
"figmaFrameName": "MCP"
|
|
54
|
-
},
|
|
55
|
-
{ "uiKitFileName": "SurrealML", "exportNodeId": "25433:6610", "figmaFrameName": "SurrealML" },
|
|
56
|
-
{ "uiKitFileName": "YUApp", "exportNodeId": "21128:24571", "figmaFrameName": "YU app" }
|
|
57
|
-
]
|
package/tools/picto-compare.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Compare Figma light exports against existing ui-kit pictos using perceptual hash.
|
|
3
|
-
* Used to validate the picto-figma-map.json name mappings.
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* bun tools/picto-compare.ts <urls-manifest.json>
|
|
7
|
-
*
|
|
8
|
-
* Manifest format: [{ fileName: "PadlockClosed", nodeId, url }] for light versions
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { execSync } from "node:child_process";
|
|
12
|
-
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
13
|
-
import { join } from "node:path";
|
|
14
|
-
|
|
15
|
-
const PICTOS_DIR = join(import.meta.dir, "../src/assets/pictos");
|
|
16
|
-
const TMP = join(import.meta.dir, "../.picto-compare");
|
|
17
|
-
|
|
18
|
-
async function phash(path: string): Promise<string> {
|
|
19
|
-
return execSync(`magick "${path}" -colorspace Gray -resize 16x16! -depth 8 txt:-`, {
|
|
20
|
-
encoding: "utf8",
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function hashDistance(a: string, b: string): number {
|
|
25
|
-
const valsA = [...a.matchAll(/\\((\\d+),(\\d+)\\)\\s+#(\\w+)/g)].map((m) =>
|
|
26
|
-
Number.parseInt(m[3], 16),
|
|
27
|
-
);
|
|
28
|
-
const valsB = [...b.matchAll(/\\((\\d+),(\\d+)\\)\\s+#(\\w+)/g)].map((m) =>
|
|
29
|
-
Number.parseInt(m[3], 16),
|
|
30
|
-
);
|
|
31
|
-
let dist = 0;
|
|
32
|
-
for (let i = 0; i < Math.min(valsA.length, valsB.length); i++) {
|
|
33
|
-
dist += Math.abs(valsA[i] - valsB[i]);
|
|
34
|
-
}
|
|
35
|
-
return dist;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async function main() {
|
|
39
|
-
const manifest: { fileName: string; url: string }[] = JSON.parse(
|
|
40
|
-
await readFile(process.argv[2], "utf8"),
|
|
41
|
-
);
|
|
42
|
-
await mkdir(TMP, { recursive: true });
|
|
43
|
-
|
|
44
|
-
const results: { fileName: string; distance: number; matched: boolean }[] = [];
|
|
45
|
-
|
|
46
|
-
for (const { fileName, url } of manifest) {
|
|
47
|
-
const figmaPng = join(TMP, `${fileName}-figma.png`);
|
|
48
|
-
const res = await fetch(url);
|
|
49
|
-
await writeFile(figmaPng, await res.arrayBuffer());
|
|
50
|
-
|
|
51
|
-
const repoWebp = join(PICTOS_DIR, `${fileName}.webp`);
|
|
52
|
-
const repoPng = join(TMP, `${fileName}-repo.png`);
|
|
53
|
-
execSync(`magick "${repoWebp}" "${repoPng}"`);
|
|
54
|
-
|
|
55
|
-
const dist = hashDistance(await phash(figmaPng), await phash(repoPng));
|
|
56
|
-
results.push({ fileName, distance: dist, matched: dist < 5000 });
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const failed = results.filter((r) => !r.matched);
|
|
60
|
-
console.log(`Compared ${results.length}, mismatches: ${failed.length}`);
|
|
61
|
-
for (const f of failed) console.log(` ${f.fileName}: distance ${f.distance}`);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
main().catch(console.error);
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/** Merge partial/agent screenshot URL results into urls.json */
|
|
2
|
-
import { readdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
|
|
5
|
-
const PNG_DIR = join(import.meta.dir, "../.picto-gradient-png");
|
|
6
|
-
|
|
7
|
-
const batchFiles = (await readdir(PNG_DIR))
|
|
8
|
-
.filter((f) => f.startsWith("screenshot-batch-") && f.endsWith(".json"))
|
|
9
|
-
.sort((a, b) => Number(a.match(/\d+/)?.[0]) - Number(b.match(/\d+/)?.[0]));
|
|
10
|
-
|
|
11
|
-
const expected: { fileName: string; nodeId: string }[] = [];
|
|
12
|
-
for (const f of batchFiles) {
|
|
13
|
-
expected.push(...JSON.parse(await readFile(join(PNG_DIR, f), "utf8")));
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const resultFiles = [
|
|
17
|
-
"results-transparent-partial.json",
|
|
18
|
-
"results-partial.json",
|
|
19
|
-
...(await readdir(PNG_DIR))
|
|
20
|
-
.filter((f) => f.startsWith("results-agent-") && f.endsWith(".json"))
|
|
21
|
-
.sort(),
|
|
22
|
-
];
|
|
23
|
-
|
|
24
|
-
const byName = new Map<string, string>();
|
|
25
|
-
for (const rf of resultFiles) {
|
|
26
|
-
try {
|
|
27
|
-
const entries: { fileName: string; url: string }[] = JSON.parse(
|
|
28
|
-
await readFile(join(PNG_DIR, rf), "utf8"),
|
|
29
|
-
);
|
|
30
|
-
for (const e of entries) byName.set(e.fileName, e.url);
|
|
31
|
-
} catch {
|
|
32
|
-
// file may not exist yet
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const urls = expected.map(({ fileName }) => {
|
|
37
|
-
const url = byName.get(fileName);
|
|
38
|
-
if (!url) throw new Error(`Missing URL for ${fileName}`);
|
|
39
|
-
return { fileName, url };
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
await writeFile(join(PNG_DIR, "urls.json"), `${JSON.stringify(urls, null, 2)}\n`);
|
|
43
|
-
console.log(`Merged ${urls.length} entries into urls.json`);
|