GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — feat(platform): add the Paper design system and isolated workbench by emir-karabeg · Pull Request #9667 · simstudioai/sim Conversation
|
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
[Low impact] The changes stay within a new, standalone Platform workbench and repository tooling. The PR appears safe to merge, with two non-blocking workbench interaction fixes. FindingsSummaryAdds an isolated Platform workbench with shared controls, Paper recipes, theme foundations, fixture data, and design checks.
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| <input | ||
| aria-label={searchLabel} | ||
| placeholder={searchLabel} | ||
| value={query} | ||
| data-type='T060' | ||
| onChange={(event) => setQuery(event.target.value)} |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Pressing Enter in the SearchableSelect search box submits the surrounding dialog form. In the workbench’s “Default role” step, this calls advance() without choosing the matching role. The previous role stays selected. Keep Enter inside the picker while it is open rather than letting it submit the form.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| .viewport { | ||
| max-block-size: var(--spacing-154); | ||
| min-block-size: 0; | ||
| overflow: hidden; |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Catalog rows cannot be reached
This wrapper has only max-block-size, so the nested ScrollArea cannot resolve its percentage height against a definite parent height. It grows with its content, and the wrapper hides everything beyond the limit. For example, C1082 contains the twenty-row C1080 list, but its later rows cannot be scrolled into view. Give the wrapper a definite block-size, as the workbench chat list already does.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
15 issues found across 91 files
Confidence score: 3/5
scripts/sync-platform-recipes.ts emits numeric positions as unitless CSS lengths, which browsers discard. Add units to numeric values for dimensional properties.specimen.module.css gives the viewport only a maximum height, so the nested ScrollArea may never overflow or scroll. Give it a definite height.searchable-select.tsx drops the typed query when the user chooses create, so the consumer cannot create the requested item. Pass the current query to onCreate.workspace-fields.tsx does not retain or submit typed addresses, so they can be lost. Add a callback or form value for the entered addresses.
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/sim/platform/design-system/components/range-picker.tsx">
<violation number="1" location="apps/sim/platform/design-system/components/range-picker.tsx:33">
P3: A disabled picker is visually indistinguishable from an enabled one and still shows a pointer cursor, because `.trigger` has no `:disabled` rule. Add a `.trigger:disabled` rule in `range-picker.module.css` with `cursor: default` and `opacity: var(--opacity-disabled)`, matching the other disabled controls.</violation>
<violation number="2" location="apps/sim/platform/design-system/components/range-picker.tsx:64">
P3: Re-selecting the current option still calls `onValueChange`, so consumers get a change event with no change. Guard the state update and callback with `option !== value`, and still close the menu.</violation>
</file>
<file name="scripts/sync-platform-foundations.ts">
<violation number="1" location="scripts/sync-platform-foundations.ts:36">
P2: Any fourth argument other than `--check` silently runs the write path. Reject unknown modes so a mistyped `--check` cannot regenerate the files and report success.</violation>
<violation number="2" location="scripts/sync-platform-foundations.ts:52">
P2: The `>= 4` cutoff keeps 1–3px spacing and radius tokens in px, so they will not scale with the root font size while the rest of the scale does. The header only justifies keeping strokes in px. Scope the exemption to stroke types, or convert every scalable px value.</violation>
<violation number="3" location="scripts/sync-platform-foundations.ts:65">
P3: `tokens.css` is written before the type recipes are validated. If a type style is missing, the run throws after updating only `tokens.css`, leaving the two generated files out of sync. Build all outputs in memory, then write them together.</violation>
</file>
<file name="scripts/sync-platform-recipes.ts">
<violation number="1" location="scripts/sync-platform-recipes.ts:212">
P2: Numeric style literals are emitted as unitless CSS lengths, so browsers discard positions such as the generated `left: 16` and `top: 6`. Normalize numeric values with units for dimensional properties while preserving unitless properties and zero.</violation>
</file>
<file name="scripts/platform-variant-usage.ts">
<violation number="1" location="scripts/platform-variant-usage.ts:86">
P2: A local `const` holding JSX is walked only once, under the first prop signature that reaches it. The `seen.has(decl)` guard in the identifier branch blocks re-walks with different props, so variants passed through that local can be missed. Key the guard by the props signature as well as the declaration, or walk the initializer on every reference.</violation>
</file>
<file name="apps/sim/platform/preview/specimen.module.css">
<violation number="1" location="apps/sim/platform/preview/specimen.module.css:24">
P2: `.viewport` caps the height with `max-block-size`, which does not give the nested ScrollArea a definite height. Its `max-block-size: 100%` resolves to none, so the scroll region never overflows and the `overflow: hidden` here clips the remaining content with no way to reach it. Use `block-size` so the child percentage resolves against a definite height.</violation>
</file>
<file name="apps/sim/platform/design-system/styles/control-roles.css">
<violation number="1" location="apps/sim/platform/design-system/styles/control-roles.css:8">
P3: In light theme, `--control-danger-hover` and `--control-danger-pressed` both resolve to the same `--color-light-danger` as the resting `--control-danger`, so destructive buttons show no hover or pressed feedback. Map these to a distinct light-theme red shade, as the dark block already does for hover and pressed. Primary pressed in light has the same problem and should get a distinct value too.</violation>
</file>
<file name="apps/sim/platform/design-system/components/search-field.tsx">
<violation number="1" location="apps/sim/platform/design-system/components/search-field.tsx:34">
P2: The shared `event.shiftKey` guard also blocks `/`, even though `event.key === '/'` already pins the character. On layouts that type `/` with Shift (for example German or Spanish), the shortcut never fires. Limit the Shift exclusion to the `⌘K` branch.</violation>
</file>
<file name="apps/sim/platform/design-system/components/fields.tsx">
<violation number="1" location="apps/sim/platform/design-system/components/fields.tsx:71">
P2: Optional fields lose the "(optional)" marker for assistive tech. The `aria-label={label}` set by `Input`, `TextArea` and `Select` overrides the `<label>` text, so the marker is dropped from the control's name. Pass the optional-aware label to the control in `TextField`, `TextAreaField` and `SelectField`.</violation>
</file>
<file name="apps/sim/platform/preview/workbench.tsx">
<violation number="1" location="apps/sim/platform/preview/workbench.tsx:32">
P3: The theme and fixture-state links drop `destination`, so toggling theme or state resets a selected workspace section to Dashboard. `href()` carries `recipe` explicitly but not `destination`, while the specimen's tab links keep it. Carry `destination` through the same helper.</violation>
</file>
<file name="apps/sim/platform/design-system/components/searchable-select.tsx">
<violation number="1" location="apps/sim/platform/design-system/components/searchable-select.tsx:90">
P2: The create action cannot tell the consumer what to create. `onCreate` takes no arguments and the typed search text is never passed, so a creatable select loses the user's input. Pass the current query to `onCreate` and type it as `(value: string) => void`.</violation>
</file>
<file name="apps/sim/platform/design-system/components/controls.tsx">
<violation number="1" location="apps/sim/platform/design-system/components/controls.tsx:182">
P2: `AvatarGroup` already renders the first two people, so this overflow popover repeats them instead of listing only hidden members; pass `people.slice(visibleCount)` to `PeoplePopover`.</violation>
</file>
<file name="apps/sim/platform/design-system/components/workspace-fields.tsx">
<violation number="1" location="apps/sim/platform/design-system/components/workspace-fields.tsx:90">
P2: Typing an address never updates `recipients` or reaches the caller, and this input has no `name` for form submission. Add an add/change callback or expose a form value so entered addresses are retained.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Turn on auto-fix | Re-trigger cubic
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
| const scalable = new Set(['fontSize', 'lineHeight', 'container', 'spacing', 'breakpoint', 'radius']) | ||
| function tokenValue(token: Token) { | ||
| const size = /^(\d+(?:\.\d+)?)px$/.exec(token.value) | ||
| return size && scalable.has(token.type) && Number(size[1]) >= 4 |
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
P2: The >= 4 cutoff keeps 1–3px spacing and radius tokens in px, so they will not scale with the root font size while the rest of the scale does. The header only justifies keeping strokes in px. Scope the exemption to stroke types, or convert every scalable px value.
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At scripts/sync-platform-foundations.ts, line 52:
<comment>The `>= 4` cutoff keeps 1–3px spacing and radius tokens in px, so they will not scale with the root font size while the rest of the scale does. The header only justifies keeping strokes in px. Scope the exemption to stroke types, or convert every scalable px value.</comment>
<file context>
@@ -0,0 +1,101 @@
+const scalable = new Set(['fontSize', 'lineHeight', 'container', 'spacing', 'breakpoint', 'radius'])
+function tokenValue(token: Token) {
+ const size = /^(\d+(?:\.\d+)?)px$/.exec(token.value)
+ return size && scalable.has(token.type) && Number(size[1]) >= 4
+ ? `${Number(size[1]) / 16}rem`
+ : token.value
</file context>
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| const { typography }: { typography: TypeMaster[] } = JSON.parse(readFileSync(registryFile, 'utf8')) | ||
| const styles: Record<string, TypeStyle> = JSON.parse(readFileSync(stylesFile, 'utf8')) | ||
| const directory = path.resolve(import.meta.dirname, '../apps/sim/platform/design-system/styles') | ||
| const compare = mode === '--check' |
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
P2: Any fourth argument other than --check silently runs the write path. Reject unknown modes so a mistyped --check cannot regenerate the files and report success.
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At scripts/sync-platform-foundations.ts, line 36:
<comment>Any fourth argument other than `--check` silently runs the write path. Reject unknown modes so a mistyped `--check` cannot regenerate the files and report success.</comment>
<file context>
@@ -0,0 +1,101 @@
+const { typography }: { typography: TypeMaster[] } = JSON.parse(readFileSync(registryFile, 'utf8'))
+const styles: Record<string, TypeStyle> = JSON.parse(readFileSync(stylesFile, 'utf8'))
+const directory = path.resolve(import.meta.dirname, '../apps/sim/platform/design-system/styles')
+const compare = mode === '--check'
+function emit(filename: string, css: string) {
+ const file = path.join(directory, filename)
</file context>
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| if (!ts.isPropertyAssignment(prop)) continue | ||
| const value = prop.initializer | ||
| if (ts.isStringLiteral(value) || ts.isNumericLiteral(value)) | ||
| style[prop.name.getText(file)] = value.text |
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
P2: Numeric style literals are emitted as unitless CSS lengths, so browsers discard positions such as the generated left: 16 and top: 6. Normalize numeric values with units for dimensional properties while preserving unitless properties and zero.
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At scripts/sync-platform-recipes.ts, line 212:
<comment>Numeric style literals are emitted as unitless CSS lengths, so browsers discard positions such as the generated `left: 16` and `top: 6`. Normalize numeric values with units for dimensional properties while preserving unitless properties and zero.</comment>
<file context>
@@ -0,0 +1,630 @@
+ if (!ts.isPropertyAssignment(prop)) continue
+ const value = prop.initializer
+ if (ts.isStringLiteral(value) || ts.isNumericLiteral(value))
+ style[prop.name.getText(file)] = value.text
+ }
+ }
</file context>
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| } | ||
| } | ||
| function visit(node: ts.Node) { | ||
| if (ts.isFunctionDeclaration(node) || ts.isVariableDeclaration(node)) return |
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
P2: A local const holding JSX is walked only once, under the first prop signature that reaches it. The seen.has(decl) guard in the identifier branch blocks re-walks with different props, so variants passed through that local can be missed. Key the guard by the props signature as well as the declaration, or walk the initializer on every reference.
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At scripts/platform-variant-usage.ts, line 86:
<comment>A local `const` holding JSX is walked only once, under the first prop signature that reaches it. The `seen.has(decl)` guard in the identifier branch blocks re-walks with different props, so variants passed through that local can be missed. Key the guard by the props signature as well as the declaration, or walk the initializer on every reference.</comment>
<file context>
@@ -0,0 +1,187 @@
+ }
+ }
+ function visit(node: ts.Node) {
+ if (ts.isFunctionDeclaration(node) || ts.isVariableDeclaration(node)) return
+ if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) {
+ const decl = declaration(node.tagName)
</file context>
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| value={option} | ||
| selected={option === value} | ||
| onSelect={() => { | ||
| setValue(option) |
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
P3: Re-selecting the current option still calls onValueChange, so consumers get a change event with no change. Guard the state update and callback with option !== value, and still close the menu.
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/sim/platform/design-system/components/range-picker.tsx, line 64:
<comment>Re-selecting the current option still calls `onValueChange`, so consumers get a change event with no change. Guard the state update and callback with `option !== value`, and still close the menu.</comment>
<file context>
@@ -0,0 +1,75 @@
+ value={option}
+ selected={option === value}
+ onSelect={() => {
+ setValue(option)
+ setOpen(false)
+ onValueChange?.(option)
</file context>
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| type='button' | ||
| className={styles.trigger} | ||
| aria-label={value ? `${label}: ${value}` : label} | ||
| disabled={!options.length} |
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
P3: A disabled picker is visually indistinguishable from an enabled one and still shows a pointer cursor, because .trigger has no :disabled rule. Add a .trigger:disabled rule in range-picker.module.css with cursor: default and opacity: var(--opacity-disabled), matching the other disabled controls.
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/sim/platform/design-system/components/range-picker.tsx, line 33:
<comment>A disabled picker is visually indistinguishable from an enabled one and still shows a pointer cursor, because `.trigger` has no `:disabled` rule. Add a `.trigger:disabled` rule in `range-picker.module.css` with `cursor: default` and `opacity: var(--opacity-disabled)`, matching the other disabled controls.</comment>
<file context>
@@ -0,0 +1,75 @@
+ type='button'
+ className={styles.trigger}
+ aria-label={value ? `${label}: ${value}` : label}
+ disabled={!options.length}
+ data-size={size}
+ data-type='T060'
</file context>
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| `/* Shared foundation. Reference px become rem at a 16px root; strokes retain their measured width. */\n[data-platform-ui] {\n${ordered.map((t) => ` ${t.name}: ${tokenValue(t)};`).join('\n')}\n}` | ||
| ) | ||
|
|
||
| const recipes = new Map<string, string[]>() |
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
P3: tokens.css is written before the type recipes are validated. If a type style is missing, the run throws after updating only tokens.css, leaving the two generated files out of sync. Build all outputs in memory, then write them together.
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At scripts/sync-platform-foundations.ts, line 65:
<comment>`tokens.css` is written before the type recipes are validated. If a type style is missing, the run throws after updating only `tokens.css`, leaving the two generated files out of sync. Build all outputs in memory, then write them together.</comment>
<file context>
@@ -0,0 +1,101 @@
+ `/* Shared foundation. Reference px become rem at a 16px root; strokes retain their measured width. */\n[data-platform-ui] {\n${ordered.map((t) => ` ${t.name}: ${tokenValue(t)};`).join('\n')}\n}`
+)
+
+const recipes = new Map<string, string[]>()
+for (const { key, base } of typography) {
+ const type = styles[base]
</file context>
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| --control-primary-pressed: var(--color-light-text-strong); | ||
| --control-danger: var(--color-light-danger); | ||
| --control-danger-soft: var(--color-red-soft); | ||
| --control-danger-hover: var(--color-light-danger); |
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
P3: In light theme, --control-danger-hover and --control-danger-pressed both resolve to the same --color-light-danger as the resting --control-danger, so destructive buttons show no hover or pressed feedback. Map these to a distinct light-theme red shade, as the dark block already does for hover and pressed. Primary pressed in light has the same problem and should get a distinct value too.
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/sim/platform/design-system/styles/control-roles.css, line 8:
<comment>In light theme, `--control-danger-hover` and `--control-danger-pressed` both resolve to the same `--color-light-danger` as the resting `--control-danger`, so destructive buttons show no hover or pressed feedback. Map these to a distinct light-theme red shade, as the dark block already does for hover and pressed. Primary pressed in light has the same problem and should get a distinct value too.</comment>
<file context>
@@ -0,0 +1,45 @@
+ --control-primary-pressed: var(--color-light-text-strong);
+ --control-danger: var(--color-light-danger);
+ --control-danger-soft: var(--color-red-soft);
+ --control-danger-hover: var(--color-light-danger);
+ --control-danger-pressed: var(--color-light-danger);
+ --control-hover-border: var(--color-ink-20);
</file context>
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| const workspace = organization?.workspaces[0] | ||
| const href = (nextTheme: string, nextState: string) => { | ||
| const params = new URLSearchParams({ theme: nextTheme, state: nextState }) | ||
| if (recipe) params.set('recipe', recipe) |
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
P3: The theme and fixture-state links drop destination, so toggling theme or state resets a selected workspace section to Dashboard. href() carries recipe explicitly but not destination, while the specimen's tab links keep it. Carry destination through the same helper.
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/sim/platform/preview/workbench.tsx, line 32:
<comment>The theme and fixture-state links drop `destination`, so toggling theme or state resets a selected workspace section to Dashboard. `href()` carries `recipe` explicitly but not `destination`, while the specimen's tab links keep it. Carry `destination` through the same helper.</comment>
<file context>
@@ -0,0 +1,221 @@
+ const workspace = organization?.workspaces[0]
+ const href = (nextTheme: string, nextState: string) => {
+ const params = new URLSearchParams({ theme: nextTheme, state: nextState })
+ if (recipe) params.set('recipe', recipe)
+ return `/?${params}`
+ }
</file context>
Sorry, something went wrong.
There was an error while loading. Please reload this page.
94354c26
Deployed
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.