@worca/app 1.2.0-rc.2 → 1.2.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 +21 -1
- package/package.json +1 -1
- package/src/cli/worca-cc.mjs +183 -8
- package/src/core/ask/models.mjs +7 -1
- package/src/core/ask/turn.mjs +5 -0
- package/src/core/config.mjs +33 -4
- package/src/core/folder-dialog.mjs +21 -12
- package/src/core/marketplaces.mjs +13 -0
- package/src/core/model-env.mjs +41 -0
- package/src/core/model-test.mjs +2 -1
- package/src/core/plugin-manifest.mjs +45 -8
- package/src/core/run-harness.mjs +7 -0
- package/src/core/settings.mjs +61 -0
- package/src/core/title.mjs +72 -9
- package/src/core/ui-instance.mjs +31 -4
- package/src/core/workflow-export.mjs +1169 -0
- package/src/core/workflow-share.mjs +191 -0
- package/src/core/workflows.mjs +35 -4
- package/src/shared/graph/validate.mjs +5 -3
- package/ui/public/app.js +675 -23
- package/ui/public/ask-panel.mjs +242 -8
- package/ui/public/export-slug.mjs +30 -0
- package/ui/public/graph/composer.mjs +24 -3
- package/ui/public/graph/inspector.mjs +4 -1
- package/ui/public/index.html +151 -5
- package/ui/public/models-view.mjs +34 -3
- package/ui/public/style.css +117 -8
- package/ui/server.mjs +237 -77
|
@@ -77,7 +77,7 @@ export function suggestDuplicateId(id, takenIds = []) {
|
|
|
77
77
|
* `globals` come MASKED from GET /api/models. `predefinedShadowedIds` marks
|
|
78
78
|
* built-ins currently overridden by a global entry.
|
|
79
79
|
*/
|
|
80
|
-
export function renderModelsList({ globals = [], legacy = [], plugins = [], predefined = [], efforts = [], projectName = '' } = {}, { doc = globalThis.document } = {}) {
|
|
80
|
+
export function renderModelsList({ globals = [], legacy = [], plugins = [], predefined = [], efforts = [], hideBuiltin = false, projectName = '' } = {}, { doc = globalThis.document } = {}) {
|
|
81
81
|
const root = h(doc, 'div', 'mv-list');
|
|
82
82
|
const predefLc = new Set(predefined.map((m) => m.id.toLowerCase()));
|
|
83
83
|
const pluginLc = new Set(plugins.map((m) => m.id.toLowerCase()));
|
|
@@ -89,6 +89,30 @@ export function renderModelsList({ globals = [], legacy = [], plugins = [], pred
|
|
|
89
89
|
return s;
|
|
90
90
|
};
|
|
91
91
|
|
|
92
|
+
// The endpoint-routed badge (#422): honest disclosure that worca points the
|
|
93
|
+
// CLI's internal haiku/sonnet/opus/fable lookups at this model's own id
|
|
94
|
+
// (config.mjs#resolveModelEnv), so nothing falls back to the Anthropic API.
|
|
95
|
+
// The masked env still carries its KEYS, so presence is decidable here.
|
|
96
|
+
const routedBadge = (m) => {
|
|
97
|
+
if (!m.env || !Object.prototype.hasOwnProperty.call(m.env, 'ANTHROPIC_BASE_URL')) return null;
|
|
98
|
+
const b = h(doc, 'span', 'badge waiting mv-routed', 'endpoint-routed');
|
|
99
|
+
b.title = "worca points Claude Code's internal haiku/sonnet/opus/fable lookups at this model, so it never falls back to the Anthropic API.";
|
|
100
|
+
return b;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// ── Hide built-in models (#422) — one checkbox, top of the pane ──
|
|
104
|
+
const hideRow = h(doc, 'div', 'mv-hide-builtin-row');
|
|
105
|
+
const hideLabel = h(doc, 'label', 'check-row');
|
|
106
|
+
const hideCb = h(doc, 'input', 'mv-hide-builtin');
|
|
107
|
+
hideCb.type = 'checkbox';
|
|
108
|
+
hideCb.checked = !!hideBuiltin;
|
|
109
|
+
hideLabel.appendChild(hideCb);
|
|
110
|
+
hideLabel.appendChild(doc.createTextNode(" Hide built-in models (you don't use a first-party Anthropic account)"));
|
|
111
|
+
hideRow.appendChild(hideLabel);
|
|
112
|
+
hideRow.appendChild(h(doc, 'small', 'hint',
|
|
113
|
+
'Drops the built-ins from every model picker — new pipelines, the composer, Ask Worca, title generation. Cosmetic only: a run or reference that already names one keeps working.'));
|
|
114
|
+
root.appendChild(hideRow);
|
|
115
|
+
|
|
92
116
|
// ── Your models (global) ──
|
|
93
117
|
const yours = section('Your models', 'Defined once, available in every project. An entry with a built-in id overrides that built-in.');
|
|
94
118
|
if (!globals.length) {
|
|
@@ -102,6 +126,8 @@ export function renderModelsList({ globals = [], legacy = [], plugins = [], pred
|
|
|
102
126
|
head.appendChild(h(doc, 'b', 'mv-name', m.label || m.id));
|
|
103
127
|
if (predefLc.has(m.id.toLowerCase())) head.appendChild(h(doc, 'span', 'badge violet mv-shadow', 'overrides built-in'));
|
|
104
128
|
else if (pluginLc.has(m.id.toLowerCase())) head.appendChild(h(doc, 'span', 'badge violet mv-shadow', 'overrides plugin'));
|
|
129
|
+
const rb = routedBadge(m);
|
|
130
|
+
if (rb) head.appendChild(rb);
|
|
105
131
|
// The §4.6 "unreliable" badge is meaningless once an override GOVERNS this
|
|
106
132
|
// model's spend — and the backend only lifts the stored flag on the model's
|
|
107
133
|
// next result event, so suppress it here the moment pricing is pinned.
|
|
@@ -169,6 +195,8 @@ export function renderModelsList({ globals = [], legacy = [], plugins = [], pred
|
|
|
169
195
|
head.appendChild(h(doc, 'b', 'mv-name', m.label || m.id));
|
|
170
196
|
head.appendChild(h(doc, 'span', 'badge waiting mv-origin', `plugin: ${m.plugin}`));
|
|
171
197
|
if (globalLc.has(m.id.toLowerCase())) head.appendChild(h(doc, 'span', 'badge violet mv-shadowed', 'overridden by your copy'));
|
|
198
|
+
const prb = routedBadge(m);
|
|
199
|
+
if (prb) head.appendChild(prb);
|
|
172
200
|
// Same rule as a global card: a manifest-pinned price governs the spend,
|
|
173
201
|
// so the §4.6 "unreliable" flag says nothing about it.
|
|
174
202
|
if (m.costUnreliable && !m.cost) head.appendChild(h(doc, 'span', 'badge waiting mv-cost', 'cost not verified'));
|
|
@@ -199,8 +227,11 @@ export function renderModelsList({ globals = [], legacy = [], plugins = [], pred
|
|
|
199
227
|
}
|
|
200
228
|
|
|
201
229
|
// ── Built-ins (read-only) ──
|
|
202
|
-
const builtins = section('Built-in models',
|
|
203
|
-
|
|
230
|
+
const builtins = section('Built-in models', hideBuiltin
|
|
231
|
+
? `Hidden from every picker (${predefined.length} built-in${predefined.length === 1 ? '' : 's'}) — untick the box above to show them.`
|
|
232
|
+
: 'Shipped with worca. Add a model with the same id to override its label, efforts, or routing.');
|
|
233
|
+
builtins.classList.add(hideBuiltin ? 'mv-builtins-hidden' : 'mv-builtins-shown');
|
|
234
|
+
for (const m of hideBuiltin ? [] : predefined) {
|
|
204
235
|
const row = h(doc, 'div', 'mv-builtin');
|
|
205
236
|
row.dataset.id = m.id;
|
|
206
237
|
row.appendChild(h(doc, 'b', 'mv-name', m.label));
|
package/ui/public/style.css
CHANGED
|
@@ -959,6 +959,11 @@ fieldset.source legend{font-weight:600;font-size:13px;padding:0 6px;color:var(--
|
|
|
959
959
|
/* Confirm dialog must stack ABOVE the plugin/wizard modal (equal .viewer-modal z-index,
|
|
960
960
|
but #confirm-modal precedes #plugin-modal in the DOM). */
|
|
961
961
|
#confirm-modal.viewer-modal{z-index:60;}
|
|
962
|
+
/* The folder browser is a leaf picker opened FROM other modals (add-project,
|
|
963
|
+
export). It precedes #export-modal in the DOM, so at equal .viewer-modal
|
|
964
|
+
z-index the export modal would paint over it, hiding the Browse… picker.
|
|
965
|
+
Elevate it above every other modal so it is always usable when open. */
|
|
966
|
+
#folder-browser.viewer-modal{z-index:70;}
|
|
962
967
|
.viewer-modal .card{width:min(860px,100%);max-height:86vh;overflow:auto;}
|
|
963
968
|
/* folder browser modal (native-dialog fallback). Must come AFTER the
|
|
964
969
|
.viewer-modal rules: `.folder-modal .card` ties with `.viewer-modal .card`
|
|
@@ -1056,6 +1061,39 @@ fieldset.source legend{font-weight:600;font-size:13px;padding:0 6px;color:var(--
|
|
|
1056
1061
|
.pl-remove:disabled{opacity:.6;cursor:default;}
|
|
1057
1062
|
.pl-remove svg{display:block;}
|
|
1058
1063
|
|
|
1064
|
+
/* Export dialog (JSON file / Claude Code skill / Worca plugin). The row's
|
|
1065
|
+
Export… button itself is a .btn-ghost (see .gv-saved .pl-export). */
|
|
1066
|
+
.export-field{margin:12px 0;}
|
|
1067
|
+
.export-field .hint{display:block;margin-top:6px;}
|
|
1068
|
+
/* A checkbox row directly under a text input needs its own breathing room:
|
|
1069
|
+
.confirm-checkbox ships margin:0 (it is built for the confirm dialog's stack). */
|
|
1070
|
+
.export-field .confirm-checkbox{margin-top:10px;}
|
|
1071
|
+
/* Result view after a successful export (replaces #export-form in the same card). */
|
|
1072
|
+
.export-done{margin:12px 0 4px;}
|
|
1073
|
+
.export-done.hidden{display:none;}
|
|
1074
|
+
.export-done-head{display:flex;align-items:center;gap:10px;font-size:14px;margin-bottom:12px;}
|
|
1075
|
+
.export-done-check{display:grid;place-items:center;width:26px;height:26px;border-radius:999px;
|
|
1076
|
+
background:var(--green-bg,#e6f4ea);color:var(--green-ink,#1e6b3a);font-weight:700;font-size:14px;}
|
|
1077
|
+
.export-done-lines{display:grid;grid-template-columns:max-content 1fr;gap:6px 14px;margin:0;font-size:12.5px;}
|
|
1078
|
+
.export-done-lines dt{color:var(--ink-3);font-weight:600;}
|
|
1079
|
+
.export-done-lines dd{margin:0;color:var(--ink);word-break:break-all;}
|
|
1080
|
+
.export-done-lines code{font:12px var(--mono);background:var(--field);border-radius:6px;padding:1px 6px;}
|
|
1081
|
+
.export-done-next{margin:14px 0 0;padding:10px 12px;border-radius:10px;background:var(--field);font-size:12.5px;color:var(--ink-2);}
|
|
1082
|
+
.export-done-next code{font:12px var(--mono);}
|
|
1083
|
+
.export-label{display:block;font-weight:600;font-size:12.5px;color:var(--ink-2);margin-bottom:6px;}
|
|
1084
|
+
.export-folder-row{display:flex;gap:8px;align-items:center;}
|
|
1085
|
+
.export-folder-row .input{flex:1 1 auto;}
|
|
1086
|
+
.export-plan{margin:12px 0;max-height:240px;overflow:auto;border:1px solid var(--line-2);border-radius:10px;padding:8px;font-size:12.5px;}
|
|
1087
|
+
.export-row{padding:4px 2px;display:flex;flex-wrap:wrap;gap:6px;align-items:center;}
|
|
1088
|
+
.export-conflict-row{flex-direction:column;align-items:flex-start;}
|
|
1089
|
+
.export-tag{display:inline-block;min-width:58px;text-align:center;border-radius:6px;padding:1px 6px;font-weight:700;font-size:11px;}
|
|
1090
|
+
.export-create{background:var(--green-bg,#e6f7ec);color:var(--green-ink,#137a3b);}
|
|
1091
|
+
.export-update{background:var(--field);color:var(--ink);}
|
|
1092
|
+
.export-no-op{background:var(--field);color:var(--ink-3);}
|
|
1093
|
+
.export-warn,.export-conflict{background:var(--red-bg,#fde8e8);color:var(--red-ink,#b42323);}
|
|
1094
|
+
.export-orphan{background:var(--field);color:var(--ink-3);}
|
|
1095
|
+
.export-choices{display:flex;gap:12px;margin:4px 0 2px 64px;}
|
|
1096
|
+
.export-choice{font-size:12.5px;color:var(--ink-2);cursor:pointer;}
|
|
1059
1097
|
/* Per-agent fan-out toggle in the New Pipeline rows. */
|
|
1060
1098
|
.fanout-toggle {
|
|
1061
1099
|
display: inline-flex;
|
|
@@ -1536,10 +1574,23 @@ fieldset.source legend{font-weight:600;font-size:13px;padding:0 6px;color:var(--
|
|
|
1536
1574
|
.topnav button.active .topnav-amt{color:inherit;}
|
|
1537
1575
|
|
|
1538
1576
|
/* ---------- Settings cards ---------- */
|
|
1539
|
-
/* One width/centering/gap rule for
|
|
1577
|
+
/* One width/centering/gap rule for every Settings card. */
|
|
1540
1578
|
.settings-card{max-width:760px;padding:22px;margin-left:auto;margin-right:auto;}
|
|
1541
1579
|
.settings-card + .settings-card{margin-top:18px;}
|
|
1542
1580
|
|
|
1581
|
+
/* About card rows. .about-val and .about-link share specificity on one anchor, so
|
|
1582
|
+
.about-link must stay AFTER .about-val (test/ui-settings-about.test.mjs pins it). */
|
|
1583
|
+
.about-row{display:flex;align-items:baseline;gap:10px;font-size:13px;}
|
|
1584
|
+
.about-row + .about-row{margin-top:10px;}
|
|
1585
|
+
.about-key{flex:0 0 96px;font-weight:600;color:var(--ink-2);}
|
|
1586
|
+
.about-val{color:var(--ink);min-width:0;}
|
|
1587
|
+
.about-version{font-family:var(--mono);}
|
|
1588
|
+
/* Once painted with a release-tag href the version reads as a link. */
|
|
1589
|
+
.about-version[href]{color:var(--green-ink);text-decoration:none;}
|
|
1590
|
+
.about-version[href]:hover{text-decoration:underline;}
|
|
1591
|
+
.about-link{color:var(--green-ink);font-weight:600;text-decoration:none;overflow-wrap:anywhere;}
|
|
1592
|
+
.about-link:hover{text-decoration:underline;}
|
|
1593
|
+
|
|
1543
1594
|
/* ---------- Budget readout (Settings) ---------- */
|
|
1544
1595
|
/* Section gap before the limit fields (the disclaimer hint that used to sit
|
|
1545
1596
|
here moved into the card heading's ⓘ tooltip). */
|
|
@@ -1586,6 +1637,8 @@ fieldset.source legend{font-weight:600;font-size:13px;padding:0 6px;color:var(--
|
|
|
1586
1637
|
/* ---- Models view (configurable-models-design.md §4.10) ---- */
|
|
1587
1638
|
.mv-list{display:grid;gap:18px;}
|
|
1588
1639
|
.mv-section{display:grid;gap:10px;}
|
|
1640
|
+
.mv-hide-builtin-row{display:grid;gap:6px;padding:10px 12px;border:1px solid var(--line-2,#e6e6ec);border-radius:10px;}
|
|
1641
|
+
.mv-hide-builtin-row .hint{margin:0;}
|
|
1589
1642
|
.mv-section-title{margin:4px 0 0;font-size:13px;text-transform:uppercase;letter-spacing:.04em;opacity:.75;}
|
|
1590
1643
|
.mv-card{padding:16px 20px;display:flex;align-items:center;gap:14px;}
|
|
1591
1644
|
.mv-body{flex:1 1 auto;min-width:0;}
|
|
@@ -2820,7 +2873,8 @@ body.rail-collapsed .ask-dock{left:76px;}
|
|
|
2820
2873
|
font-family:var(--mono);font-weight:400;font-size:10.5px;}
|
|
2821
2874
|
|
|
2822
2875
|
.ask-sheet{pointer-events:auto;position:relative;display:flex;flex-direction:column;overflow:hidden;
|
|
2823
|
-
width:min(782px,100%);height:min(669px,calc(100% - 20px));
|
|
2876
|
+
width:min(782px,100%);height:min(669px,calc(100% - 20px));max-width:100%;max-height:calc(100% - 20px);
|
|
2877
|
+
background:var(--panel);
|
|
2824
2878
|
border:1px solid var(--line-2);border-radius:var(--r-card);
|
|
2825
2879
|
box-shadow:0 18px 60px rgba(25,25,27,.14),0 2px 6px rgba(25,25,27,.06);
|
|
2826
2880
|
animation:wr-rise .26s cubic-bezier(.2,.7,.3,1) both;}
|
|
@@ -2917,7 +2971,7 @@ body.rail-collapsed .ask-dock{left:76px;}
|
|
|
2917
2971
|
.ask-agent-log-t{flex:0 0 auto;color:var(--seq);}
|
|
2918
2972
|
.ask-agent-log-text{min-width:0;white-space:pre-wrap;overflow-wrap:anywhere;}
|
|
2919
2973
|
|
|
2920
|
-
.ask-answer{max-width:
|
|
2974
|
+
.ask-answer{max-width:100%;font-size:13.5px;line-height:1.72;color:var(--ink);}
|
|
2921
2975
|
.ask-answer-plain{white-space:pre-wrap;overflow-wrap:anywhere;}
|
|
2922
2976
|
.ask-md :is(h1,h2,h3,h4,h5,h6){margin:14px 0 6px;font-size:13.5px;font-weight:600;color:var(--ink);}
|
|
2923
2977
|
.ask-md h1{font-size:15px;}
|
|
@@ -3105,6 +3159,41 @@ body.rail-collapsed .ask-dock{left:76px;}
|
|
|
3105
3159
|
.ask-wt-row .ask-runinfo-sub.ask-wt-path{cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:260px;}
|
|
3106
3160
|
.ask-wt-row .ask-thread-trash{flex:none;}
|
|
3107
3161
|
|
|
3162
|
+
/* Resize grips (spec §10.3 addendum). Invisible edge zones INSIDE the sheet —
|
|
3163
|
+
overflow:hidden would clip anything outside it — that only change the cursor;
|
|
3164
|
+
a 2px inset line appears on hover and stays while dragging (.is-active). No
|
|
3165
|
+
bottom grip: the sheet is bottom-anchored. ask-panel.mjs applies the user's
|
|
3166
|
+
size as inline width/height and clamps it to the dock; the max-* pair on
|
|
3167
|
+
.ask-sheet above is the backstop that keeps a stale inline size inside the
|
|
3168
|
+
dock (the rail collapsing changes the dock width without a window resize).
|
|
3169
|
+
The floor (JS-only, the stylesheet default 782×669: the sheet grows, never
|
|
3170
|
+
shrinks) is not a CSS min-* on purpose — that would overflow narrow viewports
|
|
3171
|
+
that width:min(782px,100%) handles today.
|
|
3172
|
+
Corners: the sheet clips at its rounded corner (inner radius = --r-card minus
|
|
3173
|
+
the 1px border), so a square corner grip's straight inset lines fall almost
|
|
3174
|
+
entirely outside the clip and only a ~3px sliver ever shows. Each corner is
|
|
3175
|
+
therefore exactly one radius wide and carries the same outer radius, so its
|
|
3176
|
+
inset shadow bends along the arc, flush with the border like the straight
|
|
3177
|
+
edges; the straight grips start where the arc ends (--ask-corner). */
|
|
3178
|
+
.ask-resize{position:absolute;z-index:5;background:transparent;touch-action:none;--ask-grip:transparent;
|
|
3179
|
+
--ask-corner:calc(var(--r-card) - 1px);transition:box-shadow .15s;}
|
|
3180
|
+
.ask-resize:hover{--ask-grip:var(--ink-3);}
|
|
3181
|
+
.ask-resize.is-active{--ask-grip:var(--ink);}
|
|
3182
|
+
.ask-resize-n{top:0;left:var(--ask-corner);right:var(--ask-corner);height:6px;cursor:ns-resize;box-shadow:inset 0 2px 0 var(--ask-grip);}
|
|
3183
|
+
.ask-resize-e{top:var(--ask-corner);right:0;bottom:var(--ask-corner);width:6px;cursor:ew-resize;box-shadow:inset -2px 0 0 var(--ask-grip);}
|
|
3184
|
+
.ask-resize-w{top:var(--ask-corner);left:0;bottom:var(--ask-corner);width:6px;cursor:ew-resize;box-shadow:inset 2px 0 0 var(--ask-grip);}
|
|
3185
|
+
.ask-resize-ne{top:0;right:0;width:var(--ask-corner);height:var(--ask-corner);cursor:nesw-resize;
|
|
3186
|
+
border-top-right-radius:var(--ask-corner);box-shadow:inset -2px 2px 0 var(--ask-grip);}
|
|
3187
|
+
.ask-resize-nw{top:0;left:0;width:var(--ask-corner);height:var(--ask-corner);cursor:nwse-resize;
|
|
3188
|
+
border-top-left-radius:var(--ask-corner);box-shadow:inset 2px 2px 0 var(--ask-grip);}
|
|
3189
|
+
.ask-sheet.is-resizing{user-select:none;}
|
|
3190
|
+
/* The ne corner grip (one radius square, z-index:5) overlaps the top-right
|
|
3191
|
+
corner of the header's last icon button (30×30 at padding 13px/14px): the
|
|
3192
|
+
buttons sit above the grips so a click there still closes/opens instead of
|
|
3193
|
+
starting a resize, and a hurried double-click never resets the size. The
|
|
3194
|
+
grip's visible band hugs the border, well outside the button box. */
|
|
3195
|
+
.ask-header .ask-icon-btn{position:relative;z-index:6;}
|
|
3196
|
+
|
|
3108
3197
|
/* ---------- Ask Worca dock clearance ---------- */
|
|
3109
3198
|
/* The dock is a fixed overlay -- .ask-dock above: 26px dock padding-bottom plus the
|
|
3110
3199
|
~47px pill occludes a ~73px strip at the bottom of every scrollport, while the page
|
|
@@ -3459,6 +3548,8 @@ input.gv-name:focus-visible{outline:none;}
|
|
|
3459
3548
|
padding:4px 11px;font:600 11.5px var(--sans);cursor:pointer;flex:0 0 auto;white-space:nowrap;}
|
|
3460
3549
|
.gv-errchip:hover{filter:brightness(.97);}
|
|
3461
3550
|
.gv-errchip[hidden]{display:none;}
|
|
3551
|
+
/* Unfinished wiring is a to-do, not a mistake: the same chip in a quiet dress. */
|
|
3552
|
+
.gv-errchip.is-incomplete{background:var(--field);color:var(--ink-2);border-color:var(--line-2);}
|
|
3462
3553
|
/* Toolbar density: .btn-ghost/.btn-go are PAGE-level pills (13–14px of vertical
|
|
3463
3554
|
padding, 20–28px horizontal). In a 52px bar they wrap their own labels, so
|
|
3464
3555
|
the bar re-states them at .btn-mini metrics — same skin, toolbar size. */
|
|
@@ -3637,11 +3728,27 @@ input.gv-name:focus-visible{outline:none;}
|
|
|
3637
3728
|
padding:16px 20px;border-bottom:1px solid var(--line);}
|
|
3638
3729
|
.saved-head b{font-size:15px;font-weight:600;letter-spacing:-.01em;}
|
|
3639
3730
|
.saved-head .cnt{color:var(--ink-3);font-size:12.5px;font-weight:600;margin-left:9px;}
|
|
3731
|
+
.saved-head-actions{display:flex;align-items:center;gap:8px;}
|
|
3732
|
+
/* One tab per domain — the same pill tabs the History detail uses, one size down. */
|
|
3733
|
+
.gv-saved-tabs{display:flex;align-items:center;flex-wrap:wrap;gap:8px;padding:12px 20px;border-bottom:1px solid var(--line);}
|
|
3734
|
+
.gv-saved-tabs[hidden]{display:none;}
|
|
3735
|
+
.gv-saved-tab{display:flex;align-items:center;gap:8px;padding:7px 13px;border:1.5px solid var(--line);
|
|
3736
|
+
border-radius:999px;background:var(--panel);font:600 12.5px var(--sans);color:var(--ink-2);cursor:pointer;}
|
|
3737
|
+
.gv-saved-tab:hover{border-color:var(--ink-3);color:var(--ink);}
|
|
3738
|
+
.gv-saved-tab.active{background:var(--ink);color:#fff;border-color:var(--ink);}
|
|
3739
|
+
.gv-saved-tab:focus-visible{outline:2px solid var(--ink);outline-offset:2px;}
|
|
3740
|
+
.gv-saved-tab-badge{padding:1px 7px;border-radius:999px;background:var(--field);color:var(--ink-3);font:600 10.5px var(--mono);}
|
|
3741
|
+
.gv-saved-tab.active .gv-saved-tab-badge{background:var(--blue-bg);color:var(--blue-ink);}
|
|
3640
3742
|
.saved-list{display:flex;flex-direction:column;}
|
|
3641
3743
|
.gv-saved .pl-item{border-bottom:1px solid var(--line);}
|
|
3642
3744
|
.gv-saved .pl-item:last-child{border-bottom:none;}
|
|
3643
3745
|
.gv-saved .pl-row{display:flex;align-items:center;gap:14px;padding:12px 20px;transition:background .12s;}
|
|
3644
3746
|
.gv-saved .pl-row:hover{background:var(--field);}
|
|
3747
|
+
/* The row IS the Open action for a v2 pipeline. */
|
|
3748
|
+
.gv-saved .pl-row.pl-openable{cursor:pointer;}
|
|
3749
|
+
.gv-saved .pl-row.pl-openable:focus-visible{outline:2px solid var(--ink);outline-offset:-2px;}
|
|
3750
|
+
.gv-saved .pl-new{display:inline-block;margin-left:8px;padding:2px 7px;border-radius:999px;vertical-align:middle;
|
|
3751
|
+
background:var(--blue-bg);color:var(--blue-ink);font:700 10px var(--mono);letter-spacing:.06em;}
|
|
3645
3752
|
/* The thumbnail is a fixed TILE at the head of the row: the SVG carries its own
|
|
3646
3753
|
width/height, so a percentage box would leave it stranded in dead space. */
|
|
3647
3754
|
.gv-saved .pl-thumb{flex:0 0 auto;width:140px;height:54px;display:grid;place-items:center;overflow:hidden;
|
|
@@ -3654,14 +3761,16 @@ input.gv-name:focus-visible{outline:none;}
|
|
|
3654
3761
|
overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
3655
3762
|
.gv-saved .pl-meta{margin-top:4px;font-size:10.5px;font-weight:700;letter-spacing:.05em;
|
|
3656
3763
|
text-transform:uppercase;color:var(--ink-3);}
|
|
3657
|
-
.gv-saved .pl-
|
|
3764
|
+
.gv-saved .pl-export{flex:0 0 auto;padding:7px 15px;font-size:12.5px;}
|
|
3658
3765
|
.gv-saved .pl-legacy,.gv-saved .pl-origin{flex:0 0 auto;background:var(--field);border:1px solid var(--line);color:var(--ink-3);
|
|
3659
3766
|
border-radius:999px;padding:3px 10px;font-size:11px;font-weight:600;}
|
|
3660
3767
|
.gv-saved .pl-origin{font-family:var(--mono);letter-spacing:-.01em;}
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3768
|
+
/* Delete: the bin icon in the pipeline Archive button's dress (.hd-archive — red
|
|
3769
|
+
ink on a red-bg hairline pill, fills on hover), icon-only at row-action size. */
|
|
3770
|
+
.gv-saved .pl-del{flex:0 0 auto;width:34px;height:34px;border-radius:999px;border:1.5px solid var(--red-bg);
|
|
3771
|
+
background:var(--panel);display:grid;place-items:center;cursor:pointer;color:var(--red-ink);transition:.12s;}
|
|
3772
|
+
.gv-saved .pl-del svg{width:15px;height:15px;stroke-width:1.9;}
|
|
3773
|
+
.gv-saved .pl-del:hover{border-color:var(--red);background:var(--red-bg);}
|
|
3665
3774
|
.gv-saved .pl-del:focus-visible{outline:2px solid var(--ink);outline-offset:2px;}
|
|
3666
3775
|
.gv-saved .pl-empty{padding:30px 20px;text-align:center;color:var(--ink-3);font-size:13px;}
|
|
3667
3776
|
.gv-archived{display:flex;flex-wrap:wrap;align-items:center;gap:7px;
|