@duro-app/cli 2.0.1 → 3.1.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 (41) hide show
  1. package/README.md +49 -4
  2. package/dist/bin.js +15 -1
  3. package/dist/bin.js.map +1 -1
  4. package/dist/commands/hook.d.ts.map +1 -1
  5. package/dist/commands/hook.js +21 -0
  6. package/dist/commands/hook.js.map +1 -1
  7. package/dist/commands/mcp.d.ts +28 -1
  8. package/dist/commands/mcp.d.ts.map +1 -1
  9. package/dist/commands/mcp.js +22 -1
  10. package/dist/commands/mcp.js.map +1 -1
  11. package/dist/commands/mockup.d.ts +38 -0
  12. package/dist/commands/mockup.d.ts.map +1 -0
  13. package/dist/commands/mockup.js +436 -0
  14. package/dist/commands/mockup.js.map +1 -0
  15. package/dist/commands/skill.d.ts +9 -0
  16. package/dist/commands/skill.d.ts.map +1 -0
  17. package/dist/commands/skill.js +100 -0
  18. package/dist/commands/skill.js.map +1 -0
  19. package/dist/disambiguation.d.ts.map +1 -1
  20. package/dist/disambiguation.js +4 -2
  21. package/dist/disambiguation.js.map +1 -1
  22. package/dist/hook-script.d.ts +7 -6
  23. package/dist/hook-script.d.ts.map +1 -1
  24. package/dist/hook-script.js +6 -5
  25. package/dist/hook-script.js.map +1 -1
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +1 -0
  29. package/dist/index.js.map +1 -1
  30. package/dist/manifest.d.ts +2 -0
  31. package/dist/manifest.d.ts.map +1 -1
  32. package/dist/manifest.js +58 -1
  33. package/dist/manifest.js.map +1 -1
  34. package/dist/registry-types.d.ts +6 -0
  35. package/dist/registry-types.d.ts.map +1 -1
  36. package/dist/skill-template.d.ts +22 -0
  37. package/dist/skill-template.d.ts.map +1 -0
  38. package/dist/skill-template.js +119 -0
  39. package/dist/skill-template.js.map +1 -0
  40. package/package.json +5 -5
  41. package/registry.json +155 -8
package/registry.json CHANGED
@@ -2294,13 +2294,18 @@
2294
2294
  "importPath": "@duro-app/ui",
2295
2295
  "kind": "component",
2296
2296
  "meta": {
2297
- "description": "CSS grid layout. Supports fixed column count (1-6) or responsive auto-fit via minColumnWidth.",
2298
- "example": "// Responsive: columns auto-fit based on min width\n<Grid minColumnWidth=\"280px\" gap=\"md\">\n <Card>A</Card>\n <Card>B</Card>\n <Card>C</Card>\n</Grid>\n\n// Fixed: always 3 columns\n<Grid columns={3} gap=\"md\">\n <Card>A</Card>\n <Card>B</Card>\n <Card>C</Card>\n</Grid>",
2297
+ "description": "Grid layout. Columns are a count (1-6), a list of weights ([1, 2] for one-third / two-thirds), responsive auto-fit via minColumnWidth, or a named split layout (list/detail, nav/content) that collapses to one column on the width of its own container, so it is safe to nest and to place beside a DetailPanel. Weights rather than CSS template strings, so the same props render as CSS grid on web and as a wrapping flex row on native.",
2298
+ "example": "// Split: list ≥ 240px beside a detail pane, one column below md\n<Grid layout=\"split\" gap=\"lg\">\n <List.Root>…</List.Root>\n <Panel.Root>…</Panel.Root>\n</Grid>\n\n// Responsive: columns auto-fit based on min width\n<Grid minColumnWidth=\"280px\" gap=\"md\">\n <Card>A</Card>\n <Card>B</Card>\n <Card>C</Card>\n</Grid>\n\n// Fixed: always 3 columns\n<Grid columns={3} gap=\"md\">\n <Card>A</Card>\n <Card>B</Card>\n <Card>C</Card>\n</Grid>\n\n// Weighted: a one-third / two-thirds split (works on native too)\n<Grid columns={[1, 2]} gap=\"md\">\n <Card>Sidebar</Card>\n <Card>Content</Card>\n</Grid>",
2299
2299
  "relatedTo": [
2300
2300
  {
2301
2301
  "component": "Stack",
2302
2302
  "kind": "contrast",
2303
2303
  "relationship": "Grid for multi-column layouts; Stack for a single vertical column"
2304
+ },
2305
+ {
2306
+ "component": "split-pane",
2307
+ "kind": "composition",
2308
+ "relationship": "Grid layout=\"split\" with a List and a Panel is the split-pane recipe"
2304
2309
  }
2305
2310
  ],
2306
2311
  "whenNotToUse": [
@@ -2309,7 +2314,8 @@
2309
2314
  ],
2310
2315
  "whenToUse": [
2311
2316
  "Card grids, dashboard layouts, multi-column forms",
2312
- "Responsive layouts that should auto-adjust column count"
2317
+ "Responsive layouts that should auto-adjust column count",
2318
+ "A list/detail or nav/content screen — layout=\"split\" | \"split-wide\", never a hand-rolled gridTemplateColumns with its own @media; a split inside a split is fine, each collapses on its own room"
2313
2319
  ]
2314
2320
  },
2315
2321
  "name": "Grid",
@@ -2331,15 +2337,23 @@
2331
2337
  ]
2332
2338
  },
2333
2339
  {
2340
+ "description": "A column count (equal columns) or weights: `[1, 2]` gives a one-third / two-thirds split. Weights are the portable form of a track list — a CSS template string would be silently ignored on native.",
2334
2341
  "name": "columns",
2335
2342
  "required": false,
2336
- "type": "1 | 2 | 3 | 4 | 5 | 6"
2343
+ "type": "GridColumns"
2337
2344
  },
2338
2345
  {
2346
+ "description": "Responsive auto-fit: as many columns of at least this width as fit. Wins over `columns` when both are set. On native this approximates to cells of that minimum width that grow and wrap.",
2339
2347
  "name": "minColumnWidth",
2340
2348
  "required": false,
2341
2349
  "type": "string"
2342
2350
  },
2351
+ {
2352
+ "description": "A named responsive layout. `split` is list/detail (1:2, list ≥ 240px), `split-wide` is nav/content (1:3, nav ≥ 280px). Each collapses to one column on the width of its OWN container — `split` below `sm` (640px), `split-wide` below `md` (768px) — so a split nested inside another split, or beside an open DetailPanel, collapses when it runs out of room rather than when the window does. Wins over `columns` / `minColumnWidth`.",
2353
+ "name": "layout",
2354
+ "required": false,
2355
+ "type": "GridLayout"
2356
+ },
2343
2357
  {
2344
2358
  "name": "children",
2345
2359
  "required": true,
@@ -3304,11 +3318,21 @@
3304
3318
  "component": "SideNav",
3305
3319
  "kind": "composition",
3306
3320
  "relationship": "Often placed alongside PageShell"
3321
+ },
3322
+ {
3323
+ "component": "page-with-sidenav",
3324
+ "kind": "composition",
3325
+ "relationship": "PageShell with a SideNav rail is the page-with-sidenav recipe — copy it, do not lay it out by hand"
3326
+ },
3327
+ {
3328
+ "component": "admin-detail-page",
3329
+ "kind": "composition",
3330
+ "relationship": "PageShell with a record heading and Tabs is the admin-detail-page recipe"
3307
3331
  }
3308
3332
  ],
3309
3333
  "whenNotToUse": [
3310
3334
  "Nested section layouts — use Stack or Card",
3311
- "Sidebar layouts — combine with SideNav manually"
3335
+ "Sidebar layouts — start from the page-with-sidenav recipe (PageShell + Grid layout=\"split-wide\" + SideNav)"
3312
3336
  ],
3313
3337
  "whenToUse": [
3314
3338
  "Top-level page layout",
@@ -3840,6 +3864,11 @@
3840
3864
  "component": "PageShell",
3841
3865
  "kind": "composition",
3842
3866
  "relationship": "Often placed alongside PageShell"
3867
+ },
3868
+ {
3869
+ "component": "page-with-sidenav",
3870
+ "kind": "composition",
3871
+ "relationship": "SideNav beside routed content is the page-with-sidenav recipe"
3843
3872
  }
3844
3873
  ],
3845
3874
  "whenNotToUse": [
@@ -4741,6 +4770,11 @@
4741
4770
  "component": "SideNav",
4742
4771
  "kind": "contrast",
4743
4772
  "relationship": "Tabs switch content in-place; SideNav navigates between pages"
4773
+ },
4774
+ {
4775
+ "component": "admin-detail-page",
4776
+ "kind": "composition",
4777
+ "relationship": "Tabs as the facets of one record is the admin-detail-page recipe"
4744
4778
  }
4745
4779
  ],
4746
4780
  "whenNotToUse": [
@@ -6044,6 +6078,39 @@
6044
6078
  "Menu"
6045
6079
  ]
6046
6080
  },
6081
+ "admin-detail-page": {
6082
+ "export": "AdminDetailPageRecipe",
6083
+ "meta": {
6084
+ "description": "Admin detail page for one record: breadcrumb link, heading with status and actions, then Tabs whose panels hold the sections (the /admin/<collection>/:id shape).",
6085
+ "example": "<AdminDetailPageRecipe />",
6086
+ "whenNotToUse": [
6087
+ "One facet only — a PageShell with a Card is enough",
6088
+ "A collection page — use the data-table or split-pane recipe"
6089
+ ],
6090
+ "whenToUse": [
6091
+ "A record page with several facets — overview, members, approvals, audit",
6092
+ "Any page that would otherwise stack every section into one long scroll"
6093
+ ]
6094
+ },
6095
+ "name": "admin-detail-page",
6096
+ "peerDeps": [],
6097
+ "source": "import {PageShell, Tabs, Stack, Inline, Heading, Text, Badge, Button, LinkButton, Card, EmptyState} from '@duro-app/ui'\n\nfunction Header() {\n return (\n <Stack gap=\"sm\">\n <LinkButton href=\"#applications\" variant=\"secondary\" size=\"small\">\n ← Applications\n </LinkButton>\n <Inline gap=\"md\" align=\"center\" justify=\"between\">\n <Inline gap=\"sm\" align=\"center\">\n <Heading level={1}>Grafana</Heading>\n <Badge variant=\"success\">active</Badge>\n </Inline>\n <Inline gap=\"sm\">\n <Button variant=\"secondary\">Edit</Button>\n <Button variant=\"danger\">Retire</Button>\n </Inline>\n </Inline>\n </Stack>\n )\n}\n\nexport function AdminDetailPageRecipe() {\n return (\n <PageShell maxWidth=\"lg\" padding=\"md\" header={<Header />}>\n <Tabs.Root defaultValue=\"overview\">\n <Tabs.List>\n <Tabs.Tab value=\"overview\">Overview</Tabs.Tab>\n <Tabs.Tab value=\"members\">Members</Tabs.Tab>\n <Tabs.Tab value=\"approvals\">Approvals</Tabs.Tab>\n <Tabs.Tab value=\"audit\">Audit</Tabs.Tab>\n </Tabs.List>\n\n <Tabs.Panel value=\"overview\">\n <Stack gap=\"lg\">\n <Card>\n <Stack gap=\"sm\">\n <Heading level={3}>About</Heading>\n <Text color=\"muted\">\n Dashboards for the platform team. Owned by platform, reachable at grafana.example.\n </Text>\n </Stack>\n </Card>\n <Card>\n <Stack gap=\"sm\">\n <Heading level={3}>Access</Heading>\n <Text>3 roles · 2 entitlements · 41 grants</Text>\n </Stack>\n </Card>\n </Stack>\n </Tabs.Panel>\n\n <Tabs.Panel value=\"members\">\n <EmptyState message=\"No members yet.\" action={<Button>Add member</Button>} />\n </Tabs.Panel>\n <Tabs.Panel value=\"approvals\">\n <EmptyState message=\"Every grant is auto-approved. Add a gate to require a decision.\" />\n </Tabs.Panel>\n <Tabs.Panel value=\"audit\">\n <EmptyState message=\"Nothing recorded for this application.\" />\n </Tabs.Panel>\n </Tabs.Root>\n </PageShell>\n )\n}\n",
6098
+ "sourcePath": "docs/ai/recipes/admin-detail-page.recipe.tsx",
6099
+ "title": "Admin Detail Page",
6100
+ "usesComponents": [
6101
+ "Badge",
6102
+ "Button",
6103
+ "Card",
6104
+ "EmptyState",
6105
+ "Heading",
6106
+ "Inline",
6107
+ "LinkButton",
6108
+ "PageShell",
6109
+ "Stack",
6110
+ "Tabs",
6111
+ "Text"
6112
+ ]
6113
+ },
6047
6114
  "data-table": {
6048
6115
  "export": "DataTableRecipe",
6049
6116
  "meta": {
@@ -6150,6 +6217,36 @@
6150
6217
  "Stack"
6151
6218
  ]
6152
6219
  },
6220
+ "page-with-sidenav": {
6221
+ "export": "PageWithSideNavRecipe",
6222
+ "meta": {
6223
+ "description": "Page with a side navigation rail: SideNav in the narrow column of Grid layout=\"split-wide\", the routed content in the wide one; the rail stacks above the content when the page is narrower than md.",
6224
+ "example": "<PageWithSideNavRecipe />",
6225
+ "whenNotToUse": [
6226
+ "Three or four destinations — Tabs inside a PageShell read better than a rail",
6227
+ "The left column is data, not navigation — use the split-pane recipe"
6228
+ ],
6229
+ "whenToUse": [
6230
+ "The admin or settings area of an app — several destinations, one shell",
6231
+ "Anywhere PageShell + SideNav were about to be combined by hand"
6232
+ ]
6233
+ },
6234
+ "name": "page-with-sidenav",
6235
+ "peerDeps": [],
6236
+ "source": "import {useState} from 'react'\nimport {Grid, PageShell, SideNav, Icon, Stack, Heading, Text, Card} from '@duro-app/ui'\n\nconst SECTIONS: Record<string, {title: string; body: string}> = {\n identities: {title: 'Identities', body: 'People and service accounts known to the system.'},\n grants: {title: 'Grants', body: 'Who holds what, and since when.'},\n applications: {title: 'Applications', body: 'Everything access is granted to.'},\n plugins: {title: 'Plugins', body: 'Push and pull connectors. Rarely visited, so it is a Group.'},\n}\n\nexport function PageWithSideNavRecipe() {\n const [section, setSection] = useState('identities')\n const current = SECTIONS[section] ?? SECTIONS.identities\n\n return (\n <PageShell maxWidth=\"lg\" padding=\"md\">\n <Grid layout=\"split-wide\" gap=\"xl\">\n <SideNav.Root value={section} onValueChange={setSection}>\n <SideNav.Section label=\"People & access\">\n <SideNav.Item value=\"identities\" icon={<Icon name=\"users\" size=\"md\" />}>\n Identities\n </SideNav.Item>\n <SideNav.Item value=\"grants\" icon={<Icon name=\"key\" size=\"md\" />}>\n Grants\n </SideNav.Item>\n </SideNav.Section>\n <SideNav.Section label=\"Catalog\">\n <SideNav.Item value=\"applications\" icon={<Icon name=\"box\" size=\"md\" />}>\n Applications\n </SideNav.Item>\n </SideNav.Section>\n <SideNav.Group label=\"Advanced\" defaultExpanded={false}>\n <SideNav.Item value=\"plugins\" icon={<Icon name=\"plug\" size=\"md\" />}>\n Plugins\n </SideNav.Item>\n </SideNav.Group>\n </SideNav.Root>\n\n <Stack gap=\"lg\">\n <Heading level={1}>{current.title}</Heading>\n <Card>\n <Text color=\"muted\">{current.body}</Text>\n </Card>\n </Stack>\n </Grid>\n </PageShell>\n )\n}\n",
6237
+ "sourcePath": "docs/ai/recipes/page-with-sidenav.recipe.tsx",
6238
+ "title": "Page With Sidenav",
6239
+ "usesComponents": [
6240
+ "Card",
6241
+ "Grid",
6242
+ "Heading",
6243
+ "Icon",
6244
+ "PageShell",
6245
+ "SideNav",
6246
+ "Stack",
6247
+ "Text"
6248
+ ]
6249
+ },
6153
6250
  "settings-page": {
6154
6251
  "export": "SettingsPageRecipe",
6155
6252
  "meta": {
@@ -6185,6 +6282,38 @@
6185
6282
  "Tabs",
6186
6283
  "Text"
6187
6284
  ]
6285
+ },
6286
+ "split-pane": {
6287
+ "export": "SplitPaneRecipe",
6288
+ "meta": {
6289
+ "description": "List/detail split: a selectable List beside a Panel showing the selection, on Grid layout=\"split\" (list ≥ 240px, one column when the board is narrower than sm).",
6290
+ "example": "<SplitPaneRecipe />",
6291
+ "whenNotToUse": [
6292
+ "The detail is a short-lived inspection — use DetailPanel over the list instead",
6293
+ "The left side is navigation, not data — use the page-with-sidenav recipe"
6294
+ ],
6295
+ "whenToUse": [
6296
+ "A screen that browses a collection and inspects one item at a time (applications, identities, certificates)",
6297
+ "Anywhere a hand-rolled gridTemplateColumns with its own @media is about to be written"
6298
+ ]
6299
+ },
6300
+ "name": "split-pane",
6301
+ "peerDeps": [],
6302
+ "source": "import {useState} from 'react'\nimport {Grid, List, Panel, Stack, Inline, Heading, Text, Badge, Button, EmptyState} from '@duro-app/ui'\n\nconst ITEMS = [\n {id: 'grafana', name: 'Grafana', owner: 'platform', status: 'active'},\n {id: 'vault', name: 'Vault', owner: 'security', status: 'active'},\n {id: 'legacy-crm', name: 'Legacy CRM', owner: 'sales', status: 'retiring'},\n] as const\n\nexport function SplitPaneRecipe() {\n const [selected, setSelected] = useState<string | null>(ITEMS[0].id)\n const item = ITEMS.find((candidate) => candidate.id === selected)\n\n return (\n <Grid layout=\"split\" gap=\"lg\">\n <List.Root selectionMode=\"single\" aria-label=\"Applications\">\n {ITEMS.map((candidate) => (\n <List.Item\n key={candidate.id}\n selected={candidate.id === selected}\n onClick={() => setSelected(candidate.id)}\n >\n <List.Content>\n <List.Text>{candidate.name}</List.Text>\n <List.Description>Owned by {candidate.owner}</List.Description>\n </List.Content>\n <List.Actions>\n <Badge variant={candidate.status === 'active' ? 'success' : 'warning'}>\n {candidate.status}\n </Badge>\n </List.Actions>\n </List.Item>\n ))}\n <List.Empty>No applications.</List.Empty>\n </List.Root>\n\n <Panel.Root bordered>\n {item ? (\n <>\n <Panel.Header>\n <Heading level={2}>{item.name}</Heading>\n <Inline gap=\"sm\">\n <Button variant=\"secondary\" size=\"small\">\n Edit\n </Button>\n <Button variant=\"danger\" size=\"small\">\n Retire\n </Button>\n </Inline>\n </Panel.Header>\n <Panel.Body>\n <Stack gap=\"md\">\n <Text>\n Owner: <Text weight=\"medium\">{item.owner}</Text>\n </Text>\n <Text color=\"muted\">\n Everything about the selection lives here: fields, history, related records.\n </Text>\n </Stack>\n </Panel.Body>\n </>\n ) : (\n <Panel.Body>\n <EmptyState message=\"Select an application to see its details.\" />\n </Panel.Body>\n )}\n </Panel.Root>\n </Grid>\n )\n}\n",
6303
+ "sourcePath": "docs/ai/recipes/split-pane.recipe.tsx",
6304
+ "title": "Split Pane",
6305
+ "usesComponents": [
6306
+ "Badge",
6307
+ "Button",
6308
+ "EmptyState",
6309
+ "Grid",
6310
+ "Heading",
6311
+ "Inline",
6312
+ "List",
6313
+ "Panel",
6314
+ "Stack",
6315
+ "Text"
6316
+ ]
6188
6317
  }
6189
6318
  },
6190
6319
  "rules": {
@@ -6211,17 +6340,34 @@
6211
6340
  "severity": "warn"
6212
6341
  },
6213
6342
  {
6214
- "description": "Prefer design tokens over raw hex colors and px values in css.create styles",
6343
+ "description": "Disallow raw px breakpoints in media-query strings; use breakpoints.<key>",
6344
+ "fixable": "autofix",
6345
+ "id": "duro/no-raw-breakpoint-query",
6346
+ "messages": {
6347
+ "rawBreakpointQuery": "{{value}}px is a raw breakpoint in a media query. Use `breakpoints.{{token}}` from @duro-app/tokens/tokens/breakpoints.css (the scale is {{scale}}px)."
6348
+ },
6349
+ "severity": "error"
6350
+ },
6351
+ {
6352
+ "description": "Prefer design tokens over raw colors, lengths, breakpoints, type, shadows and motion in css.create styles",
6215
6353
  "fixable": "suggestion",
6216
6354
  "id": "duro/no-raw-design-values",
6217
6355
  "messages": {
6356
+ "offScaleRadius": "{{value}} on `{{property}}` is off the radius scale ({{scale}}px). Use the nearest `radii.*` token from {{pkg}}/tokens/spacing.css, or disable this line with the reason.",
6357
+ "offScaleSpacing": "{{value}} on `{{property}}` is off the spacing scale ({{scale}}px). Use the nearest `spacing.*` token from {{pkg}}/tokens/spacing.css, or disable this line with the reason.",
6358
+ "rawBreakpoint": "{{value}}px in `{{key}}` is a raw breakpoint. Use `breakpoints.{{token}}` from {{pkg}}/tokens/breakpoints.css in a template-literal key (the scale is {{scale}}px).",
6218
6359
  "rawColor": "'{{value}}' is not in the palette. Use a semantic token from {{pkg}}/tokens/colors.css instead of a raw color.",
6219
6360
  "rawColorToken": "'{{value}}' is the {{token}} token. Use `colors.{{token}}` from {{pkg}}/tokens/colors.css so it follows the theme.",
6361
+ "rawDuration": "'{{value}}' on `{{property}}` is a raw duration. Use `duration.{{token}}` from {{pkg}}/tokens/motion.css.",
6362
+ "rawEasing": "'{{value}}' on `{{property}}` is a raw easing. Use `easing.{{token}}` from {{pkg}}/tokens/motion.css.",
6363
+ "rawFontSize": "{{value}} on `fontSize` is the {{group}}.{{token}} token. Use it from {{pkg}}/tokens/typography.css.",
6364
+ "rawFontWeight": "{{value}} on `fontWeight` is the typography.{{token}} token. Use it from {{pkg}}/tokens/typography.css.",
6220
6365
  "rawRadius": "{{value}} on `{{property}}` is the {{token}} radius token. Use `radii.{{token}}` from {{pkg}}/tokens/spacing.css.",
6366
+ "rawShadow": "'{{value}}' on `boxShadow` is a raw shadow. Use `shadows.{{token}}` from {{pkg}}/tokens/shadows.css.",
6221
6367
  "rawSpacing": "{{value}} on `{{property}}` is the {{token}} spacing token. Use `spacing.{{token}}` from {{pkg}}/tokens/spacing.css.",
6222
6368
  "replaceWithToken": "Replace with {{replacement}}"
6223
6369
  },
6224
- "severity": "warn"
6370
+ "severity": "error"
6225
6371
  },
6226
6372
  {
6227
6373
  "description": "Require react-strict-dom html.* elements instead of raw HTML JSX intrinsics",
@@ -6810,7 +6956,8 @@
6810
6956
  "exportName": "typography",
6811
6957
  "importPath": "@duro-app/tokens/tokens/typography.css"
6812
6958
  }
6813
- }
6959
+ },
6960
+ "mockupCss": "/*\n * GENERATED by scripts/generate-mockup-css.mjs — do not edit.\n *\n * The design tokens as resolved values, for HTML that loads no stylesheet\n * (mockup artboards, static previews). Same names as vars.css; where vars.css\n * follows the live theme, this file carries every theme and switches on\n * <html data-theme=\"light|high-contrast\">.\n *\n * Inside the component library and the apps, never use this: go through\n * css.create() and the token imports, or vars.css from plain CSS.\n */\n:root {\n color-scheme: dark;\n /* colors */\n --duro-color-accent: #6aaffc;\n --duro-color-accent-contrast: #000000;\n --duro-color-accent-hover: #93c5fd;\n --duro-color-bg: #0f0f0f;\n --duro-color-bg-card: #1a1a1a;\n --duro-color-bg-card-hover: #242424;\n --duro-color-border: #333333;\n --duro-color-error: #f87171;\n --duro-color-error-bg: rgba(248, 113, 113, 0.1);\n --duro-color-error-border: rgba(248, 113, 113, 0.3);\n --duro-color-error-contrast: #000000;\n --duro-color-error-hover: #fca5a5;\n --duro-color-error-text: #fca5a5;\n --duro-color-info: #6aaffc;\n --duro-color-info-bg: rgba(106, 175, 252, 0.1);\n --duro-color-info-border: rgba(106, 175, 252, 0.3);\n --duro-color-info-text: #93c5fd;\n --duro-color-success: #22c55e;\n --duro-color-success-bg: rgba(34, 197, 94, 0.1);\n --duro-color-success-border: rgba(34, 197, 94, 0.3);\n --duro-color-success-text: #86efac;\n --duro-color-text: #e5e5e5;\n --duro-color-text-muted: #b0b0b0;\n --duro-color-warning: #fbbf24;\n --duro-color-warning-bg: rgba(251, 191, 36, 0.1);\n --duro-color-warning-border: rgba(251, 191, 36, 0.3);\n --duro-color-warning-text: #fde68a;\n /* shadows */\n --duro-shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.5);\n --duro-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4);\n --duro-shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.3);\n /* spacing */\n --duro-spacing-lg: 24px;\n --duro-spacing-md: 16px;\n --duro-spacing-ms: 12px;\n --duro-spacing-sm: 8px;\n --duro-spacing-xl: 32px;\n --duro-spacing-xs: 4px;\n --duro-spacing-xxl: 48px;\n --duro-spacing-xxxl: 64px;\n /* radii */\n --duro-radius-full: 9999px;\n --duro-radius-lg: 16px;\n --duro-radius-md: 12px;\n --duro-radius-sm: 8px;\n --duro-radius-xs: 4px;\n /* layoutSpacing */\n --duro-layout-spacing-container-lg: 32px;\n --duro-layout-spacing-container-md: 24px;\n --duro-layout-spacing-container-sm: 16px;\n --duro-layout-spacing-inline-lg: 24px;\n --duro-layout-spacing-inline-md: 16px;\n --duro-layout-spacing-inline-sm: 8px;\n --duro-layout-spacing-inline-xs: 4px;\n --duro-layout-spacing-stack-lg: 24px;\n --duro-layout-spacing-stack-md: 16px;\n --duro-layout-spacing-stack-sm: 8px;\n --duro-layout-spacing-stack-xl: 48px;\n --duro-layout-spacing-stack-xs: 4px;\n /* typography */\n --duro-typography-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n --duro-typography-font-family-mono: \"SF Mono\", \"Fira Code\", \"Fira Mono\", \"Roboto Mono\", \"Courier New\", monospace;\n --duro-typography-font-size-heading: 1.5rem;\n --duro-typography-font-size-lg: 1.125rem;\n --duro-typography-font-size-md: 1rem;\n --duro-typography-font-size-sm: 0.875rem;\n --duro-typography-font-size-xl: 1.25rem;\n --duro-typography-font-size-xs: 0.75rem;\n --duro-typography-font-weight-bold: 700;\n --duro-typography-font-weight-medium: 500;\n --duro-typography-font-weight-normal: 400;\n --duro-typography-font-weight-semibold: 600;\n --duro-typography-line-height: 1.5;\n /* typeScale */\n --duro-type-scale-display-lg: clamp(3.5rem, 2rem + 3.5vw, 4.5rem);\n --duro-type-scale-display-md: clamp(2.75rem, 1.75rem + 2.5vw, 3.75rem);\n --duro-type-scale-display-sm: clamp(2.25rem, 1.5rem + 2vw, 3rem);\n --duro-type-scale-font-size1: 0.75rem;\n --duro-type-scale-font-size2: 0.8125rem;\n --duro-type-scale-font-size3: 0.875rem;\n --duro-type-scale-font-size4: 1rem;\n --duro-type-scale-font-size5: 1.125rem;\n --duro-type-scale-font-size6: 1.25rem;\n --duro-type-scale-font-size7: 1.5rem;\n --duro-type-scale-font-size8: 1.875rem;\n --duro-type-scale-font-size9: 2.25rem;\n --duro-type-scale-letter-spacing-normal: 0;\n --duro-type-scale-letter-spacing-tight: -0.02em;\n --duro-type-scale-letter-spacing-wide: 0.04em;\n --duro-type-scale-line-height1: 1rem;\n --duro-type-scale-line-height2: 1.25rem;\n --duro-type-scale-line-height3: 1.25rem;\n --duro-type-scale-line-height4: 1.5rem;\n --duro-type-scale-line-height5: 1.5rem;\n --duro-type-scale-line-height6: 1.75rem;\n --duro-type-scale-line-height7: 2rem;\n --duro-type-scale-line-height8: 2.25rem;\n --duro-type-scale-line-height9: 2.75rem;\n /* duration */\n --duro-duration-base: 200ms;\n --duro-duration-fast: 150ms;\n --duro-duration-instant: 0ms;\n --duro-duration-slow: 280ms;\n /* easing */\n --duro-easing-ease-in: cubic-bezier(0.72, 0, 0.68, 0.28);\n --duro-easing-ease-out: cubic-bezier(0.32, 0.72, 0, 1);\n --duro-easing-standard: ease;\n /* breakpoints */\n --duro-breakpoint-lg: 1024px;\n --duro-breakpoint-md: 768px;\n --duro-breakpoint-sm: 640px;\n --duro-breakpoint-xl: 1280px;\n --duro-breakpoint-xs: 480px;\n}\n\n:root[data-theme=\"light\"] {\n color-scheme: light;\n /* colors */\n --duro-color-accent: #1e40af;\n --duro-color-accent-contrast: #ffffff;\n --duro-color-accent-hover: #1a3799;\n --duro-color-bg: #ffffff;\n --duro-color-bg-card: #f5f5f5;\n --duro-color-bg-card-hover: #ebebeb;\n --duro-color-border: #d4d4d4;\n --duro-color-error: #991b1b;\n --duro-color-error-bg: rgba(153, 27, 27, 0.08);\n --duro-color-error-border: rgba(153, 27, 27, 0.3);\n --duro-color-error-contrast: #ffffff;\n --duro-color-error-hover: #7f1d1d;\n --duro-color-error-text: #7f1d1d;\n --duro-color-info: #1e40af;\n --duro-color-info-bg: rgba(30, 64, 175, 0.08);\n --duro-color-info-border: rgba(30, 64, 175, 0.3);\n --duro-color-info-text: #1e40af;\n --duro-color-success: #166534;\n --duro-color-success-bg: rgba(22, 101, 52, 0.08);\n --duro-color-success-border: rgba(22, 101, 52, 0.3);\n --duro-color-success-text: #14532d;\n --duro-color-text: #1a1a1a;\n --duro-color-text-muted: #4a4a4a;\n --duro-color-warning: #92400e;\n --duro-color-warning-bg: rgba(146, 64, 14, 0.08);\n --duro-color-warning-border: rgba(146, 64, 14, 0.3);\n --duro-color-warning-text: #78350f;\n /* shadows */\n --duro-shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.16);\n --duro-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.12);\n --duro-shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.08);\n}\n\n:root[data-theme=\"high-contrast\"] {\n color-scheme: dark;\n /* colors */\n --duro-color-accent: #60a5fa;\n --duro-color-accent-contrast: #000000;\n --duro-color-accent-hover: #93c5fd;\n --duro-color-bg: #000000;\n --duro-color-bg-card: #111111;\n --duro-color-bg-card-hover: #1a1a1a;\n --duro-color-border: #555555;\n --duro-color-error: #f87171;\n --duro-color-error-bg: rgba(248, 113, 113, 0.15);\n --duro-color-error-border: rgba(248, 113, 113, 0.5);\n --duro-color-error-contrast: #000000;\n --duro-color-error-hover: #fca5a5;\n --duro-color-error-text: #fca5a5;\n --duro-color-info: #60a5fa;\n --duro-color-info-bg: rgba(96, 165, 250, 0.15);\n --duro-color-info-border: rgba(96, 165, 250, 0.5);\n --duro-color-info-text: #bfdbfe;\n --duro-color-success: #4ade80;\n --duro-color-success-bg: rgba(74, 222, 128, 0.15);\n --duro-color-success-border: rgba(74, 222, 128, 0.5);\n --duro-color-success-text: #86efac;\n --duro-color-text: #ffffff;\n --duro-color-text-muted: #b0b0b0;\n --duro-color-warning: #fcd34d;\n --duro-color-warning-bg: rgba(252, 211, 77, 0.15);\n --duro-color-warning-border: rgba(252, 211, 77, 0.5);\n --duro-color-warning-text: #fef08a;\n /* shadows */\n --duro-shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.8);\n --duro-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.7);\n --duro-shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.6);\n}\n"
6814
6961
  },
6815
6962
  "unions": {
6816
6963
  "AlertVariant": [