@ashley-shrok/viewmodel-shell 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -51,6 +51,8 @@ The download endpoint stays auth-gated and the server authorizes in the action h
51
51
 
52
52
  ## Terminal (TUI)
53
53
 
54
+ > ⚠️ **Experimental.** The terminal adapter (`@ashley-shrok/viewmodel-shell/tui` + `vms-tui`) is incomplete and under active design — scrolling, keyboard/focus ergonomics, and layout coverage all need more work. Its API and behavior may change or be removed **without a major-version bump**; don't build production workflows on it yet. The browser/server/core packages are stable and unaffected. Constructing a `TuiAdapter` prints a one-time notice — silence it with `VMS_TUI_SILENCE_EXPERIMENTAL=1`.
55
+
54
56
  The same backend renders in a terminal — same wire, no backend change, with a real lazygit-style UX: mouse clicks, wheel scroll, per-pane focus cycle, and a context-aware status bar. Point the CLI at any ViewModel Shell endpoint:
55
57
 
56
58
  ```bash
package/dist/browser.d.ts CHANGED
@@ -31,6 +31,7 @@ export declare class BrowserAdapter implements Adapter {
31
31
  private link;
32
32
  private statBar;
33
33
  private tabs;
34
+ private image;
34
35
  private progress;
35
36
  private modal;
36
37
  private table;
package/dist/browser.js CHANGED
@@ -190,6 +190,7 @@ export class BrowserAdapter {
190
190
  case "button": return this.button(n, parent, on);
191
191
  case "text": return this.text(n, parent);
192
192
  case "link": return this.link(n, parent);
193
+ case "image": return this.image(n, parent);
193
194
  case "stat-bar": return this.statBar(n, parent);
194
195
  case "tabs": return this.tabs(n, parent, on);
195
196
  case "progress": return this.progress(n, parent);
@@ -249,14 +250,12 @@ export class BrowserAdapter {
249
250
  form.className = `vms-form${n.layout && n.layout !== "stack" ? ` vms-form--${n.layout}` : ""}`;
250
251
  form.noValidate = true;
251
252
  this.kids(n.children, form, on);
252
- const submit = document.createElement("button");
253
- submit.type = "submit";
254
- submit.className = "vms-button vms-button--primary";
255
- submit.textContent = n.submitLabel ?? "Submit";
256
- form.appendChild(submit);
257
- form.addEventListener("submit", (e) => {
258
- e.preventDefault();
259
- const ctx = { ...(n.submitAction.context ?? {}) };
253
+ // 0.10.0 (#15) — harvest this form's current field values, merge into the
254
+ // given action's context, and dispatch. Factored out of the submit
255
+ // handler so both the default submit AND each buttons[] entry can call it
256
+ // with a DIFFERENT action carrying the SAME live field values.
257
+ const harvest = (base) => {
258
+ const ctx = { ...(base.context ?? {}) };
260
259
  const files = {};
261
260
  form.querySelectorAll("input:not([type=checkbox]):not([type=file]), textarea").forEach(el => { if (el.name)
262
261
  ctx[el.name] = el.value; });
@@ -277,11 +276,43 @@ export class BrowserAdapter {
277
276
  if (inp.name && inp.files?.[0])
278
277
  files[inp.name] = inp.files[0];
279
278
  });
280
- const action = { name: n.submitAction.name, context: ctx };
279
+ const action = { name: base.name, context: ctx };
281
280
  if (Object.keys(files).length > 0)
282
281
  action.files = files;
283
282
  on(action);
284
- });
283
+ };
284
+ // Default submit button + Enter-to-submit, only when submitAction is set.
285
+ // (0.10.0: submitAction is now optional — a buttons[]-only form renders
286
+ // no default button, and Enter does not submit at the form level.)
287
+ if (n.submitAction) {
288
+ const submitAction = n.submitAction;
289
+ const submit = document.createElement("button");
290
+ submit.type = "submit";
291
+ submit.className = "vms-button vms-button--primary";
292
+ submit.textContent = n.submitLabel ?? "Submit";
293
+ form.appendChild(submit);
294
+ form.addEventListener("submit", (e) => {
295
+ e.preventDefault();
296
+ harvest(submitAction);
297
+ });
298
+ }
299
+ else {
300
+ // No default submit — still neutralize implicit Enter submission so a
301
+ // single-field buttons[]-only form doesn't reload via native submit.
302
+ form.addEventListener("submit", (e) => e.preventDefault());
303
+ }
304
+ // 0.10.0 (#15) — multi-action buttons. Each renders through the normal
305
+ // button() path (so variant + pendingLabel work) but its onAction is
306
+ // wrapped to harvest the form first. We render them in a footer row so
307
+ // they group like the default submit.
308
+ if (n.buttons && n.buttons.length > 0) {
309
+ const row = document.createElement("div");
310
+ row.className = "vms-form__buttons";
311
+ const harvestOn = (action) => harvest(action);
312
+ for (const btn of n.buttons)
313
+ this.button(btn, row, harvestOn);
314
+ form.appendChild(row);
315
+ }
285
316
  parent.appendChild(form);
286
317
  }
287
318
  field(n, parent, on) {
@@ -539,6 +570,19 @@ export class BrowserAdapter {
539
570
  });
540
571
  parent.appendChild(nav);
541
572
  }
573
+ image(n, parent) {
574
+ const img = document.createElement("img");
575
+ let cls = "vms-image";
576
+ if (n.size)
577
+ cls += ` vms-image--${n.size}`;
578
+ if (n.shape)
579
+ cls += ` vms-image--${n.shape}`;
580
+ img.className = cls;
581
+ img.src = n.src;
582
+ if (n.alt != null)
583
+ img.alt = n.alt;
584
+ parent.appendChild(img);
585
+ }
542
586
  progress(n, parent) {
543
587
  const track = document.createElement("div");
544
588
  track.className = "vms-progress";
package/dist/index.d.ts CHANGED
@@ -33,7 +33,7 @@ export interface Adapter {
33
33
  * async I/O errors surface via onError. */
34
34
  saveFile?(data: Blob, filename: string, contentType: string): void | Promise<void>;
35
35
  }
36
- export type ViewNode = PageNode | SectionNode | ListNode | ListItemNode | FormNode | FieldNode | CheckboxNode | ButtonNode | TextNode | LinkNode | StatBarNode | TabsNode | ProgressNode | ModalNode | TableNode | CopyButtonNode;
36
+ export type ViewNode = PageNode | SectionNode | ListNode | ListItemNode | FormNode | FieldNode | CheckboxNode | ButtonNode | TextNode | LinkNode | ImageNode | StatBarNode | TabsNode | ProgressNode | ModalNode | TableNode | CopyButtonNode;
37
37
  export interface PageNode {
38
38
  type: "page";
39
39
  title?: string;
@@ -68,10 +68,23 @@ export interface ListItemNode {
68
68
  }
69
69
  export interface FormNode {
70
70
  type: "form";
71
- submitAction: ActionEvent;
71
+ /** The default submit action — renders an auto submit button + fires on
72
+ * Enter in a text field. OPTIONAL since 0.10.0 (#15): omit it for a form
73
+ * whose only triggers are `buttons[]`. When omitted, no default submit
74
+ * button renders and Enter does not submit at the form level (a
75
+ * FieldNode.action still fires per-field). */
76
+ submitAction?: ActionEvent;
72
77
  submitLabel?: string;
73
78
  /** Layout preset for the form's controls. Omitted or "stack" = fields stacked (current, no modifier class). "inline" = field row + submit on one line (add/search bar) — emits .vms-form--inline. Closed union (D-29). */
74
79
  layout?: "stack" | "inline";
80
+ /** Multi-action submit buttons (#15). Each is a full ButtonNode (so
81
+ * `variant` + `pendingLabel` apply) that, on activation, HARVESTS this
82
+ * form's current field values into its `action.context` and dispatches —
83
+ * the same harvest the default submit performs, but carrying a different
84
+ * action. Mirrors HTML's multiple submit buttons / `formaction`. A plain
85
+ * ButtonNode placed in `children` keeps its no-harvest behavior; only
86
+ * buttons in THIS slot harvest. */
87
+ buttons?: ButtonNode[];
75
88
  children: ViewNode[];
76
89
  }
77
90
  export interface FieldNode {
@@ -118,7 +131,7 @@ export interface ButtonNode {
118
131
  export interface TextNode {
119
132
  type: "text";
120
133
  value: string;
121
- style?: "heading" | "subheading" | "body" | "muted" | "strikethrough" | "error" | "pre";
134
+ style?: "heading" | "subheading" | "body" | "muted" | "strikethrough" | "error" | "warning" | "pre";
122
135
  }
123
136
  export interface LinkNode {
124
137
  type: "link";
@@ -127,6 +140,18 @@ export interface LinkNode {
127
140
  /** true = open outside current app context (browser: new tab + noopener) */
128
141
  external?: boolean;
129
142
  }
143
+ export interface ImageNode {
144
+ type: "image";
145
+ /** Image source URL (required). */
146
+ src: string;
147
+ /** Accessibility text. Non-browser adapters (TUI) degrade to this. */
148
+ alt?: string;
149
+ /** Design-system sizing hint → `.vms-image--{size}`. Omit for intrinsic size
150
+ * (capped at 100% of the container). NOT free-form CSS. */
151
+ size?: "small" | "medium" | "large" | "full";
152
+ /** `"circle"` → square-cropped circular image (avatars). */
153
+ shape?: "circle";
154
+ }
130
155
  export interface StatBarNode {
131
156
  type: "stat-bar";
132
157
  stats: Array<{
package/dist/tui.d.ts CHANGED
@@ -8,6 +8,16 @@ interface TuiOpts {
8
8
  * of the available width; the remainder fills with the rest of the children. */
9
9
  sidebarFraction?: number;
10
10
  }
11
+ /**
12
+ * Renders a ViewModel Shell view tree to a terminal via OpenTUI (Bun runtime).
13
+ *
14
+ * @experimental The terminal adapter is incomplete and under active design —
15
+ * scrolling, keyboard/focus ergonomics, and layout coverage all need more
16
+ * work. Its API and behavior may change or be removed without a major-version
17
+ * bump. Constructing one prints a one-time stderr notice (silence with
18
+ * `VMS_TUI_SILENCE_EXPERIMENTAL=1`). The browser/server/core packages are
19
+ * stable; only `@ashley-shrok/viewmodel-shell/tui` + `vms-tui` are experimental.
20
+ */
11
21
  export declare class TuiAdapter implements Adapter {
12
22
  private renderer;
13
23
  private root;
@@ -74,5 +84,9 @@ export declare class TuiAdapter implements Adapter {
74
84
  * landed (parity with the Ink adapter's _peekSession). */
75
85
  _peekSession(key: string): string | undefined;
76
86
  }
87
+ /**
88
+ * @experimental Part of the experimental terminal target (see {@link TuiAdapter}).
89
+ * A static, non-mounting render path used by the cross-adapter conformance suite.
90
+ */
77
91
  export declare function renderTree(vm: ViewNode): React.ReactNode;
78
92
  export {};
package/dist/tui.js CHANGED
@@ -3,6 +3,34 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
3
  import { homedir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import { spawn } from "node:child_process";
6
+ // ─── Experimental notice ───────────────────────────────────────────────────
7
+ // The terminal adapter is EXPERIMENTAL (see the @experimental tag on
8
+ // TuiAdapter). We emit a one-time stderr heads-up the first time a TuiAdapter
9
+ // is constructed in a process — covering both `vms-tui` (which constructs one)
10
+ // and programmatic consumers. Fires once per process; silence with
11
+ // VMS_TUI_SILENCE_EXPERIMENTAL=1 for deliberate users who don't want the nag.
12
+ let experimentalNoticeShown = false;
13
+ function warnExperimental() {
14
+ if (experimentalNoticeShown)
15
+ return;
16
+ experimentalNoticeShown = true;
17
+ if (process.env.VMS_TUI_SILENCE_EXPERIMENTAL)
18
+ return;
19
+ process.stderr.write("[vms-tui] ⚠ The terminal adapter (TuiAdapter) is EXPERIMENTAL: incomplete, " +
20
+ "under-tested, and subject to breaking change or removal without a major-version " +
21
+ "bump. Not recommended for production. The browser/server/core packages are " +
22
+ "stable and unaffected. Silence this notice with VMS_TUI_SILENCE_EXPERIMENTAL=1.\n");
23
+ }
24
+ /**
25
+ * Renders a ViewModel Shell view tree to a terminal via OpenTUI (Bun runtime).
26
+ *
27
+ * @experimental The terminal adapter is incomplete and under active design —
28
+ * scrolling, keyboard/focus ergonomics, and layout coverage all need more
29
+ * work. Its API and behavior may change or be removed without a major-version
30
+ * bump. Constructing one prints a one-time stderr notice (silence with
31
+ * `VMS_TUI_SILENCE_EXPERIMENTAL=1`). The browser/server/core packages are
32
+ * stable; only `@ashley-shrok/viewmodel-shell/tui` + `vms-tui` are experimental.
33
+ */
6
34
  export class TuiAdapter {
7
35
  renderer = null;
8
36
  root = null;
@@ -38,6 +66,7 @@ export class TuiAdapter {
38
66
  // pending state, no per-button cleanup wiring needed).
39
67
  pendingButtonKey = null;
40
68
  constructor(opts) {
69
+ warnExperimental();
41
70
  this.viewport = opts?.viewport ?? "fill";
42
71
  const f = opts?.sidebarFraction ?? 1 / 3;
43
72
  this.sidebarFraction = Math.min(0.6, Math.max(0.15, f));
@@ -672,6 +701,7 @@ function renderNode(node, ctx, key) {
672
701
  case "section": return _jsx(SectionView, { node: node, ctx: ctx }, key);
673
702
  case "text": return _jsx(TextView, { node: node }, key);
674
703
  case "link": return _jsx(LinkView, { node: node, ctx: ctx }, key);
704
+ case "image": return _jsx(ImageView, { node: node }, key);
675
705
  case "list": return _jsx(ListView, { node: node, ctx: ctx }, key);
676
706
  case "list-item": return _jsx(ListItemView, { node: node, ctx: ctx }, key);
677
707
  case "table": return _jsx(TableView, { node: node, ctx: ctx }, key);
@@ -775,6 +805,7 @@ const STYLE_ATTRS = {
775
805
  muted: { fg: "#888888" },
776
806
  strikethrough: { attributes: 16 /* STRIKETHROUGH */, fg: "#888888" },
777
807
  error: { fg: "#ff5555" },
808
+ warning: { fg: "#e0a823" },
778
809
  pre: { fg: "#cccccc" },
779
810
  };
780
811
  function TextView({ node }) {
@@ -804,6 +835,14 @@ function LinkView({ node, ctx }) {
804
835
  : undefined;
805
836
  return (_jsx("text", { attributes: 4 /* UNDERLINE */, fg: "#6688cc", ...(onMouseDown ? { onMouseDown } : {}), children: inner }));
806
837
  }
838
+ // ── image ─────────────────────────────────────────────────────────────────
839
+ // Terminals can't render raster images, so the TUI degrades to the alt text
840
+ // (the wire's accessibility intent) — the multi-target-safe contract from the
841
+ // ImageNode design. size/shape are browser-only layout hints and are ignored.
842
+ function ImageView({ node }) {
843
+ const alt = node.alt && node.alt.trim().length > 0 ? node.alt : "image";
844
+ return _jsxs("text", { fg: "#888888", children: ["[image: ", alt, "]"] });
845
+ }
807
846
  // ── list / list-item ──────────────────────────────────────────────────────
808
847
  // A top-level `list` (direct child of page) is a pane on its own; nested
809
848
  // lists scroll as part of their containing section. list-item variants
@@ -1034,12 +1073,12 @@ function CopyButtonView({ node, ctx }) {
1034
1073
  // reads each from the adapter's fieldValues map (falling back to wire), and
1035
1074
  // dispatches submitAction with `{ [name]: value, ... }` merged into context.
1036
1075
  function FormView({ node, ctx }) {
1037
- // Snapshot the form for the closure same-instance fine since the closure
1038
- // is recreated on every render (which is every server response).
1039
- const submitThisForm = () => {
1040
- const merged = {
1041
- ...(node.submitAction.context ?? {}),
1042
- };
1076
+ // 0.10.0 (#15) harvest this form's current field values, merge into the
1077
+ // given action's context, and dispatch. Generalized from the single-submit
1078
+ // closure so the default submit AND each buttons[] entry can call it with
1079
+ // a DIFFERENT action carrying the SAME live field values.
1080
+ const submitFormWith = (base) => {
1081
+ const merged = { ...(base.context ?? {}) };
1043
1082
  const collect = (n) => {
1044
1083
  if (n.type === "field") {
1045
1084
  const wireValue = n.value ?? "";
@@ -1058,17 +1097,23 @@ function FormView({ node, ctx }) {
1058
1097
  };
1059
1098
  for (const child of node.children)
1060
1099
  collect(child);
1061
- ctx.onAction({ name: node.submitAction.name, context: merged });
1100
+ ctx.onAction({ name: base.name, context: merged });
1062
1101
  };
1102
+ // Enter-in-a-field submits the default action — only wired when present.
1103
+ const submitAction = node.submitAction;
1063
1104
  const childCtx = {
1064
1105
  ...ctx,
1065
1106
  isTopLevel: false,
1066
- submitForm: submitThisForm,
1107
+ submitForm: submitAction ? () => submitFormWith(submitAction) : null,
1067
1108
  };
1109
+ // buttons[] (#15) render through ButtonView so variant + pendingLabel work;
1110
+ // their onAction is wrapped to harvest the form first. pendingButtonKey
1111
+ // plumbing (0.8.0) flows through ctx unchanged.
1112
+ const buttonCtx = { ...childCtx, onAction: submitFormWith };
1068
1113
  // Layout preset on form: "stack" (default — fields stacked) or "inline"
1069
1114
  // (field row + submit on one line, the add-bar/search-bar pattern).
1070
1115
  const isInline = node.layout === "inline";
1071
- return (_jsxs("box", { flexDirection: isInline ? "row" : "column", gap: 1, children: [node.children.map((child, i) => renderNode(child, childCtx, i)), _jsxs("text", { attributes: 1 /* BOLD */, children: ["[ ", node.submitLabel ?? "Submit", " ]"] })] }));
1116
+ return (_jsxs("box", { flexDirection: isInline ? "row" : "column", gap: 1, children: [node.children.map((child, i) => renderNode(child, childCtx, i)), submitAction != null ? (_jsxs("text", { attributes: 1 /* BOLD */, children: ["[ ", node.submitLabel ?? "Submit", " ]"] })) : null, node.buttons && node.buttons.length > 0 ? (_jsx("box", { flexDirection: "row", gap: 2, children: node.buttons.map((btn, i) => (_jsx(ButtonView, { node: btn, ctx: buttonCtx }, `b${i}`))) })) : null] }));
1072
1117
  }
1073
1118
  // ── field ─────────────────────────────────────────────────────────────────
1074
1119
  // Real OpenTUI input/textarea/select wired to the adapter's field state.
@@ -1196,6 +1241,10 @@ function UnsupportedView({ type }) {
1196
1241
  // test/conformance.tui.test.ts) invokes the components directly. Note that
1197
1242
  // the App component is hooks-free — focus state is passed via props with a
1198
1243
  // safe default — so the walker works without a React reconciler.
1244
+ /**
1245
+ * @experimental Part of the experimental terminal target (see {@link TuiAdapter}).
1246
+ * A static, non-mounting render path used by the cross-adapter conformance suite.
1247
+ */
1199
1248
  export function renderTree(vm) {
1200
1249
  return _jsx(App, { vm: vm, onAction: () => { } });
1201
1250
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ashley-shrok/viewmodel-shell",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "A server-driven UI framework where the wire format is structured enough that agents can build full-stack apps without ever opening a browser and all UI tests are pure unit tests with no browser runtime. Server returns a JSON tree of typed nodes; a thin TypeScript adapter renders it to DOM. Backend-agnostic — a .NET reference backend ships with the repo, but any language can produce the JSON contract.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -28,12 +28,15 @@
28
28
  --vms-done-text: #9090a0;
29
29
  --vms-error: #c2453d;
30
30
  --vms-error-glow: rgba(194, 69, 61, 0.10);
31
- --vms-warning: #a37510;
31
+ --vms-warning: #8a630d;
32
32
  --vms-priority-high: #d76410;
33
33
  --vms-success: #2da359;
34
34
  --vms-info: #2277dd;
35
35
  --vms-radius: 10px;
36
36
  --vms-radius-sm: 6px;
37
+ --vms-image-small: 4rem;
38
+ --vms-image-medium: 8rem;
39
+ --vms-image-large: 16rem;
37
40
  --vms-font-body: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
38
41
  --vms-font-head: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
39
42
  --vms-font-mono: ui-monospace, 'Cascadia Code', 'Fira Code', Menlo, Consolas, 'Liberation Mono', monospace;
@@ -231,6 +234,16 @@ body {
231
234
  flex-wrap: wrap;
232
235
  }
233
236
  .vms-form--inline > .vms-field { flex: 1 1 12rem; }
237
+ /* Multi-action submit buttons row (0.10.0 / #15) — groups the form's
238
+ buttons[] entries on one line, like a dialog action bar. align-self
239
+ flex-start keeps the row from stretching full-width in the stacked form. */
240
+ .vms-form__buttons {
241
+ display: flex;
242
+ flex-direction: row;
243
+ gap: var(--vms-space-sm);
244
+ align-self: flex-start;
245
+ flex-wrap: wrap;
246
+ }
234
247
  .vms-field { display: flex; flex-direction: column; gap: var(--vms-space-2xs); }
235
248
  .vms-field__label { font-size: var(--vms-text-sm); color: var(--vms-text-muted); }
236
249
  .vms-field__input {
@@ -448,6 +461,7 @@ select.vms-field__input { cursor: pointer; padding-right: var(--vms-space-xl);
448
461
  .vms-text--muted { color: var(--vms-text-muted); font-size: var(--vms-text-sm); }
449
462
  .vms-text--strikethrough { color: var(--vms-done-text); text-decoration: line-through; }
450
463
  .vms-text--error { color: var(--vms-error); font-size: var(--vms-text-base); }
464
+ .vms-text--warning { color: var(--vms-warning); font-size: var(--vms-text-base); }
451
465
  .vms-text--pre { font-family: var(--vms-font-mono); white-space: pre; }
452
466
 
453
467
  /* ── Link ── */
@@ -463,6 +477,15 @@ select.vms-field__input { cursor: pointer; padding-right: var(--vms-space-xl);
463
477
  }
464
478
  .vms-link:hover { border-bottom-color: var(--vms-accent); }
465
479
 
480
+ /* ── Image ── */
481
+ .vms-image { max-width: 100%; height: auto; display: block; }
482
+ .vms-image--small { width: var(--vms-image-small); }
483
+ .vms-image--medium { width: var(--vms-image-medium); }
484
+ .vms-image--large { width: var(--vms-image-large); }
485
+ .vms-image--full { width: 100%; }
486
+ /* Avatars: square-crop to a circle (object-fit avoids distortion on non-square sources). */
487
+ .vms-image--circle { border-radius: 50%; aspect-ratio: 1; object-fit: cover; }
488
+
466
489
  /* ── Progress ── */
467
490
  .vms-progress { height: 3px; background: var(--vms-surface-2); border-radius: 99px; overflow: hidden; }
468
491
  .vms-progress__bar { height: 100%; background: var(--vms-accent); border-radius: 99px; transition: width 0.3s ease; }
@@ -13,7 +13,7 @@
13
13
  --vms-done-text: #9090a0;
14
14
  --vms-error: #c2453d;
15
15
  --vms-error-glow: rgba(194, 69, 61, 0.10);
16
- --vms-warning: #c89610;
16
+ --vms-warning: #8a630d;
17
17
  --vms-priority-high:#d76410;
18
18
  --vms-success: #2da359;
19
19
  --vms-info: #2277dd;
@@ -13,7 +13,7 @@
13
13
  --vms-done-text: #9090a0;
14
14
  --vms-error: #c2453d;
15
15
  --vms-error-glow: rgba(194, 69, 61, 0.10);
16
- --vms-warning: #c89610;
16
+ --vms-warning: #8a630d;
17
17
  --vms-priority-high:#d76410;
18
18
  --vms-success: #2da359;
19
19
  --vms-info: #2277dd;
@@ -13,7 +13,7 @@
13
13
  --vms-done-text: #9090a0;
14
14
  --vms-error: #c2453d;
15
15
  --vms-error-glow: rgba(194, 69, 61, 0.10);
16
- --vms-warning: #c89610;
16
+ --vms-warning: #8a630d;
17
17
  --vms-priority-high:#d76410;
18
18
  --vms-success: #2da359;
19
19
  --vms-info: #2277dd;
@@ -16,7 +16,7 @@
16
16
  --vms-done-text: #9090a0;
17
17
  --vms-error: #c2453d;
18
18
  --vms-error-glow: rgba(194, 69, 61, 0.10);
19
- --vms-warning: #c89610;
19
+ --vms-warning: #8a630d;
20
20
  --vms-priority-high:#d76410;
21
21
  --vms-success: #2da359;
22
22
  --vms-info: #2277dd;
@@ -13,7 +13,7 @@
13
13
  --vms-done-text: #9090a0;
14
14
  --vms-error: #c2453d;
15
15
  --vms-error-glow: rgba(194, 69, 61, 0.10);
16
- --vms-warning: #c89610;
16
+ --vms-warning: #8a630d;
17
17
  --vms-priority-high:#d76410;
18
18
  --vms-success: #2da359;
19
19
  --vms-info: #2277dd;
@@ -13,7 +13,7 @@
13
13
  --vms-done-text: #9090a0;
14
14
  --vms-error: #c2453d;
15
15
  --vms-error-glow: rgba(194, 69, 61, 0.10);
16
- --vms-warning: #c89610;
16
+ --vms-warning: #8a630d;
17
17
  --vms-priority-high:#d76410;
18
18
  --vms-success: #2da359;
19
19
  --vms-info: #2277dd;