@vendoai/vendo 0.4.3 → 0.4.4

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 (46) hide show
  1. package/dist/capability-misses.js +2 -3
  2. package/dist/cli/cloud/client.d.ts +2 -5
  3. package/dist/cli/cloud/client.js +4 -4
  4. package/dist/cli/dep-versions.d.ts +1 -1
  5. package/dist/cli/dep-versions.js +1 -1
  6. package/dist/cli/doctor-codes.d.ts +4 -0
  7. package/dist/cli/doctor-codes.js +4 -0
  8. package/dist/cli/doctor.js +52 -1
  9. package/dist/cli/extract/apply.js +1 -1
  10. package/dist/cli/extract/delegate.js +9 -1
  11. package/dist/cli/extract/extraction.d.ts +4 -0
  12. package/dist/cli/extract/extraction.js +29 -2
  13. package/dist/cli/extract/harness.d.ts +12 -0
  14. package/dist/cli/extract/harness.js +3 -0
  15. package/dist/cli/extract/stages.js +5 -1
  16. package/dist/cli/init-scaffolds.d.ts +10 -1
  17. package/dist/cli/init-scaffolds.js +93 -1
  18. package/dist/cli/init.d.ts +3 -3
  19. package/dist/cli/init.js +53 -5
  20. package/dist/cli/playground/app/fake-client.js +11 -5
  21. package/dist/cli/playground/app/main.js +23 -13
  22. package/dist/cli/playground/app/scenario-mount.js +4 -1
  23. package/dist/cli/playground/app/theme-editor.js +27 -27
  24. package/dist/cli/playground/bundle.gen.d.ts +1 -1
  25. package/dist/cli/playground/bundle.gen.js +1 -1
  26. package/dist/cli/playground/embed-bundle.gen.d.ts +1 -1
  27. package/dist/cli/playground/embed-bundle.gen.js +1 -1
  28. package/dist/cli/shared.d.ts +1 -1
  29. package/dist/cli/shared.js +1 -1
  30. package/dist/cli/sync.d.ts +1 -1
  31. package/dist/cli/sync.js +1 -1
  32. package/dist/cloud-apps.js +2 -1
  33. package/dist/cloud-key-fetch.d.ts +16 -0
  34. package/dist/cloud-key-fetch.js +40 -0
  35. package/dist/cloud-tools.js +2 -1
  36. package/dist/connections.js +2 -2
  37. package/dist/dev-creds/model-edge.d.ts +21 -0
  38. package/dist/dev-creds/model-edge.js +24 -0
  39. package/dist/hosted-store.js +2 -1
  40. package/dist/refine.js +2 -1
  41. package/dist/sandbox.js +2 -2
  42. package/dist/server.d.ts +1 -1
  43. package/dist/server.js +56 -22
  44. package/dist/wire/shared.d.ts +2 -2
  45. package/dist/wire/shared.js +1 -1
  46. package/package.json +20 -11
package/dist/cli/init.js CHANGED
@@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
2
2
  import { mkdir, mkdtemp, readFile, rm } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
4
  import { dirname, join, relative, resolve, sep } from "node:path";
5
- import { mergeOverrides, vendoSync, } from "@vendoai/actions";
5
+ import { mergeOverrides, vendoSync } from "@vendoai/actions/sync";
6
6
  import { createInterface } from "node:readline/promises";
7
7
  import { stdin, stdout } from "node:process";
8
8
  import { scrubErrorDetail } from "@vendoai/telemetry";
@@ -15,7 +15,7 @@ import { ENV_KEY_VARS, resolveDevCredential, describeDevCredential } from "../de
15
15
  import { detectFramework, detectVendoWiring } from "./framework.js";
16
16
  import { resolveScaffoldAuth } from "./init-auth.js";
17
17
  import { ensureProviderDeps } from "./provider-deps.js";
18
- import { expressServerSource, registrySource, routeSource, serverActionsModuleSource, VENDO_ENV_EXAMPLE, vendoRootWrapperSource, wiringServerActions, } from "./init-scaffolds.js";
18
+ import { customServerSource, expressServerSource, registrySource, routeSource, serverActionsModuleSource, VENDO_ENV_EXAMPLE, vendoRootWrapperSource, wiringServerActions, } from "./init-scaffolds.js";
19
19
  import { createPrettyOutput, plainSelect, usePrettyOutput } from "./pretty.js";
20
20
  import { contrastingText } from "./theme/color.js";
21
21
  import { applyThemeDraft, extractTheme as extractThemeSlots, validateSlotValue, } from "./theme/extract-theme.js";
@@ -333,6 +333,16 @@ async function manualWiringLines(root, layout, withRegistry) {
333
333
  `${wrap} // around your client root (see vendo/server for the imports; <VendoOverlay /> is the visible launcher + panel)`,
334
334
  ];
335
335
  }
336
+ if (layout.kind === "custom") {
337
+ const wrap = withRegistry
338
+ ? `<VendoRoot components={registry} theme={theme}>…<VendoOverlay /></VendoRoot>`
339
+ : `<VendoRoot theme={theme}>…<VendoOverlay /></VendoRoot>`;
340
+ return [
341
+ `Route your runtime's requests through the generated module — Cloudflare Workers: export default { fetch: (request, env) => handleVendoRequest(request, env) };`,
342
+ `${wrap} // around your client root (see vendo/server for the imports; <VendoOverlay /> is the visible launcher + panel)`,
343
+ `Set VENDO_BASE_URL to the deployed origin (credential forwarding fails closed without it).`,
344
+ ];
345
+ }
336
346
  if (layout.kind === "wired" || layout.kind === "already")
337
347
  return [];
338
348
  if (layout.kind === "overlay-missing") {
@@ -495,7 +505,12 @@ export function wireNextLayout(source, wrapperSpecifier) {
495
505
  }
496
506
  async function buildPlan(options, confirmAuth, selectAuth) {
497
507
  const root = resolve(options.targetDir);
498
- const framework = options.framework ?? await detectFramework(root);
508
+ // "unknown" detection lands on the runtime-neutral custom scaffold — the
509
+ // safe default that exists now (guessing the Next layout into a Worker
510
+ // host was the field failure; the non-interactive guard still demands an
511
+ // explicit --framework so agents never inherit a guess silently).
512
+ const detected = options.framework ?? await detectFramework(root);
513
+ const framework = detected === "unknown" ? "custom" : detected;
499
514
  const changes = [];
500
515
  let authAdvice = null;
501
516
  let authWired = null;
@@ -503,7 +518,40 @@ async function buildPlan(options, confirmAuth, selectAuth) {
503
518
  let registryPath = null;
504
519
  let withRegistry = false;
505
520
  let layout = { kind: "manual" };
506
- if (framework === "express") {
521
+ if (framework === "custom") {
522
+ layout = { kind: "custom" };
523
+ const wiring = await detectVendoWiring(root);
524
+ if (!wiring.server || !wiring.client) {
525
+ const typescript = await exists(join(root, "tsconfig.json"));
526
+ const server = join(root, "vendo", typescript ? "server.ts" : "server.mjs");
527
+ const registryFile = join(root, "vendo", typescript ? "registry.tsx" : "registry.mjs");
528
+ const registryBefore = await readOptional(registryFile);
529
+ const serverBefore = await readOptional(server);
530
+ // Same ownership rules as the Express branch: init composes only when
531
+ // it CREATES the composition, and the registry regenerates only for a
532
+ // consumer that uses it.
533
+ const scaffolding = serverBefore === null && !wiring.server;
534
+ const registryPlanned = registryBefore === null
535
+ && (scaffolding || serverBefore?.includes("./registry") === true);
536
+ if (registryPlanned) {
537
+ const path = relative(root, registryFile);
538
+ const registryAfter = registrySource(typescript ? "tsx" : "mjs");
539
+ changes.push({ absolute: registryFile, path, before: null, after: registryAfter, diff: diff(path, null, registryAfter) });
540
+ registryPath = path;
541
+ }
542
+ if (scaffolding) {
543
+ const path = relative(root, server);
544
+ const auth = await resolveScaffoldAuth(root, path, options.auth, confirmAuth, selectAuth);
545
+ const serverAfter = customServerSource(typescript, auth.wired);
546
+ changes.push({ absolute: server, path, before: null, after: serverAfter, diff: diff(path, null, serverAfter) });
547
+ authAdvice = auth.advice;
548
+ authWired = auth.wired;
549
+ compositionPath = path;
550
+ }
551
+ withRegistry = registryBefore !== null || registryPlanned;
552
+ }
553
+ }
554
+ else if (framework === "express") {
507
555
  layout = { kind: "express" };
508
556
  const wiring = await detectVendoWiring(root);
509
557
  if (!wiring.server || !wiring.client) {
@@ -773,7 +821,7 @@ export async function runInit(options) {
773
821
  if (options.framework === undefined && (options.yes === true || !interactive)
774
822
  && await detectFramework(root) === "unknown") {
775
823
  output.error("Framework not detected (no next or express dependency in package.json) and this run cannot ask. " +
776
- "Pass --framework. Example: vendo init --yes --framework next");
824
+ "Pass --framework. Examples: vendo init --yes --framework next · --framework custom (any Web-standard runtime: Cloudflare Workers, Bun, Hono, ...)");
777
825
  return 1;
778
826
  }
779
827
  // (No stdin-TTY guard on these defaults, unlike the star ask's: an unshown
@@ -72,12 +72,18 @@ export function createFakeClient(fixtures) {
72
72
  },
73
73
  connections: {
74
74
  list: async () => [...state.connections],
75
- // The scripted catalog mirrors the scenarios' explicit connectors prop;
76
- // surfaces resolving in auto mode see the same two toolkits.
75
+ // A realistic host catalog: the toolkits the scenarios act through
76
+ // (slack, github) plus the breadth a real Composio-brokered host
77
+ // exposes, so the connect tray demos search + scroll + real brand
78
+ // marks (every slug below has a real logo on logos.composio.dev).
77
79
  catalog: async () => [
78
- { toolkit: "slack", connector: "composio" },
79
- { toolkit: "github", connector: "composio" },
80
- ],
80
+ "slack", "github", "gmail", "googlecalendar", "googledrive",
81
+ "googlesheets", "notion", "linear", "jira", "confluence", "asana",
82
+ "trello", "clickup", "monday", "todoist", "hubspot", "salesforce",
83
+ "stripe", "shopify", "intercom", "zendesk", "airtable", "dropbox",
84
+ "figma", "discord", "zoom", "outlook", "calendly", "mailchimp",
85
+ "typeform",
86
+ ].map((toolkit) => ({ toolkit, connector: "composio" })),
81
87
  initiate: async ({ toolkit, connector }) => ({
82
88
  id: `conn_${toolkit}_new`,
83
89
  connector: connector ?? "composio",
@@ -6,27 +6,34 @@ import { ScenarioMount } from "./scenario-mount.js";
6
6
  import { scenarios } from "./scenarios.js";
7
7
  import { ThemeEditor, useGoogleFont } from "./theme-editor.js";
8
8
  import { decodeThemeParam, encodeThemeParam } from "./theme-state.js";
9
+ /* Vendo brand shell (brand kit): Porcelain #FAFAF8, Ink #17171A, Ultramarine
10
+ #4338CA, Onest (its @font-face rides in via the chrome stylesheet). The
11
+ STAGE column deliberately has no fixed background — App paints it with the
12
+ active theme's colors.background so every chrome surface sits on its own
13
+ canvas edge-to-edge instead of printing an abrupt theme-colored rectangle
14
+ on the shell. */
9
15
  const SHELL_CSS = `
10
16
  :root { color-scheme: light; }
11
17
  * { box-sizing: border-box; }
12
- body { margin: 0; font-family: Inter, system-ui, -apple-system, sans-serif; background: #f3ede2; color: #14151a; }
18
+ body { margin: 0; font-family: Onest, Inter, system-ui, -apple-system, sans-serif; background: #fafaf8; color: #17171a; }
13
19
  .pg-shell { display: grid; grid-template-columns: 250px minmax(0, 1fr); min-height: 100vh; }
14
- .pg-nav { border-right: 1px solid #e3ddd2; background: #faf6ee; padding: 18px 14px; position: sticky; top: 0; height: 100vh; overflow-y: auto; }
20
+ .pg-nav { border-right: 1px solid rgba(23, 23, 26, 0.08); background: #fafaf8; padding: 18px 14px; position: sticky; top: 0; height: 100vh; overflow-y: auto; }
15
21
  .pg-brand { font-size: 15px; font-weight: 700; letter-spacing: -0.01em; margin: 4px 6px 2px; }
16
- .pg-brand small { display: block; font-weight: 500; font-size: 11px; color: #8a8b92; margin-top: 3px; }
17
- .pg-group { font-size: 10.5px; font-weight: 650; text-transform: uppercase; letter-spacing: 0.08em; color: #8a8b92; margin: 18px 6px 6px; }
18
- .pg-link { display: block; padding: 7px 10px; border-radius: 8px; font-size: 13px; color: #34353b; text-decoration: none; }
19
- .pg-link:hover { background: #f0e9dc; }
20
- .pg-link[aria-current="page"] { background: #14151a; color: #fffdf9; }
21
- .pg-main { padding: 26px 30px 60px; min-width: 0; }
22
+ .pg-brand-mark { color: #4338ca; }
23
+ .pg-brand small { display: block; font-weight: 500; font-size: 11px; color: #6f6f78; margin-top: 3px; }
24
+ .pg-group { font-size: 10.5px; font-weight: 650; text-transform: uppercase; letter-spacing: 0.08em; color: #85858f; margin: 18px 6px 6px; }
25
+ .pg-link { display: block; padding: 7px 10px; border-radius: 8px; font-size: 13px; color: #3a3a41; text-decoration: none; transition: background .12s, color .12s; }
26
+ .pg-link:hover { background: rgba(67, 56, 202, 0.07); color: #17171a; }
27
+ .pg-link[aria-current="page"] { background: #17171a; color: #fafaf8; }
28
+ .pg-main { padding: 26px 30px 60px; min-width: 0; transition: background .2s, color .2s; }
22
29
  @media (max-width: 640px) {
23
30
  .pg-shell { grid-template-columns: 1fr; }
24
- .pg-nav { position: static; height: auto; border-right: none; border-bottom: 1px solid #e3ddd2; }
31
+ .pg-nav { position: static; height: auto; border-right: none; border-bottom: 1px solid rgba(23, 23, 26, 0.08); }
25
32
  .pg-main { padding: 18px 16px 80px; }
26
33
  }
27
34
  .pg-head { max-width: 860px; margin-bottom: 20px; }
28
35
  .pg-head h1 { font-size: 19px; letter-spacing: -0.01em; margin: 0 0 6px; }
29
- .pg-head p { font-size: 13.5px; color: #5c5d64; line-height: 1.55; margin: 0; }
36
+ .pg-head p { font-size: 13.5px; opacity: 0.72; line-height: 1.55; margin: 0; }
30
37
  .pg-stage { max-width: 860px; }
31
38
  .pg-embed { padding: 0; }
32
39
  `;
@@ -95,12 +102,15 @@ function App() {
95
102
  const embed = new URLSearchParams(window.location.search).has("embed");
96
103
  const [theme, setTheme] = useThemeState(embed);
97
104
  if (embed) {
98
- return (_jsx("div", { className: "pg-embed", children: _jsx(ScenarioMount, { scenario: scenario, theme: theme }, scenario.id) }));
105
+ return (
106
+ // Same host-canvas rule as the stage: the embed page (the phone iframe's
107
+ // whole viewport) wears the theme background so the surface blends.
108
+ _jsx("div", { className: "pg-embed", style: { background: theme.colors.background, minHeight: "100vh" }, children: _jsx(ScenarioMount, { scenario: scenario, theme: theme }, scenario.id) }));
99
109
  }
100
110
  const groups = [...new Set(scenarios.map((entry) => entry.group))];
101
- return (_jsxs("div", { className: "pg-shell", children: [_jsxs("nav", { className: "pg-nav", "aria-label": "Scenarios", children: [_jsxs("div", { className: "pg-brand", children: ["Vendo playground", _jsx("small", { children: "every surface \u00B7 scripted data \u00B7 no model key" })] }), groups.map((group) => (_jsxs("div", { children: [_jsx("div", { className: "pg-group", children: group }), scenarios
111
+ return (_jsxs("div", { className: "pg-shell", children: [_jsxs("nav", { className: "pg-nav", "aria-label": "Scenarios", children: [_jsxs("div", { className: "pg-brand", children: [_jsx("span", { className: "pg-brand-mark", children: "vendo" }), " playground", _jsx("small", { children: "every surface \u00B7 scripted data \u00B7 no model key" })] }), groups.map((group) => (_jsxs("div", { children: [_jsx("div", { className: "pg-group", children: group }), scenarios
102
112
  .filter((entry) => entry.group === group)
103
- .map((entry) => (_jsx("a", { className: "pg-link", href: `#${entry.id}`, "aria-current": entry.id === scenario.id ? "page" : undefined, children: entry.title }, entry.id)))] }, group)))] }), _jsxs("main", { className: "pg-main", children: [_jsxs("header", { className: "pg-head", children: [_jsx("h1", { children: scenario.title }), _jsx("p", { children: scenario.description })] }), _jsx("div", { className: "pg-stage", children: _jsx(ScenarioMount, { scenario: scenario, theme: theme }, scenario.id) })] }), _jsx(ThemeEditor, { theme: theme, onChange: setTheme })] }));
113
+ .map((entry) => (_jsx("a", { className: "pg-link", href: `#${entry.id}`, "aria-current": entry.id === scenario.id ? "page" : undefined, children: entry.title }, entry.id)))] }, group)))] }), _jsxs("main", { className: "pg-main", style: { background: theme.colors.background, color: theme.colors.text }, children: [_jsxs("header", { className: "pg-head", children: [_jsx("h1", { children: scenario.title }), _jsx("p", { children: scenario.description })] }), _jsx("div", { className: "pg-stage", children: _jsx(ScenarioMount, { scenario: scenario, theme: theme }, scenario.id) })] }), _jsx(ThemeEditor, { theme: theme, onChange: setTheme })] }));
104
114
  }
105
115
  const style = document.createElement("style");
106
116
  style.textContent = SHELL_CSS;
@@ -44,5 +44,8 @@ export function ScenarioMount({ scenario, theme, root }) {
44
44
  const client = useMemo(() => createFakeClient((scenario.fixtures ?? playgroundFixtures)()), [scenario]);
45
45
  const transport = useMemo(() => (scenario.script ? new ScriptedTransport(scenario.script, { speed: scenario.speed ?? 1 }) : undefined), [scenario]);
46
46
  useAutoSend(scenario, root);
47
- return (_jsx(VendoProvider, { client: client, transport: transport, theme: theme, tools: playgroundToolMeta, connectors: [{ toolkit: "slack", label: "Slack" }, { toolkit: "github", label: "GitHub" }], children: scenario.render() }));
47
+ // No explicit `connectors` prop: the surfaces resolve the auto catalog from
48
+ // the fake client (fake-client.ts), so the tray demos search + scroll over
49
+ // a realistic host's toolkit breadth.
50
+ return (_jsx(VendoProvider, { client: client, transport: transport, theme: theme, tools: playgroundToolMeta, children: scenario.render() }));
48
51
  }
@@ -5,51 +5,51 @@ const EDITOR_CSS = `
5
5
  /* Above the chrome's own overlay/takeover layers (they top out at
6
6
  2147483100) — the editor is harness tooling and must stay reachable
7
7
  while the overlay scenario is open. */
8
- .te-card, .te-pill { position: fixed; top: 16px; right: 16px; z-index: 2147483200; font-family: Inter, system-ui, sans-serif; }
9
- .te-card { width: 302px; max-height: calc(100vh - 32px); display: flex; flex-direction: column; background: #faf6ee; border: 1px solid #e3ddd2; border-radius: 14px; box-shadow: 0 18px 44px rgba(20, 21, 26, 0.18); transform-origin: top right; transition: transform 320ms cubic-bezier(0.2, 0.8, 0.2, 1), opacity 320ms cubic-bezier(0.2, 0.8, 0.2, 1); }
8
+ .te-card, .te-pill { position: fixed; top: 16px; right: 16px; z-index: 2147483200; font-family: Onest, Inter, system-ui, sans-serif; }
9
+ .te-card { width: 302px; max-height: calc(100vh - 32px); display: flex; flex-direction: column; background: #fafaf8; border: 1px solid rgba(23, 23, 26, 0.1); border-radius: 14px; box-shadow: 0 18px 44px rgba(23, 23, 26, 0.18); transform-origin: top right; transition: transform 320ms cubic-bezier(0.2, 0.8, 0.2, 1), opacity 320ms cubic-bezier(0.2, 0.8, 0.2, 1); }
10
10
  .te-card[data-closed="true"] { transform: scale(0.6) translateY(-8px); opacity: 0; pointer-events: none; }
11
- .te-pill { display: inline-flex; align-items: center; gap: 7px; background: #14151a; color: #fffdf9; border: none; border-radius: 999px; font: 600 12.5px Inter, sans-serif; padding: 9px 15px; cursor: pointer; box-shadow: 0 10px 26px rgba(20, 21, 26, 0.25); opacity: 0; pointer-events: none; transition: opacity 320ms cubic-bezier(0.2, 0.8, 0.2, 1); }
11
+ .te-pill { display: inline-flex; align-items: center; gap: 7px; background: #17171a; color: #fafaf8; border: none; border-radius: 999px; font: 600 12.5px Onest, Inter, sans-serif; padding: 9px 15px; cursor: pointer; box-shadow: 0 10px 26px rgba(23, 23, 26, 0.25); opacity: 0; pointer-events: none; transition: opacity 320ms cubic-bezier(0.2, 0.8, 0.2, 1); }
12
12
  .te-pill[data-shown="true"] { opacity: 1; pointer-events: auto; }
13
- .te-dot { width: 13px; height: 13px; border-radius: 50%; background: conic-gradient(#e7683c, #f5c644, #55a06f, #2f5af5, #7c6cff, #e7683c); border: 1px solid rgba(255, 255, 255, 0.4); }
13
+ .te-dot { width: 13px; height: 13px; border-radius: 50%; background: conic-gradient(#4338ca, #818cf8, #22d3ee, #4338ca); border: 1px solid rgba(255, 255, 255, 0.4); }
14
14
  .te-grab { display: none; }
15
- .te-head { display: flex; align-items: center; justify-content: space-between; padding: 12px 14px 10px; border-bottom: 1px solid #e3ddd2; flex-shrink: 0; }
16
- .te-head b { font-size: 12.5px; color: #14151a; }
17
- .te-head small { display: block; font-weight: 500; font-size: 10.5px; color: #8a8b92; margin-top: 2px; }
18
- .te-x { border: none; background: none; color: #8a8b92; font-size: 14px; cursor: pointer; padding: 2px 6px; border-radius: 6px; line-height: 1; }
19
- .te-x:hover { background: #f0e9dc; color: #14151a; }
20
- .te-body { overflow-y: auto; padding: 10px 14px 12px; font-size: 12.5px; color: #14151a; }
15
+ .te-head { display: flex; align-items: center; justify-content: space-between; padding: 12px 14px 10px; border-bottom: 1px solid rgba(23, 23, 26, 0.1); flex-shrink: 0; }
16
+ .te-head b { font-size: 12.5px; color: #17171a; }
17
+ .te-head small { display: block; font-weight: 500; font-size: 10.5px; color: #6f6f78; margin-top: 2px; }
18
+ .te-x { border: none; background: none; color: #6f6f78; font-size: 14px; cursor: pointer; padding: 2px 6px; border-radius: 6px; line-height: 1; }
19
+ .te-x:hover { background: rgba(67, 56, 202, 0.08); color: #17171a; }
20
+ .te-body { overflow-y: auto; padding: 10px 14px 12px; font-size: 12.5px; color: #17171a; }
21
21
  .te-sec { margin-bottom: 14px; }
22
- .te-label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: #8a8b92; margin-bottom: 7px; }
22
+ .te-label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: #85858f; margin-bottom: 7px; }
23
23
  .te-presets { display: flex; flex-wrap: wrap; gap: 6px; }
24
- .te-chip { display: inline-flex; align-items: center; gap: 6px; border: 1px solid #e3ddd2; background: #fffdf9; border-radius: 999px; padding: 4px 10px 4px 5px; font: 600 11.5px Inter, sans-serif; cursor: pointer; color: #14151a; }
24
+ .te-chip { display: inline-flex; align-items: center; gap: 6px; border: 1px solid rgba(23, 23, 26, 0.1); background: #ffffff; border-radius: 999px; padding: 4px 10px 4px 5px; font: 600 11.5px Onest, Inter, sans-serif; cursor: pointer; color: #17171a; }
25
25
  .te-chip .te-swatch { width: 16px; height: 16px; border-radius: 50%; border: 1px solid rgba(0, 0, 0, 0.12); }
26
- .te-chip[aria-pressed="true"] { border-color: #14151a; box-shadow: inset 0 0 0 1px #14151a; }
26
+ .te-chip[aria-pressed="true"] { border-color: #4338ca; box-shadow: inset 0 0 0 1px #4338ca; }
27
27
  .te-crow { display: flex; align-items: center; gap: 8px; margin-bottom: 5px; }
28
- .te-crow input[type="color"] { width: 24px; height: 24px; border: 1px solid #e3ddd2; border-radius: 7px; padding: 1px; background: #fffdf9; cursor: pointer; flex-shrink: 0; }
28
+ .te-crow input[type="color"] { width: 24px; height: 24px; border: 1px solid rgba(23, 23, 26, 0.1); border-radius: 7px; padding: 1px; background: #ffffff; cursor: pointer; flex-shrink: 0; }
29
29
  .te-crow span { flex: 1; font-weight: 550; font-size: 11.5px; }
30
- .te-crow code { font: 500 10.5px ui-monospace, monospace; color: #8a8b92; }
31
- .te-body select, .te-body input[type="text"] { width: 100%; font: 500 12px Inter, sans-serif; color: #14151a; background: #fffdf9; border: 1px solid #e3ddd2; border-radius: 8px; padding: 6px 8px; }
30
+ .te-crow code { font: 500 10.5px ui-monospace, monospace; color: #6f6f78; }
31
+ .te-body select, .te-body input[type="text"] { width: 100%; font: 500 12px Onest, Inter, sans-serif; color: #17171a; background: #ffffff; border: 1px solid rgba(23, 23, 26, 0.1); border-radius: 8px; padding: 6px 8px; }
32
32
  .te-body input[type="text"] { margin-top: 6px; }
33
33
  .te-srow { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
34
34
  .te-srow label { font-size: 11.5px; font-weight: 550; width: 62px; flex-shrink: 0; }
35
- .te-srow input[type="range"] { flex: 1; accent-color: #14151a; min-width: 0; }
36
- .te-srow output { font: 500 10.5px ui-monospace, monospace; color: #8a8b92; width: 34px; text-align: right; flex-shrink: 0; }
35
+ .te-srow input[type="range"] { flex: 1; accent-color: #4338ca; min-width: 0; }
36
+ .te-srow output { font: 500 10.5px ui-monospace, monospace; color: #6f6f78; width: 34px; text-align: right; flex-shrink: 0; }
37
37
  .te-segs { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
38
- .te-seg { display: flex; background: #fffdf9; border: 1px solid #e3ddd2; border-radius: 9px; padding: 2px; gap: 2px; }
39
- .te-seg button { flex: 1; border: none; background: none; font: 600 11px Inter, sans-serif; color: #6b6c72; padding: 5px 0; border-radius: 7px; cursor: pointer; }
40
- .te-seg button[aria-pressed="true"] { background: #14151a; color: #fffdf9; }
41
- .te-foot { display: flex; gap: 8px; padding: 10px 14px; border-top: 1px solid #e3ddd2; flex-shrink: 0; }
42
- .te-foot button { flex: 1; font: 600 11.5px Inter, sans-serif; border-radius: 8px; padding: 7px 0; cursor: pointer; }
43
- .te-copy { background: #14151a; color: #fffdf9; border: 1px solid #14151a; }
44
- .te-link { background: #fffdf9; color: #14151a; border: 1px solid #e3ddd2; }
38
+ .te-seg { display: flex; background: #ffffff; border: 1px solid rgba(23, 23, 26, 0.1); border-radius: 9px; padding: 2px; gap: 2px; }
39
+ .te-seg button { flex: 1; border: none; background: none; font: 600 11px Onest, Inter, sans-serif; color: #6f6f78; padding: 5px 0; border-radius: 7px; cursor: pointer; }
40
+ .te-seg button[aria-pressed="true"] { background: #17171a; color: #fafaf8; }
41
+ .te-foot { display: flex; gap: 8px; padding: 10px 14px; border-top: 1px solid rgba(23, 23, 26, 0.1); flex-shrink: 0; }
42
+ .te-foot button { flex: 1; font: 600 11.5px Onest, Inter, sans-serif; border-radius: 8px; padding: 7px 0; cursor: pointer; }
43
+ .te-copy { background: #17171a; color: #fafaf8; border: 1px solid #17171a; }
44
+ .te-link { background: #ffffff; color: #17171a; border: 1px solid rgba(23, 23, 26, 0.1); }
45
45
  @media (prefers-reduced-motion: reduce) {
46
46
  .te-card, .te-pill { transition: none; }
47
47
  }
48
48
  @media (max-width: 640px) {
49
- .te-card { top: auto; right: 0; left: 0; bottom: 0; width: auto; max-height: 72vh; border-radius: 18px 18px 0 0; border-left: none; border-right: none; border-bottom: none; padding-bottom: env(safe-area-inset-bottom, 0px); transform-origin: bottom center; box-shadow: 0 -16px 44px rgba(20, 21, 26, 0.3); }
49
+ .te-card { top: auto; right: 0; left: 0; bottom: 0; width: auto; max-height: 72vh; border-radius: 18px 18px 0 0; border-left: none; border-right: none; border-bottom: none; padding-bottom: env(safe-area-inset-bottom, 0px); transform-origin: bottom center; box-shadow: 0 -16px 44px rgba(23, 23, 26, 0.3); }
50
50
  .te-card[data-closed="true"] { transform: translateY(105%); opacity: 1; }
51
51
  .te-pill { top: auto; bottom: calc(env(safe-area-inset-bottom, 0px) + 16px); }
52
- .te-grab { display: block; width: 40px; height: 4px; border-radius: 2px; background: #d7d2c7; margin: 8px auto 0; flex-shrink: 0; }
52
+ .te-grab { display: block; width: 40px; height: 4px; border-radius: 2px; background: rgba(23, 23, 26, 0.18); margin: 8px auto 0; flex-shrink: 0; }
53
53
  .te-x { padding: 6px 10px; }
54
54
  }
55
55
  `;