@beyondwork/docx-react-component 1.0.53 → 1.0.54
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/package.json +1 -1
- package/src/api/public-types.ts +35 -7
- package/src/io/docx-session.ts +30 -6
- package/src/runtime/collab/checkpoint-store.ts +1 -1
- package/src/runtime/collab/event-types.ts +4 -0
- package/src/runtime/collab/runtime-collab-sync.ts +1 -2
- package/src/runtime/document-runtime.ts +23 -9
- package/src/runtime/layout/inert-layout-facet.ts +1 -0
- package/src/runtime/layout/layout-engine-version.ts +58 -1
- package/src/runtime/layout/layout-invalidation.ts +150 -30
- package/src/runtime/layout/page-graph.ts +19 -0
- package/src/runtime/layout/paginated-layout-engine.ts +128 -19
- package/src/runtime/layout/project-block-fragments.ts +27 -0
- package/src/runtime/layout/public-facet.ts +27 -0
- package/src/runtime/render/render-frame-diff.ts +38 -2
- package/src/ui/WordReviewEditor.tsx +6 -3
- package/src/ui/headless/comment-decoration-model.ts +60 -5
- package/src/ui/headless/revision-decoration-model.ts +94 -6
- package/src/ui/shared/revision-filters.ts +16 -6
- package/src/ui-tailwind/chart/ChartSurface.tsx +236 -0
- package/src/ui-tailwind/chart/layout/axis-layout.ts +17 -9
- package/src/ui-tailwind/chart/layout/legend-layout.ts +231 -0
- package/src/ui-tailwind/chart/layout/plot-area.ts +152 -59
- package/src/ui-tailwind/chart/layout/title-layout.ts +184 -0
- package/src/ui-tailwind/chart/render/area.tsx +277 -0
- package/src/ui-tailwind/chart/render/bar-column.tsx +356 -0
- package/src/ui-tailwind/chart/render/bubble.tsx +134 -0
- package/src/ui-tailwind/chart/render/combo.tsx +85 -0
- package/src/ui-tailwind/chart/render/data-labels.tsx +513 -0
- package/src/ui-tailwind/chart/render/font-metrics.ts +298 -0
- package/src/ui-tailwind/chart/render/gridlines.ts +228 -0
- package/src/ui-tailwind/chart/render/line.tsx +363 -0
- package/src/ui-tailwind/chart/render/number-format.ts +120 -16
- package/src/ui-tailwind/chart/render/pie.tsx +275 -0
- package/src/ui-tailwind/chart/render/progressive-render.ts +103 -0
- package/src/ui-tailwind/chart/render/scatter.tsx +228 -0
- package/src/ui-tailwind/chart/render/smooth-curve.ts +101 -0
- package/src/ui-tailwind/chart/render/svg-primitives.ts +378 -0
- package/src/ui-tailwind/chart/render/unsupported.tsx +126 -0
- package/src/ui-tailwind/chrome/collab-audience-chip.tsx +11 -0
- package/src/ui-tailwind/chrome/collab-negotiation-action-bar.tsx +44 -18
- package/src/ui-tailwind/chrome/collab-presence-strip.tsx +68 -7
- package/src/ui-tailwind/chrome/collab-role-chip.tsx +21 -2
- package/src/ui-tailwind/chrome/collab-tamper-banner.tsx +20 -3
- package/src/ui-tailwind/chrome/tw-alert-banner.tsx +102 -37
- package/src/ui-tailwind/chrome/tw-command-palette.tsx +358 -0
- package/src/ui-tailwind/chrome/tw-comment-preview.tsx +108 -0
- package/src/ui-tailwind/chrome/tw-context-menu.tsx +227 -0
- package/src/ui-tailwind/chrome/tw-display-mode-selector.tsx +136 -0
- package/src/ui-tailwind/chrome/tw-empty-state.tsx +76 -0
- package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +30 -16
- package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +23 -4
- package/src/ui-tailwind/chrome/tw-paste-drop-toast.tsx +113 -0
- package/src/ui-tailwind/chrome/tw-revision-hover-preview.tsx +150 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-formatting.tsx +2 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +38 -2
- package/src/ui-tailwind/chrome/tw-selection-tool-placement.ts +15 -3
- package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +32 -20
- package/src/ui-tailwind/chrome/tw-shortcut-hint.tsx +68 -0
- package/src/ui-tailwind/chrome/tw-suggestion-card.tsx +10 -10
- package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +26 -5
- package/src/ui-tailwind/chrome/tw-table-grip-layer.tsx +29 -22
- package/src/ui-tailwind/chrome/tw-unsaved-modal.tsx +72 -10
- package/src/ui-tailwind/chrome-overlay/tw-scope-card.tsx +33 -18
- package/src/ui-tailwind/chrome-overlay/tw-table-continuation-header.tsx +94 -0
- package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +20 -7
- package/src/ui-tailwind/editor-surface/scroll-anchor.ts +93 -0
- package/src/ui-tailwind/index.ts +11 -0
- package/src/ui-tailwind/page-stack/tw-page-chrome-entry.tsx +52 -2
- package/src/ui-tailwind/page-stack/tw-page-footer-band.tsx +13 -0
- package/src/ui-tailwind/page-stack/tw-page-header-band.tsx +13 -0
- package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +8 -0
- package/src/ui-tailwind/review/tw-comment-sidebar.tsx +83 -32
- package/src/ui-tailwind/review/tw-health-panel.tsx +174 -109
- package/src/ui-tailwind/review/tw-rail-card.tsx +9 -1
- package/src/ui-tailwind/review/tw-review-rail.tsx +36 -42
- package/src/ui-tailwind/review/tw-revision-sidebar.tsx +189 -101
- package/src/ui-tailwind/review/tw-workflow-tab.tsx +11 -1
- package/src/ui-tailwind/status/tw-status-bar.tsx +114 -46
- package/src/ui-tailwind/theme/editor-theme.css +249 -22
- package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +14 -1
- package/src/ui-tailwind/toolbar/tw-shell-header.tsx +73 -32
- package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +49 -9
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +178 -14
- package/src/ui-tailwind/tw-review-workspace.tsx +39 -6
- package/src/ui-tailwind/chrome/review-queue-bar.tsx +0 -85
|
@@ -587,6 +587,14 @@
|
|
|
587
587
|
.wre-scope-rail-stripe.wre-scope-rail-label-secondary { color: var(--color-secondary); }
|
|
588
588
|
.wre-scope-rail-stripe.wre-scope-rail-label-danger { color: var(--color-danger); }
|
|
589
589
|
|
|
590
|
+
/* §3.7 canonical scope families — stripe color */
|
|
591
|
+
.wre-scope-rail-stripe.wre-scope-rail-tint-blocked { background: var(--color-scope-tint-blocked); }
|
|
592
|
+
.wre-scope-rail-stripe.wre-scope-rail-tint-in-scope { background: var(--color-scope-tint-in-scope); }
|
|
593
|
+
.wre-scope-rail-stripe.wre-scope-rail-tint-suggest { background: var(--color-scope-tint-suggest); }
|
|
594
|
+
.wre-scope-rail-stripe.wre-scope-rail-tint-comment { background: var(--color-scope-tint-comment); }
|
|
595
|
+
.wre-scope-rail-stripe.wre-scope-rail-tint-scheduled { background: var(--color-scope-tint-scheduled); }
|
|
596
|
+
.wre-scope-rail-stripe.wre-scope-rail-tint-proposed { background: var(--color-scope-tint-proposed); }
|
|
597
|
+
|
|
590
598
|
/*
|
|
591
599
|
* ─── Scope rail label pill ───
|
|
592
600
|
*
|
|
@@ -652,6 +660,38 @@
|
|
|
652
660
|
background: color-mix(in srgb, var(--color-danger) 8%, var(--color-canvas, #fff));
|
|
653
661
|
}
|
|
654
662
|
|
|
663
|
+
/* §3.7 canonical scope families — label pill */
|
|
664
|
+
.wre-scope-rail-label-blocked {
|
|
665
|
+
color: var(--color-semantic-error);
|
|
666
|
+
border-color: color-mix(in srgb, var(--color-semantic-error) 40%, transparent);
|
|
667
|
+
background: color-mix(in srgb, var(--color-semantic-error) 8%, var(--color-canvas, #fff));
|
|
668
|
+
}
|
|
669
|
+
.wre-scope-rail-label-suggest {
|
|
670
|
+
color: var(--color-semantic-warning);
|
|
671
|
+
border-color: color-mix(in srgb, var(--color-semantic-warning) 42%, transparent);
|
|
672
|
+
background: color-mix(in srgb, var(--color-semantic-warning) 8%, var(--color-canvas, #fff));
|
|
673
|
+
}
|
|
674
|
+
.wre-scope-rail-label-scheduled {
|
|
675
|
+
color: var(--color-semantic-info);
|
|
676
|
+
border-color: color-mix(in srgb, var(--color-semantic-info) 40%, transparent);
|
|
677
|
+
background: color-mix(in srgb, var(--color-semantic-info) 8%, var(--color-canvas, #fff));
|
|
678
|
+
}
|
|
679
|
+
.wre-scope-rail-label-comment {
|
|
680
|
+
color: var(--color-accent-primary);
|
|
681
|
+
border-color: color-mix(in srgb, var(--color-accent-primary) 40%, transparent);
|
|
682
|
+
background: color-mix(in srgb, var(--color-accent-primary) 8%, var(--color-canvas, #fff));
|
|
683
|
+
}
|
|
684
|
+
.wre-scope-rail-label-in-scope {
|
|
685
|
+
color: var(--color-accent-primary);
|
|
686
|
+
border-color: color-mix(in srgb, var(--color-accent-primary) 40%, transparent);
|
|
687
|
+
background: color-mix(in srgb, var(--color-accent-primary) 8%, var(--color-canvas, #fff));
|
|
688
|
+
}
|
|
689
|
+
.wre-scope-rail-label-proposed {
|
|
690
|
+
color: var(--color-accent-primary);
|
|
691
|
+
border-color: color-mix(in srgb, var(--color-accent-primary) 40%, transparent);
|
|
692
|
+
background: color-mix(in srgb, var(--color-accent-primary) 8%, var(--color-canvas, #fff));
|
|
693
|
+
}
|
|
694
|
+
|
|
655
695
|
.wre-scope-rail-label-active {
|
|
656
696
|
box-shadow: 0 0 0 1px color-mix(in srgb, currentColor 30%, transparent);
|
|
657
697
|
}
|
|
@@ -784,10 +824,14 @@
|
|
|
784
824
|
* — consumers pick whichever matches their payload shape.
|
|
785
825
|
*/
|
|
786
826
|
.wre-rail-card {
|
|
827
|
+
--wre-rail-card-edge: var(--color-border-default);
|
|
828
|
+
--wre-rail-card-eyebrow: var(--color-text-tertiary);
|
|
787
829
|
position: relative;
|
|
788
|
-
border-radius: var(--radius-
|
|
789
|
-
background: var(--color-
|
|
790
|
-
border: 1px solid
|
|
830
|
+
border-radius: var(--radius-lg);
|
|
831
|
+
background: var(--color-bg-elevated);
|
|
832
|
+
border: 1px solid var(--color-border-subtle);
|
|
833
|
+
border-left: 3px solid var(--wre-rail-card-edge);
|
|
834
|
+
box-shadow: var(--shadow-soft);
|
|
791
835
|
padding: 14px 16px 16px;
|
|
792
836
|
overflow: hidden;
|
|
793
837
|
transition: background-color var(--motion-fast) ease-out,
|
|
@@ -812,34 +856,23 @@
|
|
|
812
856
|
}
|
|
813
857
|
|
|
814
858
|
.wre-rail-card[data-tone="inReview"] {
|
|
815
|
-
--wre-rail-card-edge: var(--color-
|
|
816
|
-
--wre-rail-card-eyebrow: var(--color-
|
|
817
|
-
--wre-rail-card-progress-fill: var(--color-accent);
|
|
859
|
+
--wre-rail-card-edge: var(--color-semantic-warning);
|
|
860
|
+
--wre-rail-card-eyebrow: var(--color-semantic-warning);
|
|
818
861
|
}
|
|
819
862
|
|
|
820
863
|
.wre-rail-card[data-tone="blocked"] {
|
|
821
|
-
--wre-rail-card-edge: var(--color-
|
|
822
|
-
--wre-rail-card-eyebrow: var(--color-
|
|
823
|
-
--wre-rail-card-progress-fill: var(--color-danger);
|
|
824
|
-
background: color-mix(in srgb, var(--color-danger-soft) 75%, var(--color-surface));
|
|
864
|
+
--wre-rail-card-edge: var(--color-semantic-error);
|
|
865
|
+
--wre-rail-card-eyebrow: var(--color-semantic-error);
|
|
825
866
|
}
|
|
826
867
|
|
|
827
868
|
.wre-rail-card[data-tone="scheduled"] {
|
|
828
|
-
--wre-rail-card-edge: var(--color-
|
|
829
|
-
--wre-rail-card-eyebrow: var(--color-
|
|
830
|
-
--wre-rail-card-progress-fill: var(--color-tertiary);
|
|
869
|
+
--wre-rail-card-edge: var(--color-semantic-info);
|
|
870
|
+
--wre-rail-card-eyebrow: var(--color-semantic-info);
|
|
831
871
|
}
|
|
832
872
|
|
|
833
873
|
.wre-rail-card[data-tone="resolved"] {
|
|
834
|
-
--wre-rail-card-edge: var(--color-success);
|
|
835
|
-
--wre-rail-card-eyebrow: var(--color-success);
|
|
836
|
-
--wre-rail-card-progress-fill: var(--color-success);
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
.wre-rail-card[data-tone="neutral"] {
|
|
840
|
-
--wre-rail-card-edge: var(--color-border-strong);
|
|
841
|
-
--wre-rail-card-eyebrow: var(--color-tertiary);
|
|
842
|
-
--wre-rail-card-progress-fill: var(--color-secondary);
|
|
874
|
+
--wre-rail-card-edge: var(--color-semantic-success);
|
|
875
|
+
--wre-rail-card-eyebrow: var(--color-semantic-success);
|
|
843
876
|
}
|
|
844
877
|
|
|
845
878
|
.wre-rail-card__eyebrow {
|
|
@@ -1009,3 +1042,197 @@
|
|
|
1009
1042
|
color: var(--color-primary);
|
|
1010
1043
|
background: color-mix(in srgb, var(--color-accent-soft) 80%, var(--color-subtle));
|
|
1011
1044
|
}
|
|
1045
|
+
|
|
1046
|
+
/*
|
|
1047
|
+
* Lane 6d — Slice U1 (L8 Phase E): active H/F band chrome.
|
|
1048
|
+
*
|
|
1049
|
+
* Idle bands sit at 0.6 opacity so the header/footer content is
|
|
1050
|
+
* visible but doesn't compete with the body; hover bumps to 0.85; the
|
|
1051
|
+
* active band goes to full opacity and gains a 3 px accent ribbon
|
|
1052
|
+
* across its top edge plus a floating "Header — Section N" label in
|
|
1053
|
+
* the top-left corner so reviewers always know which band they are
|
|
1054
|
+
* editing when a section break changes the chrome.
|
|
1055
|
+
*
|
|
1056
|
+
* When any H/F story is active the chrome layer's root carries
|
|
1057
|
+
* `data-story-active="header|footer"` and the body PM surface dims to
|
|
1058
|
+
* 0.65 so the active band reads as the focal surface.
|
|
1059
|
+
*/
|
|
1060
|
+
.wre-page-band {
|
|
1061
|
+
opacity: 0.6;
|
|
1062
|
+
transition: opacity var(--motion-fast, 120ms) ease-out;
|
|
1063
|
+
pointer-events: auto;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
.wre-page-band:hover {
|
|
1067
|
+
opacity: 0.85;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
.wre-page-band[data-active="true"] {
|
|
1071
|
+
opacity: 1;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
.wre-page-band[data-active="true"]::before {
|
|
1075
|
+
content: "";
|
|
1076
|
+
position: absolute;
|
|
1077
|
+
top: 0;
|
|
1078
|
+
left: 0;
|
|
1079
|
+
right: 0;
|
|
1080
|
+
height: 3px;
|
|
1081
|
+
background: var(--color-accent);
|
|
1082
|
+
border-radius: 0 0 var(--radius-pill) var(--radius-pill);
|
|
1083
|
+
pointer-events: none;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
.wre-page-band__label {
|
|
1087
|
+
position: absolute;
|
|
1088
|
+
top: 6px;
|
|
1089
|
+
left: 10px;
|
|
1090
|
+
font-size: 10px;
|
|
1091
|
+
letter-spacing: 0.08em;
|
|
1092
|
+
text-transform: uppercase;
|
|
1093
|
+
color: var(--color-accent);
|
|
1094
|
+
background: var(--color-surface);
|
|
1095
|
+
padding: 2px 8px;
|
|
1096
|
+
border-radius: var(--radius-pill);
|
|
1097
|
+
box-shadow: var(--shadow-soft);
|
|
1098
|
+
pointer-events: none;
|
|
1099
|
+
z-index: 1;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
[data-page-stack-chrome-layer][data-story-active="header"]
|
|
1103
|
+
~ [data-pm-body-slot],
|
|
1104
|
+
[data-page-stack-chrome-layer][data-story-active="footer"]
|
|
1105
|
+
~ [data-pm-body-slot] {
|
|
1106
|
+
opacity: 0.65;
|
|
1107
|
+
transition: opacity var(--motion-fast, 120ms) ease-out;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
/*
|
|
1111
|
+
* Lane 6d — Slice S2: print variant.
|
|
1112
|
+
*
|
|
1113
|
+
* At print time, collapse all editor chrome (toolbar, rails, overlays,
|
|
1114
|
+
* page-break spacers, dock chrome) and let the browser paginate the
|
|
1115
|
+
* paper cards one per physical sheet. Intentionally no `@page` rule —
|
|
1116
|
+
* the browser's print dialog owns paper size / margins so the host
|
|
1117
|
+
* application doesn't need to fight it.
|
|
1118
|
+
*/
|
|
1119
|
+
@media print {
|
|
1120
|
+
.wre-toolbar,
|
|
1121
|
+
.wre-status-bar,
|
|
1122
|
+
.wre-review-rail,
|
|
1123
|
+
.wre-scope-rail-layer,
|
|
1124
|
+
.wre-page-chrome-widget,
|
|
1125
|
+
.wre-workspace-dock,
|
|
1126
|
+
.wre-mode-dock,
|
|
1127
|
+
[data-chrome-overlay],
|
|
1128
|
+
[data-pin-surface] {
|
|
1129
|
+
display: none !important;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
[data-paper-frame] {
|
|
1133
|
+
box-shadow: none !important;
|
|
1134
|
+
border: none !important;
|
|
1135
|
+
border-radius: 0 !important;
|
|
1136
|
+
margin: 0 !important;
|
|
1137
|
+
zoom: 1 !important;
|
|
1138
|
+
page-break-after: always;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
[data-paper-frame]:last-child {
|
|
1142
|
+
page-break-after: auto;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
.wre-page-surface {
|
|
1146
|
+
color: #000;
|
|
1147
|
+
background: #fff;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
/*
|
|
1152
|
+
* §6.23 — Scrollbar grammar (Lane 6c.S10)
|
|
1153
|
+
* Thin track, thumb low-contrast until hover. Consistent across
|
|
1154
|
+
* rail, long popovers, and the health panel.
|
|
1155
|
+
* Scoped to the editor root class to avoid leaking into host chrome.
|
|
1156
|
+
*/
|
|
1157
|
+
.wre-editor ::-webkit-scrollbar,
|
|
1158
|
+
.wre-editor *::-webkit-scrollbar {
|
|
1159
|
+
width: 6px;
|
|
1160
|
+
height: 6px;
|
|
1161
|
+
}
|
|
1162
|
+
.wre-editor ::-webkit-scrollbar-track,
|
|
1163
|
+
.wre-editor *::-webkit-scrollbar-track {
|
|
1164
|
+
background: transparent;
|
|
1165
|
+
}
|
|
1166
|
+
.wre-editor ::-webkit-scrollbar-thumb,
|
|
1167
|
+
.wre-editor *::-webkit-scrollbar-thumb {
|
|
1168
|
+
background: var(--color-border-default);
|
|
1169
|
+
border-radius: 9999px;
|
|
1170
|
+
}
|
|
1171
|
+
.wre-editor ::-webkit-scrollbar-thumb:hover,
|
|
1172
|
+
.wre-editor *::-webkit-scrollbar-thumb:hover {
|
|
1173
|
+
background: var(--color-border-strong);
|
|
1174
|
+
}
|
|
1175
|
+
.wre-editor,
|
|
1176
|
+
.wre-editor * {
|
|
1177
|
+
scrollbar-width: thin;
|
|
1178
|
+
scrollbar-color: var(--color-border-default) transparent;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
/*
|
|
1182
|
+
* §6.9 — Table grip hit area + affordance (Lane 6c.U8 / Commit C18)
|
|
1183
|
+
*
|
|
1184
|
+
* Visual stripe is 2 px; hit area is extended to 8 px via ::before pseudo-
|
|
1185
|
+
* element (3 px pad each side). On coarse-pointer devices the ::before extends
|
|
1186
|
+
* to 44 px total for WCAG 2.5.5 touch-target compliance.
|
|
1187
|
+
*
|
|
1188
|
+
* State cascade:
|
|
1189
|
+
* default → transparent background (invisible, pointer affordance only)
|
|
1190
|
+
* hover → --color-border-default (subtle stripe appears)
|
|
1191
|
+
* active → --color-accent-primary (vivid stripe during drag)
|
|
1192
|
+
*/
|
|
1193
|
+
.wre-table-grip-col {
|
|
1194
|
+
position: absolute;
|
|
1195
|
+
width: 2px;
|
|
1196
|
+
background: transparent;
|
|
1197
|
+
cursor: col-resize;
|
|
1198
|
+
transition: background 75ms ease;
|
|
1199
|
+
}
|
|
1200
|
+
.wre-table-grip-col::before {
|
|
1201
|
+
content: "";
|
|
1202
|
+
position: absolute;
|
|
1203
|
+
inset: 0 -3px;
|
|
1204
|
+
}
|
|
1205
|
+
.wre-table-grip-col:hover {
|
|
1206
|
+
background: var(--color-border-default);
|
|
1207
|
+
}
|
|
1208
|
+
.wre-table-grip-col[data-active="true"] {
|
|
1209
|
+
background: var(--color-accent-primary);
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
.wre-table-grip-row {
|
|
1213
|
+
position: absolute;
|
|
1214
|
+
height: 2px;
|
|
1215
|
+
background: transparent;
|
|
1216
|
+
cursor: row-resize;
|
|
1217
|
+
transition: background 75ms ease;
|
|
1218
|
+
}
|
|
1219
|
+
.wre-table-grip-row::before {
|
|
1220
|
+
content: "";
|
|
1221
|
+
position: absolute;
|
|
1222
|
+
inset: -3px 0;
|
|
1223
|
+
}
|
|
1224
|
+
.wre-table-grip-row:hover {
|
|
1225
|
+
background: var(--color-border-default);
|
|
1226
|
+
}
|
|
1227
|
+
.wre-table-grip-row[data-active="true"] {
|
|
1228
|
+
background: var(--color-accent-primary);
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
@media (pointer: coarse) {
|
|
1232
|
+
.wre-table-grip-col::before {
|
|
1233
|
+
inset: 0 -21px; /* 44 px total touch hit area */
|
|
1234
|
+
}
|
|
1235
|
+
.wre-table-grip-row::before {
|
|
1236
|
+
inset: -21px 0; /* 44 px total touch hit area */
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
@@ -52,7 +52,20 @@ import { preserveEditorSelectionMouseDown } from "../../ui/headless/preserve-edi
|
|
|
52
52
|
import { ROLE_ACTION_SETS } from "../chrome/role-action-sets";
|
|
53
53
|
import { TwScopePostureMenu } from "./tw-scope-posture-menu";
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Toolbar-local alias for the markup-display union. L6d.N2 widens
|
|
57
|
+
* this to match the `MarkupDisplay` surface in
|
|
58
|
+
* `comment-decoration-model.ts`, which now accepts Word's 4-mode
|
|
59
|
+
* grammar plus the legacy triple.
|
|
60
|
+
*/
|
|
61
|
+
export type MarkupDisplayMode =
|
|
62
|
+
| "all-markup"
|
|
63
|
+
| "simple-markup"
|
|
64
|
+
| "no-markup"
|
|
65
|
+
| "original"
|
|
66
|
+
| "clean"
|
|
67
|
+
| "simple"
|
|
68
|
+
| "all";
|
|
56
69
|
|
|
57
70
|
export interface WorkflowWorkItemSnapshot {
|
|
58
71
|
workItemId: string;
|
|
@@ -4,15 +4,20 @@ import * as Tabs from "@radix-ui/react-tabs";
|
|
|
4
4
|
/**
|
|
5
5
|
* TwShellHeader — the top "app chrome" bar above the document canvas.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
7
|
+
* Designsystem §6.1 — three subregions on a single 48-px row:
|
|
8
8
|
* ┌─────────────────────────────────────────────────────────────────────┐
|
|
9
|
-
* │
|
|
9
|
+
* │ LEFT │ CENTER │ RIGHT │
|
|
10
|
+
* │ brand + slot │ mode tabs (always-on) │ icon actions + CTA │
|
|
10
11
|
* └─────────────────────────────────────────────────────────────────────┘
|
|
11
12
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* Layout is CSS-grid `grid-cols-[1fr_auto_1fr]` so the center zone is
|
|
14
|
+
* always visually centred regardless of left / right slot width. The
|
|
15
|
+
* 4-mode switcher (edit / review / workflow / more) renders
|
|
16
|
+
* unconditionally — Lane 6b §6b.S1 flips it from opt-in to always-on.
|
|
17
|
+
*
|
|
18
|
+
* All colors, shadows, radius, and motion bind to the Lane 6a token
|
|
19
|
+
* substrate (`var(--color-*)` / `var(--shadow-*)` / `var(--radius-*)` /
|
|
20
|
+
* `var(--motion-*)`) — no hex literals or legacy Tailwind palette names.
|
|
16
21
|
*/
|
|
17
22
|
|
|
18
23
|
export type ShellHeaderMode = "edit" | "review" | "workflow" | "more";
|
|
@@ -40,6 +45,10 @@ export interface ShellHeaderIconAction {
|
|
|
40
45
|
|
|
41
46
|
export interface TwShellHeaderProps {
|
|
42
47
|
brand?: ReactNode;
|
|
48
|
+
/**
|
|
49
|
+
* Mode tab options. When omitted, a default 4-mode set is rendered so
|
|
50
|
+
* the center subregion always has tabs per designsystem §6.1.
|
|
51
|
+
*/
|
|
43
52
|
modes?: readonly ShellHeaderModeOption[];
|
|
44
53
|
activeMode?: ShellHeaderMode;
|
|
45
54
|
onModeChange?: (mode: ShellHeaderMode) => void;
|
|
@@ -50,23 +59,33 @@ export interface TwShellHeaderProps {
|
|
|
50
59
|
className?: string;
|
|
51
60
|
}
|
|
52
61
|
|
|
53
|
-
|
|
54
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Default 4-mode set — designsystem §6.1. Exported so hosts can extend
|
|
64
|
+
* / relabel without reconstructing the whole list.
|
|
65
|
+
*/
|
|
66
|
+
export const DEFAULT_SHELL_HEADER_MODES: readonly ShellHeaderModeOption[] = [
|
|
67
|
+
{ id: "edit", label: "Edit" },
|
|
68
|
+
{ id: "review", label: "Review" },
|
|
69
|
+
{ id: "workflow", label: "Workflow" },
|
|
70
|
+
{ id: "more", label: "More" },
|
|
71
|
+
];
|
|
55
72
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
props.brand ||
|
|
59
|
-
(props.modes && props.modes.length > 0) ||
|
|
60
|
-
(props.iconActions && props.iconActions.length > 0) ||
|
|
61
|
-
props.primaryAction;
|
|
73
|
+
const focusRingClass =
|
|
74
|
+
"focus-visible:outline-none focus-visible:shadow-[var(--shadow-focus)]";
|
|
62
75
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
76
|
+
export function TwShellHeader(props: TwShellHeaderProps): React.ReactElement {
|
|
77
|
+
const modes =
|
|
78
|
+
props.modes && props.modes.length > 0
|
|
79
|
+
? props.modes
|
|
80
|
+
: DEFAULT_SHELL_HEADER_MODES;
|
|
81
|
+
const activeMode: ShellHeaderMode = props.activeMode ?? modes[0]!.id;
|
|
66
82
|
|
|
67
83
|
const className = [
|
|
68
|
-
"
|
|
69
|
-
|
|
84
|
+
"grid h-12 shrink-0 grid-cols-[1fr_auto_1fr] items-center gap-2 px-4",
|
|
85
|
+
"bg-[var(--color-bg-chrome)]/92 backdrop-blur-sm",
|
|
86
|
+
props.isScrolled
|
|
87
|
+
? "border-b border-[var(--color-border-subtle)]"
|
|
88
|
+
: "border-b border-transparent",
|
|
70
89
|
"transition-colors duration-[var(--motion-fast)]",
|
|
71
90
|
props.className,
|
|
72
91
|
]
|
|
@@ -75,10 +94,15 @@ export function TwShellHeader(props: TwShellHeaderProps) {
|
|
|
75
94
|
|
|
76
95
|
return (
|
|
77
96
|
<header className={className} data-testid="tw-shell-header">
|
|
78
|
-
|
|
97
|
+
{/* LEFT: brand + host-supplied slot */}
|
|
98
|
+
<div
|
|
99
|
+
className="flex min-w-0 items-center gap-3"
|
|
100
|
+
data-region="left"
|
|
101
|
+
data-testid="tw-shell-header__region-left"
|
|
102
|
+
>
|
|
79
103
|
{props.brand ? (
|
|
80
104
|
<div
|
|
81
|
-
className="font-[family-name:var(--font-legal-serif)] text-[15px] font-semibold text-primary
|
|
105
|
+
className="truncate font-[family-name:var(--font-legal-serif)] text-[15px] font-semibold text-[var(--color-text-primary)]"
|
|
82
106
|
data-testid="tw-shell-header__brand"
|
|
83
107
|
>
|
|
84
108
|
{props.brand}
|
|
@@ -86,9 +110,14 @@ export function TwShellHeader(props: TwShellHeaderProps) {
|
|
|
86
110
|
) : null}
|
|
87
111
|
</div>
|
|
88
112
|
|
|
89
|
-
{
|
|
113
|
+
{/* CENTER: mode tabs (always on) */}
|
|
114
|
+
<div
|
|
115
|
+
className="flex items-center justify-center"
|
|
116
|
+
data-region="center"
|
|
117
|
+
data-testid="tw-shell-header__region-center"
|
|
118
|
+
>
|
|
90
119
|
<Tabs.Root
|
|
91
|
-
value={
|
|
120
|
+
value={activeMode}
|
|
92
121
|
onValueChange={(v: string) =>
|
|
93
122
|
props.onModeChange?.(v as ShellHeaderMode)
|
|
94
123
|
}
|
|
@@ -97,7 +126,7 @@ export function TwShellHeader(props: TwShellHeaderProps) {
|
|
|
97
126
|
aria-label="Workspace modes"
|
|
98
127
|
className="flex items-center gap-1"
|
|
99
128
|
>
|
|
100
|
-
{
|
|
129
|
+
{modes.map((mode) => (
|
|
101
130
|
<Tabs.Trigger
|
|
102
131
|
key={mode.id}
|
|
103
132
|
value={mode.id}
|
|
@@ -110,17 +139,26 @@ export function TwShellHeader(props: TwShellHeaderProps) {
|
|
|
110
139
|
))}
|
|
111
140
|
</Tabs.List>
|
|
112
141
|
</Tabs.Root>
|
|
113
|
-
|
|
114
|
-
<div aria-hidden="true" />
|
|
115
|
-
)}
|
|
142
|
+
</div>
|
|
116
143
|
|
|
117
|
-
|
|
144
|
+
{/* RIGHT: icon actions + primary CTA */}
|
|
145
|
+
<div
|
|
146
|
+
className="flex items-center justify-end gap-1"
|
|
147
|
+
data-region="right"
|
|
148
|
+
data-testid="tw-shell-header__region-right"
|
|
149
|
+
>
|
|
118
150
|
{props.iconActions?.map((action) => {
|
|
119
151
|
const commonProps = {
|
|
120
152
|
key: action.id,
|
|
121
153
|
"aria-label": action.label,
|
|
122
154
|
title: action.label,
|
|
123
|
-
className:
|
|
155
|
+
className: [
|
|
156
|
+
"inline-flex h-8 w-8 items-center justify-center rounded-[var(--radius-sm)]",
|
|
157
|
+
"text-[var(--color-text-secondary)]",
|
|
158
|
+
"transition-colors duration-[var(--motion-fast)]",
|
|
159
|
+
"hover:bg-[var(--color-bg-hover)] hover:text-[var(--color-text-primary)]",
|
|
160
|
+
focusRingClass,
|
|
161
|
+
].join(" "),
|
|
124
162
|
} as const;
|
|
125
163
|
|
|
126
164
|
if (action.href) {
|
|
@@ -145,10 +183,13 @@ export function TwShellHeader(props: TwShellHeaderProps) {
|
|
|
145
183
|
onClick={props.primaryAction.onClick}
|
|
146
184
|
data-tone={props.primaryAction.tone ?? "accent"}
|
|
147
185
|
className={[
|
|
148
|
-
"ml-2 inline-flex h-8 items-center rounded-sm px-3
|
|
186
|
+
"ml-2 inline-flex h-8 items-center rounded-[var(--radius-sm)] px-3",
|
|
187
|
+
"text-xs font-semibold",
|
|
188
|
+
"transition-colors duration-[var(--motion-fast)]",
|
|
189
|
+
"disabled:opacity-40",
|
|
149
190
|
props.primaryAction.tone === "neutral"
|
|
150
|
-
? "bg-
|
|
151
|
-
: "bg-accent text-
|
|
191
|
+
? "bg-[var(--color-bg-muted)] text-[var(--color-text-primary)] hover:bg-[var(--color-bg-hover)]"
|
|
192
|
+
: "bg-[var(--color-accent-primary)] text-[var(--color-text-on-accent)] hover:bg-[var(--color-accent-primary-hover)]",
|
|
152
193
|
focusRingClass,
|
|
153
194
|
].join(" ")}
|
|
154
195
|
data-testid="tw-shell-header__primary-action"
|
|
@@ -7,13 +7,31 @@ export interface TwToolbarIconButtonProps {
|
|
|
7
7
|
icon: React.ComponentType<{ className?: string }>;
|
|
8
8
|
label: string;
|
|
9
9
|
disabled?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Active / pressed state. Designsystem §6.2 + Lane 6b §6b.S2: active
|
|
12
|
+
* buttons paint an accent-soft tint with an accent border ring — NOT a
|
|
13
|
+
* filled accent CTA. The filled-CTA grammar is reserved for the shell
|
|
14
|
+
* header primary action only.
|
|
15
|
+
*/
|
|
10
16
|
active?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Emphasis variant — used for "stand-out" toolbar items (e.g. a pinned
|
|
19
|
+
* AI action). Paints the accent glyph color at rest; still tints on
|
|
20
|
+
* hover and on active.
|
|
21
|
+
*/
|
|
11
22
|
emphasis?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Lane 6b §6b.U6 — optional keyboard-shortcut hint rendered as a small
|
|
25
|
+
* `<kbd>` chip to the right of the label inside the tooltip. Use
|
|
26
|
+
* platform-agnostic symbols (⌘, ⇧, ⌥, ⌃) — callers format for macOS
|
|
27
|
+
* vs. Windows however they like.
|
|
28
|
+
*/
|
|
29
|
+
shortcut?: string;
|
|
12
30
|
onClick?: () => void;
|
|
13
31
|
}
|
|
14
32
|
|
|
15
33
|
const focusRingClass =
|
|
16
|
-
"focus-visible:outline-none focus-visible:
|
|
34
|
+
"focus-visible:outline-none focus-visible:shadow-[var(--shadow-focus)]";
|
|
17
35
|
|
|
18
36
|
export function TwToolbarIconButton(props: TwToolbarIconButtonProps) {
|
|
19
37
|
return (
|
|
@@ -22,17 +40,22 @@ export function TwToolbarIconButton(props: TwToolbarIconButtonProps) {
|
|
|
22
40
|
<button
|
|
23
41
|
type="button"
|
|
24
42
|
aria-label={props.label}
|
|
43
|
+
aria-pressed={props.active ?? undefined}
|
|
44
|
+
data-active={props.active ? "true" : undefined}
|
|
25
45
|
disabled={props.disabled}
|
|
26
46
|
onMouseDown={preserveEditorSelectionMouseDown}
|
|
27
47
|
onClick={props.onClick}
|
|
28
48
|
className={[
|
|
29
|
-
"inline-flex h-6 w-6 items-center justify-center rounded-
|
|
49
|
+
"inline-flex h-6 w-6 items-center justify-center rounded-[var(--radius-sm)]",
|
|
50
|
+
"border border-transparent outline-none",
|
|
51
|
+
"transition-colors duration-[var(--motion-fast)]",
|
|
30
52
|
"disabled:opacity-30 disabled:cursor-not-allowed",
|
|
31
|
-
props.
|
|
32
|
-
?
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
53
|
+
props.active
|
|
54
|
+
? // Active = underline-tint grammar: accent-soft fill, accent-primary glyph, accent border ring.
|
|
55
|
+
"bg-[var(--color-accent-soft)] text-[var(--color-accent-primary)] border-[var(--color-border-accent)]"
|
|
56
|
+
: props.emphasis
|
|
57
|
+
? "text-[var(--color-accent-primary)] hover:bg-[var(--color-bg-hover)] hover:border-[var(--color-border-subtle)]"
|
|
58
|
+
: "text-[var(--color-text-secondary)] hover:bg-[var(--color-bg-hover)] hover:text-[var(--color-text-primary)] hover:border-[var(--color-border-subtle)]",
|
|
36
59
|
focusRingClass,
|
|
37
60
|
].join(" ")}
|
|
38
61
|
>
|
|
@@ -41,10 +64,27 @@ export function TwToolbarIconButton(props: TwToolbarIconButtonProps) {
|
|
|
41
64
|
</Tooltip.Trigger>
|
|
42
65
|
<Tooltip.Portal>
|
|
43
66
|
<Tooltip.Content
|
|
44
|
-
className=
|
|
67
|
+
className={[
|
|
68
|
+
"inline-flex items-center gap-2 rounded-[var(--radius-sm)] px-2 py-1 text-xs z-50",
|
|
69
|
+
"bg-[var(--color-text-primary)] text-[var(--color-text-inverse)]",
|
|
70
|
+
"shadow-[var(--shadow-soft)]",
|
|
71
|
+
].join(" ")}
|
|
45
72
|
sideOffset={6}
|
|
46
73
|
>
|
|
47
|
-
{props.label}
|
|
74
|
+
<span>{props.label}</span>
|
|
75
|
+
{props.shortcut ? (
|
|
76
|
+
<kbd
|
|
77
|
+
className={[
|
|
78
|
+
"inline-flex items-center rounded-[var(--radius-sm)]",
|
|
79
|
+
"px-1 py-0.5 font-sans text-[10px] font-medium",
|
|
80
|
+
"border border-[var(--color-border-subtle)]/40",
|
|
81
|
+
"bg-[var(--color-bg-overlay)] text-[var(--color-text-inverse)]/80",
|
|
82
|
+
].join(" ")}
|
|
83
|
+
data-testid="tw-toolbar-icon-button__shortcut"
|
|
84
|
+
>
|
|
85
|
+
{props.shortcut}
|
|
86
|
+
</kbd>
|
|
87
|
+
) : null}
|
|
48
88
|
</Tooltip.Content>
|
|
49
89
|
</Tooltip.Portal>
|
|
50
90
|
</Tooltip.Root>
|