@worca/ui 0.37.0 → 0.40.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/app/styles.css CHANGED
@@ -10,6 +10,16 @@
10
10
  --bg: #ffffff;
11
11
  --bg-secondary: #f8fafc;
12
12
  --bg-tertiary: #f1f5f9;
13
+ /* Card / panel surface. `--surface` was used in several places
14
+ * (`.run-card`, sidebar widgets, settings cards) without ever
15
+ * being defined, so it silently resolved to `unset` → no
16
+ * background. That was invisible while the page background and
17
+ * cards were both white, but the moment a parent panel tinted
18
+ * itself gray (e.g. `.pipelines-tier-section` → `--bg-secondary`)
19
+ * the cards turned transparent. Alias it to `--bg` so cards
20
+ * always render on a white (light) / dark-slate (dark) surface
21
+ * regardless of what's behind them. */
22
+ --surface: var(--bg);
13
23
  --border: #e2e8f0;
14
24
  --border-subtle: rgba(226, 232, 240, 0.6);
15
25
  --muted: #94a3b8;
@@ -2942,7 +2952,18 @@ sl-option.template-grouped::part(label) {
2942
2952
  font-weight: 500;
2943
2953
  }
2944
2954
 
2945
- /* Description wraps below name, aligned with label */
2955
+ /* Description wraps below name, aligned with the label's left edge.
2956
+ * Shoelace renders the option as `[checked-icon] [prefix] [label] [suffix]`
2957
+ * on a single flex row. With `flex-wrap: wrap`, the suffix lands on
2958
+ * a new line — but at the parent's left edge, NOT under the label.
2959
+ * We push it back so "ID: foo" sits directly under "Foo" instead of
2960
+ * indented to the prefix slot. Shoelace's prefix slot is ~24px wide
2961
+ * by default; we use the same offset as the label's apparent x.
2962
+ *
2963
+ * The 14px is empirical: 24px ::part(base) padding-left minus the
2964
+ * ~10px the prefix slot is currently empty (no checkmark on
2965
+ * non-selected items). Adjust if Shoelace changes its inner spacing.
2966
+ */
2946
2967
  sl-option.template-grouped::part(suffix) {
2947
2968
  flex-basis: 100%;
2948
2969
  font-size: 11px;
@@ -2951,7 +2972,7 @@ sl-option.template-grouped::part(suffix) {
2951
2972
  white-space: nowrap;
2952
2973
  overflow: hidden;
2953
2974
  text-overflow: ellipsis;
2954
- padding: 0 0 4px;
2975
+ padding: 0 0 4px 14px;
2955
2976
  }
2956
2977
 
2957
2978
  /* Fix contrast on selected/current option description */
@@ -5481,6 +5502,16 @@ sl-tooltip.bead-tooltip::part(body) {
5481
5502
  text-align: center;
5482
5503
  }
5483
5504
 
5505
+ /* Transient "Saved" pill after a successful per-card save — kept subtle.
5506
+ Uses the green design-token rather than red/orange because save is a
5507
+ non-destructive confirmation; matches the badge-color-language guide
5508
+ (worca-ui/docs/badge-color-language.md): green = done. */
5509
+ .model-card-status--saved {
5510
+ color: var(--success, #22c55e);
5511
+ font-style: normal;
5512
+ font-weight: 500;
5513
+ }
5514
+
5484
5515
  .settings-tab-description {
5485
5516
  font-size: 12px;
5486
5517
  color: var(--muted);
@@ -6729,3 +6760,1048 @@ sl-dialog.markdown-dialog::part(body) {
6729
6760
  font-size: 0.8rem;
6730
6761
  word-break: break-all;
6731
6762
  }
6763
+
6764
+ /* --- Pipelines view --- */
6765
+
6766
+ .pipelines-view {
6767
+ padding: 24px 0;
6768
+ }
6769
+
6770
+ .pipelines-content {
6771
+ display: flex;
6772
+ flex-direction: column;
6773
+ gap: 24px;
6774
+ }
6775
+
6776
+ .pipelines-loading-spinner {
6777
+ display: flex;
6778
+ justify-content: center;
6779
+ padding: 48px;
6780
+ font-size: 32px;
6781
+ }
6782
+
6783
+ /*
6784
+ * Tier sections are sl-details collapsibles. The summary slot holds a
6785
+ * 2-row grid: top row [icon | TITLE | count | spacer], bottom row a
6786
+ * one-line description that wraps under the title. Shoelace's own
6787
+ * chevron sits to the right of our summary content.
6788
+ */
6789
+ .pipelines-tier-section {
6790
+ margin-bottom: 0;
6791
+ }
6792
+ .pipelines-tier-section::part(base) {
6793
+ border: 1px solid var(--border-subtle, var(--border));
6794
+ border-radius: var(--radius-lg);
6795
+ /* Match the new-run-section panel chrome so the tier sections
6796
+ * read as the same kind of grouping (gray-tinted surface holding
6797
+ * a heading + a stack of items). Previously these were on the
6798
+ * default `--surface`, which left the cards floating with no
6799
+ * visual hierarchy against the page background. */
6800
+ background: var(--bg-secondary);
6801
+ }
6802
+ .pipelines-tier-section::part(header) {
6803
+ padding: 12px 16px;
6804
+ }
6805
+ .pipelines-tier-section::part(content) {
6806
+ padding: 12px 16px 16px;
6807
+ }
6808
+
6809
+ /* The summary's flex container: lay out icon + title + count + desc. */
6810
+ .tier-section-header {
6811
+ display: grid;
6812
+ grid-template-columns: auto auto auto 1fr;
6813
+ grid-template-rows: auto auto;
6814
+ align-items: center;
6815
+ gap: 4px 10px;
6816
+ width: 100%;
6817
+ }
6818
+ .tier-section-icon {
6819
+ display: inline-flex;
6820
+ align-items: center;
6821
+ color: var(--fg-muted);
6822
+ grid-row: 1;
6823
+ }
6824
+ .tier-section-title {
6825
+ font-size: 12px;
6826
+ font-weight: 600;
6827
+ letter-spacing: 0.08em;
6828
+ text-transform: uppercase;
6829
+ color: var(--fg-muted);
6830
+ grid-row: 1;
6831
+ }
6832
+ .tier-section-count {
6833
+ grid-row: 1;
6834
+ font-size: 11px;
6835
+ justify-self: start;
6836
+ }
6837
+ .tier-section-desc {
6838
+ grid-row: 2;
6839
+ grid-column: 2 / -1;
6840
+ margin: 0;
6841
+ color: var(--fg-muted);
6842
+ font-size: 13px;
6843
+ line-height: 1.4;
6844
+ font-weight: 400;
6845
+ text-transform: none;
6846
+ letter-spacing: 0;
6847
+ }
6848
+
6849
+ /*
6850
+ * Empty-tier placeholder — sits inside an otherwise normal
6851
+ * .pipelines-tier-section so the page never collapses around an empty
6852
+ * tier. Uses the dashed Settings-style border to read as "absence,
6853
+ * not error".
6854
+ */
6855
+ .tier-section-empty {
6856
+ display: flex;
6857
+ flex-direction: column;
6858
+ gap: 6px;
6859
+ padding: 18px 20px;
6860
+ border: 1px dashed var(--border-subtle, var(--border));
6861
+ border-radius: var(--radius-lg);
6862
+ background: var(--surface);
6863
+ }
6864
+ .tier-section-empty-title {
6865
+ font-weight: 600;
6866
+ font-size: 13px;
6867
+ color: var(--fg);
6868
+ }
6869
+ .tier-section-empty-desc {
6870
+ margin: 0;
6871
+ font-size: 13px;
6872
+ line-height: 1.4;
6873
+ color: var(--fg-muted);
6874
+ }
6875
+
6876
+ .pipelines-grid {
6877
+ display: grid;
6878
+ /* Cards 50% wider than the original 320px min — fewer per row, more
6879
+ room for the description text and tag chips to breathe. The gap
6880
+ scales with the new width so the grid still feels balanced. */
6881
+ grid-template-columns: repeat(auto-fill, minmax(480px, 1fr));
6882
+ gap: 20px;
6883
+ }
6884
+
6885
+ .template-card {
6886
+ /* +50% on the original 140px to stay proportional with the wider
6887
+ grid track; longer descriptions now have headroom before the card
6888
+ needs to grow further. */
6889
+ min-height: 210px;
6890
+ }
6891
+
6892
+ /*
6893
+ * "ID: <slug>" badge — pill-shaped with a subtle gray background to
6894
+ * read as a handle/identifier chip rather than incidental text. Sits
6895
+ * inline with the file-text icon, name, and Default badge on the
6896
+ * card's top row (mirrors the tier section header layout). Uses
6897
+ * Settings-style tokens (--bg-secondary, --border-subtle) so it
6898
+ * lands in the same visual family as our other chips and badges.
6899
+ */
6900
+ .template-card-id-badge {
6901
+ display: inline-flex;
6902
+ align-items: baseline;
6903
+ gap: 4px;
6904
+ padding: 2px 8px;
6905
+ border-radius: 9999px;
6906
+ background: var(--bg-secondary);
6907
+ border: 1px solid var(--border-subtle, var(--border));
6908
+ max-width: max-content;
6909
+ min-width: 0;
6910
+ }
6911
+ .template-card-id-label {
6912
+ font-size: 10px;
6913
+ font-weight: 600;
6914
+ letter-spacing: 0.06em;
6915
+ text-transform: uppercase;
6916
+ color: var(--fg-muted);
6917
+ flex-shrink: 0;
6918
+ }
6919
+ .template-card-id {
6920
+ font-family: var(--sl-font-mono);
6921
+ font-size: 12px;
6922
+ color: var(--fg);
6923
+ background: transparent;
6924
+ padding: 0;
6925
+ white-space: nowrap;
6926
+ overflow: hidden;
6927
+ text-overflow: ellipsis;
6928
+ }
6929
+
6930
+ /*
6931
+ * Template cards inherit `cursor: pointer` and a hover lift from the
6932
+ * base `.run-card` rule. The `--clickable` modifier is added by the
6933
+ * view only when an edit/duplicate handler is wired up; in the degraded
6934
+ * read-only state we strip those affordances so the card looks (and
6935
+ * feels) inert.
6936
+ */
6937
+ .template-card:not(.template-card--clickable) {
6938
+ cursor: default;
6939
+ }
6940
+ .template-card:not(.template-card--clickable):hover {
6941
+ background: var(--surface);
6942
+ box-shadow: none;
6943
+ transform: none;
6944
+ }
6945
+ .template-card--clickable:focus-visible {
6946
+ outline: 2px solid var(--accent, #4f9eff);
6947
+ outline-offset: 2px;
6948
+ }
6949
+
6950
+ .template-default-badge {
6951
+ margin-right: 8px;
6952
+ font-weight: 600;
6953
+ }
6954
+
6955
+ /*
6956
+ * Top row is just icon + name + (optional) ★ Default. The shared
6957
+ * `.run-card-title { flex: 1 }` rule does the right thing here:
6958
+ * the title soaks up the horizontal space and pushes the Default
6959
+ * badge to the right edge.
6960
+ *
6961
+ * The "ID:slug" handle and the description below it are both
6962
+ * indented to align with the title's left edge (i.e. past the
6963
+ * icon's column). The icon is 16px wide and the .run-card-top
6964
+ * gap is 10px, so 26px of left padding aligns subsequent rows
6965
+ * with the title.
6966
+ */
6967
+ .template-card-id-row {
6968
+ display: flex;
6969
+ margin-top: 6px;
6970
+ padding-left: 26px;
6971
+ }
6972
+ .template-card .run-card-meta {
6973
+ padding-left: 26px;
6974
+ }
6975
+
6976
+ /*
6977
+ * Buttons pin to the card's bottom so cards of varying heights
6978
+ * (description length, badge presence) line their action rows up
6979
+ * with each other in a tier-section grid. `margin-top: auto` on
6980
+ * the last child of a column-flex container absorbs the extra
6981
+ * vertical space.
6982
+ */
6983
+ .template-card .run-card-actions {
6984
+ margin-top: auto;
6985
+ }
6986
+
6987
+ .template-tier-badge {
6988
+ font-size: 12px;
6989
+ text-transform: uppercase;
6990
+ letter-spacing: 0.04em;
6991
+ }
6992
+
6993
+ .template-card .run-card-stages sl-tag {
6994
+ margin-right: 4px;
6995
+ margin-bottom: 4px;
6996
+ }
6997
+
6998
+ .pipelines-empty {
6999
+ display: flex;
7000
+ flex-direction: column;
7001
+ align-items: center;
7002
+ justify-content: center;
7003
+ text-align: center;
7004
+ padding: 64px 24px;
7005
+ gap: 16px;
7006
+ color: var(--fg-muted);
7007
+ }
7008
+
7009
+ .pipelines-empty .empty-state-icon {
7010
+ color: var(--fg-muted);
7011
+ opacity: 0.5;
7012
+ margin-bottom: 8px;
7013
+ }
7014
+
7015
+ .pipelines-empty h2 {
7016
+ margin: 0;
7017
+ color: var(--fg);
7018
+ font-size: 20px;
7019
+ font-weight: 600;
7020
+ }
7021
+
7022
+ .pipelines-empty p {
7023
+ max-width: 480px;
7024
+ margin: 0 0 16px;
7025
+ line-height: 1.5;
7026
+ color: var(--fg-muted);
7027
+ }
7028
+
7029
+ .pipelines-empty .empty-state-actions {
7030
+ display: flex;
7031
+ gap: 8px;
7032
+ margin-bottom: 8px;
7033
+ }
7034
+
7035
+ .pipelines-empty .empty-state-hint {
7036
+ font-size: 13px;
7037
+ color: var(--fg-muted);
7038
+ border-top: 1px solid var(--border);
7039
+ padding-top: 16px;
7040
+ }
7041
+
7042
+ /* Deep-link card for template-driven settings (W-062 Phase 6) */
7043
+ .pipelines-deep-link-card {
7044
+ border: 1px solid var(--border-subtle);
7045
+ border-radius: var(--radius-lg);
7046
+ background: var(--bg-secondary);
7047
+ padding: 16px;
7048
+ margin-bottom: 24px;
7049
+ box-shadow: var(--shadow-sm);
7050
+ }
7051
+
7052
+ .pipelines-deep-link-content {
7053
+ display: flex;
7054
+ align-items: center;
7055
+ gap: 16px;
7056
+ }
7057
+
7058
+ .pipelines-deep-link-icon {
7059
+ display: flex;
7060
+ align-items: center;
7061
+ justify-content: center;
7062
+ width: 40px;
7063
+ height: 40px;
7064
+ background: var(--bg-tertiary);
7065
+ border-radius: var(--radius);
7066
+ color: var(--accent);
7067
+ flex-shrink: 0;
7068
+ }
7069
+
7070
+ .pipelines-deep-link-text {
7071
+ flex: 1;
7072
+ }
7073
+
7074
+ .pipelines-deep-link-title {
7075
+ font-size: 14px;
7076
+ font-weight: 600;
7077
+ color: var(--fg);
7078
+ margin-bottom: 4px;
7079
+ }
7080
+
7081
+ .pipelines-deep-link-desc {
7082
+ font-size: 13px;
7083
+ color: var(--muted);
7084
+ line-height: 1.4;
7085
+ }
7086
+
7087
+ .pipelines-deep-link-btn {
7088
+ display: inline-flex;
7089
+ align-items: center;
7090
+ gap: 8px;
7091
+ padding: 8px 16px;
7092
+ font-size: 13px;
7093
+ font-weight: 500;
7094
+ color: var(--fg);
7095
+ background: var(--bg-tertiary);
7096
+ border: 1px solid var(--border);
7097
+ border-radius: var(--radius);
7098
+ text-decoration: none;
7099
+ cursor: pointer;
7100
+ transition: var(--transition-fast);
7101
+ white-space: nowrap;
7102
+ }
7103
+
7104
+ .pipelines-deep-link-btn:hover {
7105
+ background: var(--bg);
7106
+ border-color: var(--border-subtle);
7107
+ color: var(--fg-active);
7108
+ }
7109
+
7110
+
7111
+ /* ─── Pipelines Editor ─────────────────────────────────────────────── */
7112
+
7113
+ .pipelines-editor {
7114
+ display: flex;
7115
+ flex-direction: column;
7116
+ height: 100%;
7117
+ max-height: calc(100vh - 120px);
7118
+ }
7119
+
7120
+ /*
7121
+ * Editor sub-header: lives below the page chrome's "Edit Template" title.
7122
+ * Shows the template name + scope badge on the left, view-mode toggle on
7123
+ * the right. No back button here — the page chrome already provides one.
7124
+ */
7125
+ .editor-subheader {
7126
+ display: flex;
7127
+ align-items: center;
7128
+ gap: 12px;
7129
+ padding: 12px 16px;
7130
+ border-bottom: 1px solid var(--border);
7131
+ background: var(--bg);
7132
+ position: sticky;
7133
+ top: 0;
7134
+ z-index: 10;
7135
+ }
7136
+
7137
+ .editor-subheader-title-group {
7138
+ display: flex;
7139
+ align-items: center;
7140
+ gap: 8px;
7141
+ flex: 1;
7142
+ min-width: 0;
7143
+ }
7144
+
7145
+ /*
7146
+ * Inline editable Name + ID + read-only Storage in the editor
7147
+ * subheader. All three share the same gray-pill chassis
7148
+ * (`.editor-field-pill`) so the row reads as a single field group;
7149
+ * size/weight variants live on the per-pill modifier class.
7150
+ *
7151
+ * Name is the widest (flex-grow), ID is mid-width with a monospace
7152
+ * input, Storage is a fixed compact pill showing the tier.
7153
+ */
7154
+ .editor-field-pill {
7155
+ display: inline-flex;
7156
+ align-items: center;
7157
+ gap: 4px;
7158
+ padding: 2px 4px 2px 8px;
7159
+ border-radius: 9999px;
7160
+ background: var(--bg-secondary);
7161
+ border: 1px solid var(--border-subtle, var(--border));
7162
+ min-width: 0;
7163
+ }
7164
+ .editor-field-pill-label {
7165
+ font-size: 10px;
7166
+ font-weight: 600;
7167
+ letter-spacing: 0.06em;
7168
+ text-transform: uppercase;
7169
+ color: var(--fg-muted);
7170
+ flex-shrink: 0;
7171
+ }
7172
+
7173
+ .editor-name-pill {
7174
+ flex: 1 1 240px;
7175
+ padding-right: 4px;
7176
+ }
7177
+ .editor-name-input {
7178
+ flex: 1;
7179
+ min-width: 0;
7180
+ }
7181
+ .editor-name-input::part(form-control-input) {
7182
+ font-weight: 600;
7183
+ }
7184
+ .editor-name-input::part(base) {
7185
+ border: none;
7186
+ background: transparent;
7187
+ box-shadow: none;
7188
+ min-height: 22px;
7189
+ }
7190
+
7191
+ .editor-id-badge {
7192
+ flex: 0 1 240px;
7193
+ }
7194
+ .editor-id-input {
7195
+ flex: 1;
7196
+ min-width: 0;
7197
+ }
7198
+ .editor-id-input::part(form-control-input) {
7199
+ font-family: var(--sl-font-mono);
7200
+ font-size: 12px;
7201
+ padding: 0 6px;
7202
+ }
7203
+ .editor-id-input::part(base) {
7204
+ border: none;
7205
+ background: transparent;
7206
+ box-shadow: none;
7207
+ min-height: 22px;
7208
+ }
7209
+
7210
+ .editor-storage-pill {
7211
+ flex: 0 0 auto;
7212
+ padding: 2px 10px 2px 8px;
7213
+ }
7214
+
7215
+ /*
7216
+ * Description section sits above the tab group inside the scroll
7217
+ * area — outside any tab so it always shows. Matches the
7218
+ * settings-section header style (h3.settings-section-title +
7219
+ * settings-field + label + textarea) so the editor reads like
7220
+ * the rest of the app.
7221
+ */
7222
+ .editor-description-section {
7223
+ display: flex;
7224
+ flex-direction: column;
7225
+ gap: 12px;
7226
+ padding-bottom: 16px;
7227
+ margin-bottom: 4px;
7228
+ }
7229
+ .editor-description-input {
7230
+ width: 100%;
7231
+ }
7232
+ .editor-description-input::part(textarea) {
7233
+ font-size: 13px;
7234
+ line-height: 1.5;
7235
+ color: var(--fg);
7236
+ }
7237
+ .editor-storage-value {
7238
+ font-size: 12px;
7239
+ font-weight: 500;
7240
+ color: var(--fg);
7241
+ text-transform: capitalize;
7242
+ }
7243
+
7244
+ /*
7245
+ * Visual error state on a field pill — used when the ID collides
7246
+ * with an existing template in the same tier. The border turns
7247
+ * warning-amber to match the inline badge that appears next to
7248
+ * the input and the disabled Save button.
7249
+ */
7250
+ .editor-field-pill--invalid {
7251
+ border-color: var(--sl-color-warning-600, #d97706);
7252
+ }
7253
+ .editor-id-collision-badge {
7254
+ flex-shrink: 0;
7255
+ }
7256
+
7257
+ .editor-subheader-title {
7258
+ font-size: 16px;
7259
+ font-weight: 600;
7260
+ margin: 0;
7261
+ color: var(--fg);
7262
+ white-space: nowrap;
7263
+ overflow: hidden;
7264
+ text-overflow: ellipsis;
7265
+ }
7266
+
7267
+ .icon-btn {
7268
+ background: none;
7269
+ border: none;
7270
+ padding: 4px;
7271
+ border-radius: 4px;
7272
+ cursor: pointer;
7273
+ color: var(--fg-muted);
7274
+ transition: var(--transition-fast);
7275
+ }
7276
+
7277
+ .icon-btn:hover {
7278
+ background: var(--bg-secondary);
7279
+ color: var(--fg);
7280
+ }
7281
+
7282
+ .editor-mode-toggle {
7283
+ margin-left: auto;
7284
+ }
7285
+
7286
+ /*
7287
+ * Read-only badge in the editor subheader. Sits next to the tier
7288
+ * badge whenever we're viewing a built-in template; visually warns
7289
+ * the user that nothing they type here will save.
7290
+ */
7291
+ .editor-readonly-badge {
7292
+ margin-left: 4px;
7293
+ }
7294
+
7295
+ /*
7296
+ * Editor content gate: when readOnly, the form controls inside each
7297
+ * tab panel (and the JSON view) become non-interactive — but the
7298
+ * tab nav stays clickable so users can move between Models /
7299
+ * Stages / Agents / etc. to inspect the whole template. We scope
7300
+ * pointer-events:none to the panels (not the whole content) so the
7301
+ * `<sl-tab slot="nav">` items still take clicks.
7302
+ *
7303
+ * `aria-disabled="true"` on the wrapper tells assistive tech the
7304
+ * surface is locked; the per-panel muted opacity signals "look,
7305
+ * don't touch". Inputs visually keep their value so users can still
7306
+ * read and copy from them.
7307
+ *
7308
+ * Cheaper than threading a `disabled` flag through every section
7309
+ * and every input; tradeoff is screen readers won't announce each
7310
+ * control as disabled — only the panel container. Acceptable for a
7311
+ * read-only inspector mode.
7312
+ */
7313
+ .editor-content--readonly sl-tab-panel,
7314
+ .editor-content--readonly .editor-section--json {
7315
+ pointer-events: none;
7316
+ opacity: 0.7;
7317
+ user-select: text; /* let users still highlight + copy values */
7318
+ }
7319
+ .editor-content--readonly sl-tab-panel *,
7320
+ .editor-content--readonly .editor-section--json * {
7321
+ cursor: default !important;
7322
+ }
7323
+
7324
+ /*
7325
+ * Editor uses the same .settings-tab-content / .settings-section-title /
7326
+ * .settings-cards primitives as Project Settings — define one supporting
7327
+ * helper here (the lead-in paragraph under a section title) and a few
7328
+ * tab-group tweaks so the editor's tab nav sits naturally inside its
7329
+ * scroll container.
7330
+ */
7331
+ .settings-section-desc {
7332
+ margin: -2px 0 14px;
7333
+ color: var(--fg-muted);
7334
+ font-size: 13px;
7335
+ line-height: 1.5;
7336
+ }
7337
+ .settings-section-desc code {
7338
+ background: var(--bg-secondary);
7339
+ padding: 1px 5px;
7340
+ border-radius: 4px;
7341
+ font-size: 12px;
7342
+ }
7343
+
7344
+ .editor-tab-group {
7345
+ --indicator-color: var(--accent, var(--sl-color-primary-600, #4f9eff));
7346
+ /* Keep Shoelace's default track color (subtle border under the tab
7347
+ * row) so the editor matches the Project Settings convention of a
7348
+ * horizontal line between the nav and the panel content. Used to
7349
+ * be `transparent` which suppressed that divider. */
7350
+ }
7351
+ .editor-tab-group::part(base) {
7352
+ /* Hand the scroll to .editor-content so the tab nav stays sticky. */
7353
+ height: 100%;
7354
+ }
7355
+ /*
7356
+ * Tab nav style mirrors the project Settings tabs: 13px medium,
7357
+ * 10/16 padding, and a clear gap between the icon and the label
7358
+ * so the icon doesn't visually fuse with the first letter.
7359
+ */
7360
+ .editor-tab-group sl-tab::part(base) {
7361
+ font-size: 13px;
7362
+ font-weight: 500;
7363
+ padding: 10px 16px;
7364
+ gap: 6px;
7365
+ display: flex;
7366
+ align-items: center;
7367
+ }
7368
+ .editor-tab-group sl-tab svg {
7369
+ width: 14px;
7370
+ height: 14px;
7371
+ flex-shrink: 0;
7372
+ }
7373
+ .editor-tab-group sl-tab-panel::part(base) {
7374
+ padding: 16px 4px 0;
7375
+ }
7376
+
7377
+ /*
7378
+ * Pipeline tab holds three sections (Stage configuration, Loop
7379
+ * limits, Circuit breaker) on one panel — the same shape as the
7380
+ * Project Settings → Pipeline tab. Each section is its own
7381
+ * `.settings-tab-content` flex column with gap:20px between fields
7382
+ * but no margin above the section heading, so without an explicit
7383
+ * separator they jam right up against each other. Give the three
7384
+ * direct children real vertical breathing room.
7385
+ */
7386
+ .editor-pipeline-tab {
7387
+ display: flex;
7388
+ flex-direction: column;
7389
+ gap: 32px;
7390
+ }
7391
+ .editor-pipeline-tab > .settings-tab-content + .settings-tab-content {
7392
+ /* Visual separator on top of the gap so it reads as a fresh
7393
+ * section rather than a continuation of the previous one. */
7394
+ border-top: 1px solid var(--border-subtle, var(--border));
7395
+ padding-top: 24px;
7396
+ }
7397
+
7398
+ /*
7399
+ * Shared chrome for the template action dialogs (Create / Duplicate /
7400
+ * Rename / Import). Wraps the Settings-style form primitives so the
7401
+ * dialog body looks like a one-screen mini Settings tab.
7402
+ */
7403
+ .template-action-dialog::part(panel) {
7404
+ min-width: min(520px, 100vw - 32px);
7405
+ }
7406
+ .template-action-dialog .dialog-lead {
7407
+ margin: 0 0 14px;
7408
+ color: var(--fg-muted);
7409
+ font-size: 13px;
7410
+ line-height: 1.5;
7411
+ }
7412
+ .template-action-dialog .dialog-lead code {
7413
+ background: var(--bg-secondary);
7414
+ padding: 1px 5px;
7415
+ border-radius: 4px;
7416
+ font-size: 12px;
7417
+ }
7418
+ .template-action-dialog .settings-field {
7419
+ margin-bottom: 14px;
7420
+ }
7421
+ .template-action-dialog .dialog-error {
7422
+ margin-top: 8px;
7423
+ }
7424
+ .template-action-dialog .dialog-bundle-list {
7425
+ margin: 4px 0 0;
7426
+ padding-left: 18px;
7427
+ font-size: 13px;
7428
+ color: var(--fg-muted);
7429
+ }
7430
+ .template-action-dialog .dialog-bundle-list code {
7431
+ font-family: var(--sl-font-mono);
7432
+ color: var(--fg);
7433
+ }
7434
+
7435
+ .editor-content {
7436
+ flex: 1;
7437
+ overflow-y: auto;
7438
+ padding: 16px;
7439
+ display: flex;
7440
+ flex-direction: column;
7441
+ gap: 24px;
7442
+ }
7443
+
7444
+ /* Editor Sections */
7445
+
7446
+ .editor-section {
7447
+ max-width: 900px;
7448
+ }
7449
+
7450
+ .editor-section--json {
7451
+ max-width: 1000px;
7452
+ }
7453
+
7454
+ .section-header {
7455
+ display: flex;
7456
+ align-items: center;
7457
+ justify-content: space-between;
7458
+ margin-bottom: 12px;
7459
+ }
7460
+
7461
+ .section-title {
7462
+ font-size: 15px;
7463
+ font-weight: 600;
7464
+ margin: 0;
7465
+ color: var(--fg);
7466
+ }
7467
+
7468
+ /* Stages List */
7469
+
7470
+ .stages-list {
7471
+ display: flex;
7472
+ flex-direction: column;
7473
+ gap: 8px;
7474
+ }
7475
+
7476
+ .stage-row {
7477
+ display: flex;
7478
+ align-items: center;
7479
+ gap: 12px;
7480
+ padding: 12px;
7481
+ background: var(--bg-secondary);
7482
+ border: 1px solid var(--border);
7483
+ border-radius: var(--radius);
7484
+ transition: var(--transition-fast);
7485
+ }
7486
+
7487
+ .stage-row--disabled {
7488
+ opacity: 0.5;
7489
+ }
7490
+
7491
+ .stage-row-info {
7492
+ display: flex;
7493
+ align-items: center;
7494
+ gap: 8px;
7495
+ flex: 1;
7496
+ }
7497
+
7498
+ .stage-name {
7499
+ font-weight: 500;
7500
+ font-family: var(--sl-font-mono);
7501
+ }
7502
+
7503
+ .stage-row.disabled .stage-name {
7504
+ text-decoration: line-through;
7505
+ }
7506
+
7507
+ .stage-row-agent {
7508
+ width: 200px;
7509
+ }
7510
+
7511
+ /* Agents Matrix */
7512
+
7513
+ .agents-matrix {
7514
+ display: flex;
7515
+ flex-direction: column;
7516
+ gap: 8px;
7517
+ }
7518
+
7519
+ .agent-row {
7520
+ display: flex;
7521
+ align-items: center;
7522
+ gap: 12px;
7523
+ padding: 10px 12px;
7524
+ background: var(--bg-secondary);
7525
+ border: 1px solid var(--border);
7526
+ border-radius: var(--radius);
7527
+ }
7528
+
7529
+ .agent-name {
7530
+ flex: 0 0 100px;
7531
+ font-weight: 500;
7532
+ font-family: var(--sl-font-mono);
7533
+ }
7534
+
7535
+ .agent-fields {
7536
+ display: grid;
7537
+ grid-template-columns: 1fr 1fr 1fr;
7538
+ gap: 8px;
7539
+ flex: 1;
7540
+ }
7541
+
7542
+ .agent-field {
7543
+ display: flex;
7544
+ flex-direction: column;
7545
+ gap: 4px;
7546
+ }
7547
+
7548
+ /* Loops Grid */
7549
+
7550
+ .loops-grid {
7551
+ display: grid;
7552
+ grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
7553
+ gap: 12px;
7554
+ }
7555
+
7556
+ .loop-field {
7557
+ display: flex;
7558
+ flex-direction: column;
7559
+ gap: 4px;
7560
+ }
7561
+
7562
+ /* Circuit Breaker Grid */
7563
+
7564
+ .circuit-breaker-grid {
7565
+ display: grid;
7566
+ grid-template-columns: 1fr 1fr;
7567
+ gap: 16px;
7568
+ }
7569
+
7570
+ .cb-field {
7571
+ display: flex;
7572
+ flex-direction: column;
7573
+ gap: 8px;
7574
+ }
7575
+
7576
+ .cb-field-row {
7577
+ display: flex;
7578
+ align-items: center;
7579
+ gap: 8px;
7580
+ }
7581
+
7582
+ /* Governance */
7583
+
7584
+ .governance-content {
7585
+ display: flex;
7586
+ flex-direction: column;
7587
+ gap: 24px;
7588
+ }
7589
+
7590
+ /* JSON Editor */
7591
+
7592
+ .json-editor-wrapper {
7593
+ background: var(--bg-secondary);
7594
+ border: 1px solid var(--border);
7595
+ border-radius: var(--radius);
7596
+ padding: 4px;
7597
+ }
7598
+
7599
+ .json-editor {
7600
+ font-family: var(--sl-font-mono);
7601
+ font-size: 13px;
7602
+ line-height: 1.5;
7603
+ min-height: 300px;
7604
+ resize: vertical;
7605
+ }
7606
+
7607
+ .json-editor-hint {
7608
+ font-size: 12px;
7609
+ color: var(--fg-muted);
7610
+ margin-top: 8px;
7611
+ }
7612
+
7613
+ /* Diff view styles */
7614
+ .diff-hint {
7615
+ font-size: 13px;
7616
+ color: var(--fg-muted);
7617
+ margin-bottom: 16px;
7618
+ line-height: 1.5;
7619
+ }
7620
+
7621
+ .diff-reset-icon {
7622
+ display: inline-block;
7623
+ vertical-align: middle;
7624
+ margin: 0 4px;
7625
+ color: var(--accent);
7626
+ }
7627
+
7628
+ .diff-no-changes {
7629
+ text-align: center;
7630
+ padding: 48px 24px;
7631
+ color: var(--status-completed);
7632
+ }
7633
+
7634
+ .diff-no-changes svg {
7635
+ margin-bottom: 16px;
7636
+ color: var(--status-completed);
7637
+ }
7638
+
7639
+ .diff-no-changes h3 {
7640
+ margin: 0 0 8px 0;
7641
+ font-size: 16px;
7642
+ font-weight: 600;
7643
+ }
7644
+
7645
+ .diff-no-changes p {
7646
+ margin: 0;
7647
+ color: var(--fg-muted);
7648
+ }
7649
+
7650
+ .diff-empty,
7651
+ .diff-loading {
7652
+ padding: 24px;
7653
+ text-align: center;
7654
+ color: var(--fg-muted);
7655
+ }
7656
+
7657
+ .diff-loading {
7658
+ display: flex;
7659
+ align-items: center;
7660
+ justify-content: center;
7661
+ gap: 12px;
7662
+ }
7663
+
7664
+ .diff-table-container {
7665
+ overflow-x: auto;
7666
+ border: 1px solid var(--border);
7667
+ border-radius: var(--radius);
7668
+ }
7669
+
7670
+ .diff-table {
7671
+ width: 100%;
7672
+ border-collapse: collapse;
7673
+ font-size: 13px;
7674
+ }
7675
+
7676
+ .diff-table th {
7677
+ text-align: left;
7678
+ padding: 10px 12px;
7679
+ background: var(--bg-secondary);
7680
+ border-bottom: 2px solid var(--border);
7681
+ font-weight: 600;
7682
+ color: var(--fg);
7683
+ position: sticky;
7684
+ top: 0;
7685
+ }
7686
+
7687
+ .diff-table td {
7688
+ padding: 10px 12px;
7689
+ border-bottom: 1px solid var(--border-subtle);
7690
+ vertical-align: top;
7691
+ }
7692
+
7693
+ .diff-row--changed {
7694
+ background: rgba(251, 191, 36, 0.05);
7695
+ }
7696
+
7697
+ .diff-row--changed .diff-path {
7698
+ font-weight: 600;
7699
+ }
7700
+
7701
+ .diff-path code {
7702
+ display: block;
7703
+ font-family: ui-monospace, 'SF Mono', 'Menlo', monospace;
7704
+ font-size: 11px;
7705
+ color: var(--fg-muted);
7706
+ margin-bottom: 4px;
7707
+ }
7708
+
7709
+ .diff-label {
7710
+ display: block;
7711
+ font-size: 12px;
7712
+ color: var(--fg);
7713
+ }
7714
+
7715
+ .diff-value {
7716
+ font-family: ui-monospace, 'SF Mono', 'Menlo', monospace;
7717
+ font-size: 12px;
7718
+ max-width: 300px;
7719
+ overflow-x: auto;
7720
+ white-space: pre-wrap;
7721
+ word-break: break-all;
7722
+ }
7723
+
7724
+ .diff-value--builtin {
7725
+ color: var(--fg-muted);
7726
+ background: var(--bg-tertiary);
7727
+ padding: 4px 8px;
7728
+ border-radius: 4px;
7729
+ }
7730
+
7731
+ .diff-value--current {
7732
+ color: var(--fg);
7733
+ background: rgba(59, 130, 246, 0.1);
7734
+ padding: 4px 8px;
7735
+ border-radius: 4px;
7736
+ }
7737
+
7738
+ .diff-actions {
7739
+ white-space: nowrap;
7740
+ }
7741
+
7742
+ /* Common field styles */
7743
+
7744
+ .field-label {
7745
+ font-size: 12px;
7746
+ font-weight: 500;
7747
+ color: var(--fg-muted);
7748
+ }
7749
+
7750
+ .field-hint {
7751
+ font-size: 12px;
7752
+ color: var(--fg-muted);
7753
+ }
7754
+
7755
+ /* Footer */
7756
+
7757
+ .editor-footer {
7758
+ display: flex;
7759
+ align-items: center;
7760
+ justify-content: flex-end;
7761
+ gap: 8px;
7762
+ padding: 12px 16px;
7763
+ border-top: 1px solid var(--border);
7764
+ background: var(--bg);
7765
+ position: sticky;
7766
+ bottom: 0;
7767
+ z-index: 10;
7768
+ }
7769
+
7770
+ /* Save alert */
7771
+
7772
+ .editor-save-alert {
7773
+ margin-bottom: 16px;
7774
+ }
7775
+
7776
+ .editor-validation-alert {
7777
+ margin-bottom: 16px;
7778
+ }
7779
+
7780
+ .validation-list {
7781
+ margin: 8px 0 0 16px;
7782
+ padding-left: 20px;
7783
+ font-size: 13px;
7784
+ }
7785
+
7786
+ .validation-list li {
7787
+ margin-bottom: 4px;
7788
+ }
7789
+
7790
+ .validation-list code {
7791
+ font-family: var(--sl-font-mono);
7792
+ background: var(--bg-tertiary);
7793
+ padding: 2px 4px;
7794
+ border-radius: 3px;
7795
+ }
7796
+
7797
+ /* Loading state */
7798
+
7799
+ .editor-loading {
7800
+ display: flex;
7801
+ align-items: center;
7802
+ justify-content: center;
7803
+ padding: 40px;
7804
+ font-size: 14px;
7805
+ color: var(--fg-muted);
7806
+ gap: 8px;
7807
+ }