foundry-design-web-adapter 0.2.0-beta.2 → 0.2.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -8
- package/THIRD_PARTY_NOTICES.md +14 -0
- package/dist/adapter.js +1111 -217
- package/dist/index.d.ts.map +1 -1
- package/dist/keyline-icons.d.ts.map +1 -1
- package/dist/ui-foundations.d.ts +8 -0
- package/dist/ui-foundations.d.ts.map +1 -0
- package/dist/workspace.d.ts +10 -4
- package/dist/workspace.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/adapter.js
CHANGED
|
@@ -797,7 +797,15 @@ var data38 = {
|
|
|
797
797
|
var x_default = data38;
|
|
798
798
|
|
|
799
799
|
// src/keyline-icons.ts
|
|
800
|
-
|
|
800
|
+
function normalizeKeylineIcon(icon) {
|
|
801
|
+
const onePixelStroke = icon.body.replace(/stroke-width="[^"]+"/g, 'stroke-width="1"');
|
|
802
|
+
const nonScalingStroke = onePixelStroke.replace(
|
|
803
|
+
/<(path|circle|rect|line|polyline|polygon|ellipse)\b(?![^>]*vector-effect)/g,
|
|
804
|
+
'<$1 vector-effect="non-scaling-stroke"'
|
|
805
|
+
);
|
|
806
|
+
return { ...icon, body: nonScalingStroke };
|
|
807
|
+
}
|
|
808
|
+
var RAW_KEYLINE_ICONS = {
|
|
801
809
|
accessibility: user_check_default,
|
|
802
810
|
activity: activity_default,
|
|
803
811
|
"align-horizontal-space-around": align_offset_left_default,
|
|
@@ -837,6 +845,12 @@ var KEYLINE_ICONS = {
|
|
|
837
845
|
"wand-sparkles": pencil_ruler_default,
|
|
838
846
|
x: x_default
|
|
839
847
|
};
|
|
848
|
+
var KEYLINE_ICONS = Object.fromEntries(
|
|
849
|
+
Object.entries(RAW_KEYLINE_ICONS).map(([semantic, icon]) => [
|
|
850
|
+
semantic,
|
|
851
|
+
normalizeKeylineIcon(icon)
|
|
852
|
+
])
|
|
853
|
+
);
|
|
840
854
|
function renderKeylineIcons(root) {
|
|
841
855
|
root.querySelectorAll("[data-foundry-icon]").forEach((placeholder) => {
|
|
842
856
|
const semanticName = placeholder.dataset.foundryIcon;
|
|
@@ -847,18 +861,51 @@ function renderKeylineIcons(root) {
|
|
|
847
861
|
svg.setAttribute("viewBox", `0 0 ${icon.width ?? 24} ${icon.height ?? 24}`);
|
|
848
862
|
svg.setAttribute("aria-hidden", "true");
|
|
849
863
|
svg.setAttribute("focusable", "false");
|
|
864
|
+
svg.setAttribute("fill", "none");
|
|
865
|
+
svg.setAttribute("stroke-width", "1");
|
|
850
866
|
svg.setAttribute("data-keyline-icon", semanticName);
|
|
851
867
|
svg.innerHTML = icon.body;
|
|
852
868
|
placeholder.replaceWith(svg);
|
|
853
869
|
});
|
|
854
870
|
}
|
|
855
871
|
|
|
872
|
+
// src/ui-foundations.ts
|
|
873
|
+
var FOUNDRY_UI_FOUNDATION_CSS = `
|
|
874
|
+
:host {
|
|
875
|
+
--fdc-space-1:4px;
|
|
876
|
+
--fdc-space-2:8px;
|
|
877
|
+
--fdc-space-3:12px;
|
|
878
|
+
--fdc-space-4:16px;
|
|
879
|
+
--fdc-space-5:20px;
|
|
880
|
+
--fdc-space-6:24px;
|
|
881
|
+
--fdc-space-7:28px;
|
|
882
|
+
--fdc-space-8:32px;
|
|
883
|
+
--fdc-radius-xs:4px;
|
|
884
|
+
--fdc-radius-sm:8px;
|
|
885
|
+
--fdc-radius-md:12px;
|
|
886
|
+
--fdc-radius-lg:16px;
|
|
887
|
+
--fdc-control-sm:32px;
|
|
888
|
+
--fdc-control-md:36px;
|
|
889
|
+
--fdc-control-lg:40px;
|
|
890
|
+
--fdc-icon-sm:12px;
|
|
891
|
+
--fdc-icon-md:16px;
|
|
892
|
+
--fdc-icon-lg:20px;
|
|
893
|
+
--fdc-type-caption:12px;
|
|
894
|
+
--fdc-type-body:16px;
|
|
895
|
+
--fdc-type-heading:20px;
|
|
896
|
+
}
|
|
897
|
+
`;
|
|
898
|
+
|
|
856
899
|
// src/workspace.ts
|
|
900
|
+
function resolveInterfaceTheme(preference, systemPrefersDark) {
|
|
901
|
+
return preference === "system" ? systemPrefersDark ? "dark" : "light" : preference;
|
|
902
|
+
}
|
|
857
903
|
var DEFAULT_WORKSPACE_STATE = {
|
|
858
904
|
layersOpen: true,
|
|
859
905
|
inspectorOpen: true,
|
|
860
906
|
utility: null,
|
|
861
|
-
|
|
907
|
+
changeSummaryVisible: false,
|
|
908
|
+
reviewOpen: false,
|
|
862
909
|
workbenchOpen: false,
|
|
863
910
|
comparisonOpen: false
|
|
864
911
|
};
|
|
@@ -873,7 +920,14 @@ function updateWorkspace(state, action) {
|
|
|
873
920
|
return { ...state, utility: state.utility === action.utility ? null : action.utility };
|
|
874
921
|
}
|
|
875
922
|
if (action.type === "close-utility") return { ...state, utility: null };
|
|
876
|
-
if (action.type === "set-
|
|
923
|
+
if (action.type === "set-change-summary") {
|
|
924
|
+
return {
|
|
925
|
+
...state,
|
|
926
|
+
changeSummaryVisible: action.visible,
|
|
927
|
+
reviewOpen: action.visible ? state.reviewOpen : false
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
if (action.type === "set-review") return { ...state, reviewOpen: action.open };
|
|
877
931
|
if (action.type === "set-workbench") return { ...state, workbenchOpen: action.open };
|
|
878
932
|
return { ...state, comparisonOpen: action.open };
|
|
879
933
|
}
|
|
@@ -914,14 +968,19 @@ var CONTROL_SECTIONS = {
|
|
|
914
968
|
{
|
|
915
969
|
label: "Position and size",
|
|
916
970
|
properties: [
|
|
917
|
-
"
|
|
918
|
-
"
|
|
971
|
+
"left",
|
|
972
|
+
"top",
|
|
919
973
|
"width",
|
|
920
974
|
"height",
|
|
975
|
+
"widthMode",
|
|
976
|
+
"heightMode",
|
|
921
977
|
"minWidth",
|
|
922
978
|
"maxWidth",
|
|
923
979
|
"aspectRatio",
|
|
924
|
-
"overflow"
|
|
980
|
+
"overflow",
|
|
981
|
+
"rotate",
|
|
982
|
+
"scaleX",
|
|
983
|
+
"scaleY"
|
|
925
984
|
],
|
|
926
985
|
columns: 2,
|
|
927
986
|
prefixes: {
|
|
@@ -932,7 +991,12 @@ var CONTROL_SECTIONS = {
|
|
|
932
991
|
minWidth: "Min",
|
|
933
992
|
maxWidth: "Max",
|
|
934
993
|
aspectRatio: "Ratio",
|
|
935
|
-
overflow: "Clip"
|
|
994
|
+
overflow: "Clip",
|
|
995
|
+
left: "X",
|
|
996
|
+
top: "Y",
|
|
997
|
+
rotate: "\xB0",
|
|
998
|
+
scaleX: "FX",
|
|
999
|
+
scaleY: "FY"
|
|
936
1000
|
}
|
|
937
1001
|
},
|
|
938
1002
|
{
|
|
@@ -988,17 +1052,29 @@ var CONTROL_SECTIONS = {
|
|
|
988
1052
|
}
|
|
989
1053
|
],
|
|
990
1054
|
color: [
|
|
991
|
-
{ label: "Appearance", properties: ["
|
|
992
|
-
{ label: "
|
|
1055
|
+
{ label: "Appearance", properties: ["opacity"] },
|
|
1056
|
+
{ label: "Fill", properties: ["color", "backgroundColor", "backgroundImage"], stacked: true }
|
|
993
1057
|
],
|
|
994
1058
|
effects: [
|
|
995
1059
|
{
|
|
996
|
-
label: "Corners
|
|
997
|
-
properties: [
|
|
1060
|
+
label: "Corners",
|
|
1061
|
+
properties: [
|
|
1062
|
+
"borderRadius",
|
|
1063
|
+
"borderTopLeftRadius",
|
|
1064
|
+
"borderTopRightRadius",
|
|
1065
|
+
"borderBottomLeftRadius",
|
|
1066
|
+
"borderBottomRightRadius"
|
|
1067
|
+
],
|
|
998
1068
|
columns: 2,
|
|
999
|
-
prefixes: {
|
|
1069
|
+
prefixes: {
|
|
1070
|
+
borderRadius: "All",
|
|
1071
|
+
borderTopLeftRadius: "TL",
|
|
1072
|
+
borderTopRightRadius: "TR",
|
|
1073
|
+
borderBottomLeftRadius: "BL",
|
|
1074
|
+
borderBottomRightRadius: "BR"
|
|
1075
|
+
}
|
|
1000
1076
|
},
|
|
1001
|
-
{ label: "Stroke", properties: ["borderColor"] },
|
|
1077
|
+
{ label: "Stroke", properties: ["borderWidth", "borderColor"] },
|
|
1002
1078
|
{
|
|
1003
1079
|
label: "Effects",
|
|
1004
1080
|
properties: ["boxShadow", "filter"],
|
|
@@ -1021,70 +1097,71 @@ var CONTROL_SECTIONS = {
|
|
|
1021
1097
|
]
|
|
1022
1098
|
};
|
|
1023
1099
|
var PANEL_CSS = `
|
|
1100
|
+
${FOUNDRY_UI_FOUNDATION_CSS}
|
|
1024
1101
|
:host { all:initial; color-scheme:dark; --fdc-ink:#f0f1f3; --fdc-paper:#141416; --fdc-surface:#1c1c1f; --fdc-subtle:#242428; --fdc-elevated:#29292e; --fdc-line:#303035; --fdc-signal:#3b82f6; --fdc-signal-soft:#172d50; --fdc-component:#9b87f5; --fdc-component-soft:#28233d; --fdc-muted:#9a9aa2; --fdc-font:"Geist Sans",Geist,Arial,sans-serif; font-family:var(--fdc-font); color:var(--fdc-ink); }
|
|
1025
1102
|
*,*::before,*::after { box-sizing:border-box;font-family:inherit; }
|
|
1026
1103
|
button,select,input { font:inherit; }
|
|
1027
|
-
button:focus-visible,select:focus-visible,input:focus-visible { outline:
|
|
1028
|
-
.outline { position:fixed;z-index:2147483645;pointer-events:none;border:
|
|
1029
|
-
.measure { position:absolute;left:-
|
|
1030
|
-
.cross::before,.cross::after { content:"";position:absolute;background:var(--fdc-signal); }.cross::before { width:
|
|
1031
|
-
.panel { position:fixed;z-index:2147483646;top:12px;right:12px;width:
|
|
1104
|
+
button:focus-visible,select:focus-visible,input:focus-visible { outline:4px solid var(--fdc-signal);outline-offset:4px; }
|
|
1105
|
+
.outline { position:fixed;z-index:2147483645;pointer-events:none;border:1px solid var(--fdc-signal);box-shadow:0 0 0 1px rgb(255 255 255 / 90%),0 0 0 4px rgb(54 89 244 / 12%);transition:top 80ms linear,left 80ms linear,width 80ms linear,height 80ms linear; }
|
|
1106
|
+
.measure { position:absolute;left:-4px;top:-28px;height:24px;display:flex;align-items:center;padding:0 8px;color:white;background:var(--fdc-signal);border-radius:4px 4px 4px 0;font:650 12px/1 var(--fdc-font);letter-spacing:.02em;white-space:nowrap;box-shadow:0 4px 16px rgb(25 43 124 / 20%); }
|
|
1107
|
+
.cross::before,.cross::after { content:"";position:absolute;background:var(--fdc-signal); }.cross::before { width:12px;height:1px;left:-8px;top:-1px; }.cross::after { width:1px;height:12px;left:-1px;top:-8px; }
|
|
1108
|
+
.panel { position:fixed;z-index:2147483646;top:12px;right:12px;width:384px;min-width:340px;max-width:min(520px,calc(100vw - 24px));max-height:calc(100vh - 24px);overflow:hidden;display:flex;flex-direction:column;color:var(--fdc-ink);background:var(--fdc-surface);border:1px solid var(--fdc-line);box-shadow:0 1px 4px rgb(0 0 0 / 5%),0 12px 28px rgb(0 0 0 / 10%);pointer-events:auto;border-radius:12px; }
|
|
1032
1109
|
.top { flex:none;background:var(--fdc-surface);border-bottom:1px solid var(--fdc-line); }
|
|
1033
1110
|
.top-identity { position:relative;min-height:44px;display:flex;align-items:center;padding:0 12px; }
|
|
1034
1111
|
.brand { flex:1;display:flex;align-items:center;min-width:0; }
|
|
1035
|
-
.brand-copy { display:flex;align-items:baseline;gap:
|
|
1036
|
-
.session-status { flex:none;margin-left:8px;display:flex;align-items:center;gap:
|
|
1037
|
-
.top-identity>.close { flex:none;margin-left:4px; }.top-actions { min-height:40px;display:grid;grid-template-columns:repeat(6,minmax(0,1fr));align-items:center;padding:4px 8px;border-top:1px solid var(--fdc-line);background:var(--fdc-surface); }.top-actions .icon-button { justify-self:center; }.icon-button { width:32px;height:32px;display:grid;place-items:center;border:0;border-radius:
|
|
1038
|
-
.selection { position:relative;padding:
|
|
1039
|
-
.scope { display:grid;grid-template-columns:1fr 1fr;gap:8px;padding:12px
|
|
1040
|
-
.tool-shelf { position:fixed;z-index:2147483646;left:50%;bottom:
|
|
1041
|
-
.tool-select,.tab { position:relative;flex:none;width:36px;height:36px;display:grid;place-items:center;padding:0;border:0;border-radius:
|
|
1042
|
-
.controls { min-height:
|
|
1043
|
-
.motion-list { padding:0 12px 12px; }.motion-row { padding:12px 0;border-bottom:1px solid var(--fdc-line); }.motion-title { display:flex;justify-content:space-between;gap:12px;margin-bottom:
|
|
1044
|
-
.motion-timeline { width:100%;margin:
|
|
1045
|
-
.empty { padding:48px 28px;text-align:center;color:var(--fdc-muted);font-size:
|
|
1046
|
-
.change-dock { min-height:48px;display:grid;grid-template-columns:minmax(0,1fr)
|
|
1047
|
-
.review-view { min-height:0;flex:1;display:none;flex-direction:column;background:var(--fdc-surface); }.panel.reviewing .selection,.panel.reviewing .scope,.panel.reviewing .controls,.panel.reviewing>.footer,.panel.reviewing>.change-dock { display:none; }.panel.reviewing .review-view { display:flex; }.review-head { min-height:48px;display:flex;align-items:center;gap:8px;padding:0
|
|
1048
|
-
.toast { position:fixed;left:50%;bottom:76px;transform:translate(-50%,8px);padding:
|
|
1049
|
-
.resize-handle { position:absolute;width:
|
|
1050
|
-
.resize-handle[data-handle="n"]{top:-
|
|
1051
|
-
.panel-resizer { position:absolute;z-index:3;top:84px;bottom:0;left:-4px;width:8px;border:0;background:transparent;cursor:ew-resize;touch-action:none; }.panel-resizer::after { content:"";position:absolute;top:50%;left:
|
|
1052
|
-
.canvas-variant { position:fixed;z-index:2147483645;height:
|
|
1053
|
-
.onboarding-card { position:fixed;z-index:2147483647;left:50%;bottom:
|
|
1054
|
-
.review-details { margin-top:
|
|
1112
|
+
.brand-copy { display:flex;align-items:baseline;gap:4px; }.brand-copy b { font-size:12px;line-height:1;font-weight:550;letter-spacing:-.02em; }.brand-copy span { color:var(--fdc-muted);font:400 12px/1 var(--fdc-font); }
|
|
1113
|
+
.session-status { flex:none;margin-left:8px;display:flex;align-items:center;gap:4px;padding:4px 8px;border:0;color:#236c59;background:#eef8f4;border-radius:1000px;font:500 12px/1 var(--fdc-font);cursor:pointer; }.session-status i { width:4px;height:4px;background:#2ca67f;border-radius:50%; }.session-status.saving { color:#6b570f;background:#fff8d8; }.session-status.saving i { background:#d5a91d;animation:fdc-pulse 1s ease-in-out infinite; }.session-status.error,.session-status.offline { color:#8b4d3d;background:#faece7; }.session-status.error i,.session-status.offline i { background:#d16d51; }.session-status.saved { color:#236c59;background:#eef8f4; }.status-popover { position:absolute;z-index:2;top:40px;right:40px;width:240px;padding:12px;border:1px solid var(--fdc-line);border-radius:8px;background:white;box-shadow:0 12px 28px rgb(0 0 0 / 14%); }.status-popover[hidden] { display:none; }.status-popover strong { display:block;font-size:12px;font-weight:550; }.status-popover span,.status-popover code { display:block;margin-top:4px;overflow:hidden;text-overflow:ellipsis;color:var(--fdc-muted);font:400 8px/1.45 var(--fdc-font);white-space:nowrap; }.status-popover button { width:100%;height:28px;margin-top:8px;border:1px solid var(--fdc-line);border-radius:4px;background:white;color:var(--fdc-ink);font-size:8px;cursor:pointer; }
|
|
1114
|
+
.top-identity>.close { flex:none;margin-left:4px; }.top-actions { min-height:40px;display:grid;grid-template-columns:repeat(6,minmax(0,1fr));align-items:center;padding:4px 8px;border-top:1px solid var(--fdc-line);background:var(--fdc-surface); }.top-actions .icon-button { justify-self:center; }.icon-button { width:32px;height:32px;display:grid;place-items:center;border:0;border-radius:8px;background:transparent;color:var(--fdc-muted);cursor:pointer; }.icon-button:hover { background:var(--fdc-subtle);color:var(--fdc-ink); }.icon-button.active { color:#0761d1;background:#edf6ff; }.icon-button:disabled { opacity:.35;cursor:not-allowed; }.icon-button:disabled:hover { color:var(--fdc-muted);background:transparent; }
|
|
1115
|
+
.selection { position:relative;padding:16px;background:var(--fdc-surface);border-bottom:1px solid var(--fdc-line); }.selection::before { content:"";position:absolute;top:12px;left:0;width:4px;height:0;background:var(--fdc-signal);border-radius:0 4px 4px 0;transition:height .18s ease; }.panel.has-selection .selection::before { height:24px; }.selection-heading { display:flex;align-items:center;justify-content:space-between;margin-bottom:8px; }.selection-kind { max-width:220px;overflow:hidden;text-overflow:ellipsis;padding:4px 8px;color:#4d4d4d;background:var(--fdc-subtle);border-radius:4px;font:500 12px/1 var(--fdc-font);text-transform:uppercase;letter-spacing:.025em;white-space:nowrap; }.selection-state { color:var(--fdc-muted);font:450 12px/1 var(--fdc-font); }.panel.has-selection .selection-state { color:#0761d1; }.selection strong { display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:16px;line-height:1.3;font-weight:550;letter-spacing:-.025em; }.selection code { display:block;margin-top:4px;overflow:hidden;text-overflow:ellipsis;color:var(--fdc-muted);font:400 12px/1.45 var(--fdc-font);white-space:nowrap; }.selection-hint { display:block;margin-top:8px;color:#858585;font:400 12px/1.45 var(--fdc-font); }.selection-stats { display:flex;gap:4px;margin-top:12px; }.selection-stats[hidden] { display:none; }.selection-stats span { padding:4px 8px;color:#4d4d4d;background:var(--fdc-subtle);border-radius:4px;font:400 12px/1 var(--fdc-font); }.selection-stats span:first-child { color:#0761d1;background:#edf6ff; }.selection.selected-flash strong { animation:fdc-selection-title .2s ease-out; }
|
|
1116
|
+
.scope { display:grid;grid-template-columns:1fr 1fr;gap:8px;padding:12px 16px;background:var(--fdc-surface);border-bottom:1px solid var(--fdc-line); }.scope label { display:flex;flex-direction:column;gap:8px;color:var(--fdc-muted);font:500 12px/1.2 var(--fdc-font); }.scope select { width:100%;height:36px;padding:0 8px;border:1px solid var(--fdc-line);border-radius:8px;background:var(--fdc-surface);color:var(--fdc-ink);font:400 12px/1 var(--fdc-font);text-transform:none;letter-spacing:0;outline:none;cursor:pointer; }
|
|
1117
|
+
.tool-shelf { position:fixed;z-index:2147483646;left:50%;bottom:20px;transform:translateX(-50%);display:flex;align-items:center;gap:8px;max-width:calc(100vw - 32px);padding:8px;background:var(--fdc-surface);border:1px solid var(--fdc-line);border-radius:12px;box-shadow:0 4px 4px rgb(0 0 0 / 4%),0 8px 16px -4px rgb(0 0 0 / 14%);pointer-events:auto; }.mode-copy { min-width:96px;display:flex;flex-direction:column;gap:4px;padding:0 8px 0 1px; }.mode-copy strong { font-size:12px;font-weight:550;line-height:1.1; }.mode-copy span { color:var(--fdc-muted);font-size:8px;line-height:1.2;white-space:nowrap; }
|
|
1118
|
+
.tool-select,.tab { position:relative;flex:none;width:36px;height:36px;display:grid;place-items:center;padding:0;border:0;border-radius:8px;background:transparent;color:#4d4d4d;cursor:pointer; }.tool-select:hover,.tab:hover { color:var(--fdc-ink);background:var(--fdc-subtle); }.tool-select.active,.tab.active { color:white;background:var(--fdc-ink); }.tool-select svg,.icon-button svg { width:20px;height:20px;pointer-events:none; }.tab svg { width:16px;height:16px;pointer-events:none; }.tool-select::after,.tab::after { content:attr(data-tooltip);position:absolute;left:50%;bottom:calc(100% + 8px);transform:translate(-50%,4px);padding:8px 8px;border-radius:4px;background:var(--fdc-ink);color:white;font:400 12px/1 var(--fdc-font);white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .12s ease,transform .12s ease; }.tool-select:hover::after,.tool-select:focus-visible::after,.tab:not(:disabled):hover::after,.tab:not(:disabled):focus-visible::after { opacity:1;transform:translate(-50%,0); }.tool-divider { width:1px;height:24px;flex:none;background:var(--fdc-line); }.tabs { display:flex;gap:4px;overflow:visible;scrollbar-width:none; }.tab:disabled { color:#a1a1a1;cursor:default; }.tab:disabled:hover { background:transparent; }
|
|
1119
|
+
.controls { min-height:212px;overflow:auto;background:var(--fdc-surface); }.inspector-heading { position:sticky;top:0;z-index:1;height:44px;display:flex;align-items:center;gap:8px;padding:0 12px;background:rgb(255 255 255 / 96%);border-bottom:1px solid var(--fdc-line);backdrop-filter:blur(8px); }.inspector-heading svg { width:16px;height:16px;color:#4d4d4d; }.inspector-heading strong { font-size:12px;font-weight:550; }.property-count { margin-left:auto;color:var(--fdc-muted);font:400 12px/1 var(--fdc-font);letter-spacing:.01em; }.property-section { padding:0;border-bottom:1px solid var(--fdc-line); }.section-head { width:100%;min-height:40px;display:flex;align-items:center;padding:0 8px 0 4px;background:white;color:#3f3f3f; }.section-head:hover { background:#fcfcfc; }.section-toggle { min-width:0;min-height:40px;flex:1;display:flex;align-items:center;gap:8px;padding:0 8px;border:0;background:transparent;color:inherit;text-align:left;cursor:pointer; }.section-toggle>svg { width:12px;height:12px;color:#858585;transition:transform .12s ease; }.property-section.collapsed .section-toggle>svg { transform:rotate(-90deg); }.section-head strong { font-size:12px;font-weight:500; }.section-grid { display:grid;gap:8px;padding:0 12px 12px; }.property-section.collapsed .section-grid { display:none; }.section-grid.two { grid-template-columns:1fr 1fr; }.section-grid.stacked .property-control { grid-template-columns:1fr;gap:8px; }.property-control { display:grid;grid-template-columns:minmax(0,1fr) 132px 24px;align-items:center;gap:8px;min-height:36px; }.property-label { overflow:hidden;text-overflow:ellipsis;color:#4d4d4d;font-size:12px;font-weight:400;white-space:nowrap; }.control-field { position:relative;display:flex;align-items:center;min-width:0;height:32px;border:1px solid var(--fdc-line);border-radius:4px;background:var(--fdc-paper);overflow:hidden;transition:border-color .12s ease,box-shadow .12s ease,background .12s ease; }.control-field:hover { background:var(--fdc-surface); }.control-field:focus-within { border-color:var(--fdc-signal);background:var(--fdc-surface);box-shadow:0 0 0 4px rgb(0 112 243 / 10%); }.control-reset { width:24px;height:24px;display:grid;place-items:center;padding:0;border:0;border-radius:4px;background:transparent;color:#8a8a8a;cursor:pointer;opacity:0; }.property-control:hover .control-reset,.compact-control:hover .control-reset,.control-reset:focus-visible { opacity:1; }.control-reset:hover { color:var(--fdc-ink);background:var(--fdc-subtle); }.control-reset svg { width:12px;height:12px; }.compact-control { min-width:0;display:grid;grid-template-columns:minmax(0,1fr) 24px;gap:4px;align-items:center; }.compact-control .control-field { width:100%; }.field-prefix { min-width:28px;padding-left:8px;color:#7a7a7a;font:400 12px/1 var(--fdc-font); }.compact-control .field-prefix.wide { min-width:40px; }.property-control input,.property-control select,.compact-control input,.compact-control select { width:100%;min-width:0;height:32px;padding:0 8px;border:0;background:transparent;color:var(--fdc-ink);font:400 12px/1 var(--fdc-font);outline:none; }.property-control input[type="number"],.compact-control input[type="number"] { appearance:textfield; }.property-control input[type="number"]::-webkit-inner-spin-button,.property-control input[type="number"]::-webkit-outer-spin-button,.compact-control input[type="number"]::-webkit-inner-spin-button,.compact-control input[type="number"]::-webkit-outer-spin-button { margin:0;appearance:none; }.property-control select { font-family:var(--fdc-font); }.control-field .unit-select { width:40px;flex:none;padding:0 4px;color:#707070;font-size:8px;cursor:pointer; }[data-scrub-for] { cursor:ew-resize;user-select:none;touch-action:none; }.property-label[data-scrub-for]:hover,.field-prefix[data-scrub-for]:hover,[data-scrub-for].scrubbing { color:var(--fdc-signal); }.color-swatch { width:16px;height:16px;flex:none;margin-left:8px;border:1px solid rgb(0 0 0 / 10%);border-radius:4px;background:var(--swatch-color); }.color-swatch.transparent { background-color:white;background-image:linear-gradient(45deg,#d9d9d9 25%,transparent 25%),linear-gradient(-45deg,#d9d9d9 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#d9d9d9 75%),linear-gradient(-45deg,transparent 75%,#d9d9d9 75%);background-size:8px 8px;background-position:0 0,0 4px,4px -4px,-4px 0; }.color-value { overflow:hidden;text-overflow:ellipsis;margin-left:8px;color:#4d4d4d;font:400 12px/1 var(--fdc-font);white-space:nowrap; }.color-picker { position:absolute;inset:0;width:100%!important;height:100%!important;opacity:0;cursor:pointer; }.unit { padding-right:8px;color:#7a7a7a;font:400 12px/1 var(--fdc-font); }.sr-only { position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0; }
|
|
1120
|
+
.motion-list { padding:0 12px 12px; }.motion-row { padding:12px 0;border-bottom:1px solid var(--fdc-line); }.motion-title { display:flex;justify-content:space-between;gap:12px;margin-bottom:8px;font-size:12px;font-weight:550; }.motion-title code { color:var(--fdc-muted);font:400 12px/1.3 var(--fdc-font); }.motion-actions { display:grid;grid-template-columns:repeat(4,1fr);gap:4px; }.motion-actions button { min-height:32px;padding:0 4px;border:1px solid var(--fdc-line);background:var(--fdc-paper);border-radius:4px;color:var(--fdc-ink);font-size:12px;font-weight:400;cursor:pointer; }.motion-actions button:hover { border-color:#c7c7c7;background:var(--fdc-surface); }
|
|
1121
|
+
.motion-timeline { width:100%;margin:4px 0 12px;accent-color:var(--fdc-signal); }.motion-fields { display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:8px; }.motion-fields label { display:grid;grid-template-columns:auto 1fr;align-items:center;gap:4px;color:var(--fdc-muted);font-size:8px; }.motion-fields input { width:100%;height:28px;padding:0 8px;border:1px solid var(--fdc-line);border-radius:4px;background:var(--fdc-paper);color:var(--fdc-ink);font-size:12px; }
|
|
1122
|
+
.empty { padding:48px 28px;text-align:center;color:var(--fdc-muted);font-size:12px;line-height:1.6; }.empty::before { content:"\u2316";display:grid;place-items:center;width:40px;height:40px;margin:0 auto 12px;color:var(--fdc-signal);background:var(--fdc-signal-soft);border-radius:12px;font:20px/1 var(--fdc-font); }
|
|
1123
|
+
.change-dock { min-height:48px;display:grid;grid-template-columns:minmax(0,1fr) 32px 68px;align-items:center;gap:8px;padding:8px 8px;border-top:1px solid var(--fdc-line);background:white; }.change-dock[hidden] { display:none; }.change-dock-copy { min-width:0;padding-left:4px; }.change-dock-copy strong,.change-dock-copy span { display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap; }.change-dock-copy strong { font-size:12px;font-weight:550; }.change-dock-copy span { margin-top:4px;color:var(--fdc-muted);font-size:8px; }.change-dock button { height:32px;border:1px solid var(--fdc-line);border-radius:4px;background:white;color:var(--fdc-ink);font-size:8px;cursor:pointer; }.change-dock .dock-review { color:white;border-color:var(--fdc-ink);background:var(--fdc-ink); }.change-dock svg { width:12px;height:12px; }.footer { display:grid;grid-template-columns:84px 1fr;gap:8px;padding:8px;background:var(--fdc-surface);border-top:1px solid var(--fdc-line); }.footer:has(.review[hidden]) { grid-template-columns:1fr; }.footer button[hidden] { display:none; }.footer button { min-height:36px;border:1px solid var(--fdc-line);border-radius:8px;background:var(--fdc-surface);color:var(--fdc-ink);font-size:12px;font-weight:450;cursor:pointer; }.footer button:hover { border-color:#d0d0d0;background:var(--fdc-paper); }.footer .review { align-items:center;justify-content:center;gap:8px;color:white;background:var(--fdc-ink);border-color:var(--fdc-ink); }.footer .review:not([hidden]) { display:flex; }.footer .review:hover { background:#2f2f2f; }.change-count { min-width:20px;height:20px;display:inline-grid;place-items:center;padding:0 4px;color:var(--fdc-ink);background:white;border-radius:1000px;font:500 8px/1 var(--fdc-font); }.change-count[hidden] { display:none; }
|
|
1124
|
+
.review-view { min-height:0;flex:1;display:none;flex-direction:column;background:var(--fdc-surface); }.panel.reviewing .selection,.panel.reviewing .scope,.panel.reviewing .controls,.panel.reviewing>.footer,.panel.reviewing>.change-dock { display:none; }.panel.reviewing .review-view { display:flex; }.review-head { min-height:48px;display:flex;align-items:center;gap:8px;padding:0 12px;border-bottom:1px solid var(--fdc-line); }.review-head button { width:32px;height:32px;display:grid;place-items:center;padding:0;border:0;border-radius:4px;background:transparent;color:var(--fdc-muted);cursor:pointer; }.review-head button:hover { background:var(--fdc-subtle);color:var(--fdc-ink); }.review-head svg { width:16px;height:16px; }.review-head strong { font-size:12px;font-weight:550; }.review-head span { margin-left:auto;color:var(--fdc-muted);font-size:12px; }.review-body { min-height:160px;overflow-x:hidden;overflow-y:auto; }.review-toolbar { position:sticky;top:0;z-index:1;display:flex;gap:4px;padding:8px 8px;border-bottom:1px solid var(--fdc-line);background:rgb(255 255 255 / 96%);backdrop-filter:blur(8px); }.review-toolbar button { height:28px;padding:0 8px;border:1px solid var(--fdc-line);border-radius:4px;background:white;color:#555;font-size:8px;cursor:pointer; }.review-toolbar button:last-child { margin-left:auto; }.review-empty { padding:44px 24px;color:var(--fdc-muted);font-size:12px;line-height:1.55;text-align:center; }.review-group { border-bottom:1px solid var(--fdc-line); }.review-group-title { width:100%;min-height:36px;display:flex;align-items:center;gap:8px;padding:0 12px;border:0;background:white;color:var(--fdc-muted);font-size:12px;font-weight:500;text-align:left;cursor:pointer; }.review-group-title span { margin-left:auto;font-size:8px; }.review-group-title svg { width:12px;height:12px;transition:transform .12s ease; }.review-group.collapsed .review-group-title svg { transform:rotate(-90deg); }.review-group.collapsed .review-card { display:none; }.review-card { display:grid;grid-template-columns:20px minmax(0,1fr);gap:8px;padding:8px 12px 12px; }.review-card.rejected { opacity:.62; }.review-card+.review-card { border-top:1px solid var(--fdc-line); }.review-card input[type="checkbox"] { width:16px;height:16px;margin:4px 0 0;accent-color:var(--fdc-ink); }.review-card-main { min-width:0; }.review-card-line { display:flex;align-items:center;gap:8px; }.review-card-line strong { min-width:0;overflow:hidden;text-overflow:ellipsis;font-size:12px;font-weight:500;white-space:nowrap; }.review-card-tools { display:flex;gap:4px;margin-left:auto; }.review-card-tools button { height:24px;padding:0 8px;border:1px solid var(--fdc-line);border-radius:4px;background:white;color:#666;font-size:8px;cursor:pointer; }.confidence-pill { flex:none;padding:4px 4px;border-radius:4px;background:#edf6ff;color:#0761d1;font-size:8px;text-transform:capitalize; }.confidence-pill.unresolved { color:#984a2b;background:#fff0e8; }.review-values { display:grid;grid-template-columns:minmax(0,1fr) 12px minmax(0,1fr);align-items:center;gap:4px;margin-top:8px; }.review-before { overflow:hidden;text-overflow:ellipsis;padding:8px;color:var(--fdc-muted);background:var(--fdc-subtle);border-radius:4px;font-size:12px;white-space:nowrap; }.review-values>span { color:var(--fdc-muted);font-size:12px;text-align:center; }.review-after { width:100%;height:28px;min-width:0;padding:0 8px;border:1px solid var(--fdc-line);border-radius:4px;background:white;font-size:12px;outline:none; }.review-after:focus { border-color:var(--fdc-signal);box-shadow:0 0 0 4px rgb(0 112 243 / 10%); }.review-source { margin-top:8px;overflow-wrap:anywhere;color:var(--fdc-muted);font-size:8px;line-height:1.45; }.review-actions { display:grid;grid-template-columns:84px 1fr;gap:8px;padding:8px;border-top:1px solid var(--fdc-line); }.review-actions button { min-height:36px;border:1px solid var(--fdc-line);border-radius:8px;background:white;font-size:12px;cursor:pointer; }.review-actions .apply { color:white;background:var(--fdc-ink);border-color:var(--fdc-ink); }.review-actions button:disabled { opacity:.45;cursor:not-allowed; }.run-summary { padding:16px 12px;border-bottom:1px solid var(--fdc-line); }.run-state { display:flex;align-items:center;gap:8px; }.run-state i { width:8px;height:8px;border-radius:50%;background:#a3a3a3; }.run-state i.active { background:var(--fdc-signal);box-shadow:0 0 0 4px rgb(0 112 243 / 10%); }.run-state i.passed { background:#2ca67f; }.run-state i.attention { background:#d16d51; }.run-state strong { font-size:12px;font-weight:550;text-transform:capitalize; }.run-summary p { margin:8px 0 0;color:var(--fdc-muted);font-size:12px;line-height:1.5; }.run-steps { padding:4px 12px 12px; }.run-step { display:grid;grid-template-columns:20px minmax(0,1fr);gap:8px;padding:8px 0;border-bottom:1px solid var(--fdc-line); }.run-step:last-child { border-bottom:0; }.run-step span:first-child { color:var(--fdc-muted);font-size:12px; }.run-step strong { display:block;font-size:12px;font-weight:500;text-transform:capitalize; }.run-step p { margin:4px 0 0;color:var(--fdc-muted);font-size:8px;line-height:1.45; }.result-list { padding:0 12px 12px; }.result-row { display:flex;align-items:flex-start;justify-content:space-between;gap:8px;padding:8px 0;border-top:1px solid var(--fdc-line);font-size:12px; }.result-row span:last-child { color:var(--fdc-muted);text-align:right; }.result-row.pass span:first-child { color:#23715c; }.result-row.fail span:first-child { color:#a24d30; }.run-files { padding:0 12px 12px;color:var(--fdc-muted);font-size:8px;line-height:1.5; }.run-files code { display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap; }
|
|
1125
|
+
.toast { position:fixed;left:50%;bottom:76px;transform:translate(-50%,8px);padding:8px 12px;background:var(--fdc-ink);color:white;border-radius:8px;font-size:12px;font-weight:450;opacity:0;transition:.15s ease;pointer-events:none;white-space:nowrap;box-shadow:0 8px 20px rgb(0 0 0 / 16%); }.toast.show { opacity:1;transform:translate(-50%,0); }
|
|
1126
|
+
.resize-handle { position:absolute;width:8px;height:8px;min-width:8px;min-height:8px;aspect-ratio:1;padding:0;border:1px solid #f0f1f3;border-radius:1px;background:var(--fdc-signal);box-shadow:0 1px 4px rgb(0 0 0 / 28%);appearance:none;pointer-events:auto;touch-action:none; }
|
|
1127
|
+
.resize-handle[data-handle="n"]{top:-4px;left:calc(50% - 4px);cursor:ns-resize}.resize-handle[data-handle="s"]{bottom:-4px;left:calc(50% - 4px);cursor:ns-resize}.resize-handle[data-handle="e"]{right:-4px;top:calc(50% - 4px);cursor:ew-resize}.resize-handle[data-handle="w"]{left:-4px;top:calc(50% - 4px);cursor:ew-resize}.resize-handle[data-handle="ne"]{right:-4px;top:-4px;cursor:nesw-resize}.resize-handle[data-handle="nw"]{left:-4px;top:-4px;cursor:nwse-resize}.resize-handle[data-handle="se"]{right:-4px;bottom:-4px;cursor:nwse-resize}.resize-handle[data-handle="sw"]{left:-4px;bottom:-4px;cursor:nesw-resize}
|
|
1128
|
+
.panel-resizer { position:absolute;z-index:3;top:84px;bottom:0;left:-4px;width:8px;border:0;background:transparent;cursor:ew-resize;touch-action:none; }.panel-resizer::after { content:"";position:absolute;top:50%;left:4px;width:4px;height:28px;transform:translateY(-50%);border-radius:4px;background:#4a4a50;opacity:0;transition:opacity .12s ease; }.panel:hover .panel-resizer::after,.panel-resizer:focus-visible::after { opacity:1; }.radius-handle { position:absolute;top:8px;left:8px;width:12px;height:12px;min-width:12px;min-height:12px;aspect-ratio:1;padding:0;border:4px solid #f0f1f3;border-radius:50%;background:#ff4d8d;box-shadow:0 1px 4px rgb(0 0 0 / 28%);appearance:none;pointer-events:auto;cursor:nwse-resize;touch-action:none; }
|
|
1129
|
+
.canvas-variant { position:fixed;z-index:2147483645;height:28px;max-width:180px;padding:0 24px 0 8px;border:1px solid #b8d5ff;border-radius:8px;background:white;color:#075fc5;font-size:8px;box-shadow:0 4px 16px rgb(0 0 0 / 10%);pointer-events:auto; }.canvas-variant[hidden] { display:none; }
|
|
1130
|
+
.onboarding-card { position:fixed;z-index:2147483647;left:50%;bottom:80px;width:288px;transform:translateX(-50%);padding:12px;border:1px solid var(--fdc-line);border-radius:12px;background:white;box-shadow:0 12px 32px rgb(0 0 0 / 14%);pointer-events:auto; }.onboarding-card[hidden] { display:none; }.onboarding-card-head { display:flex;align-items:center;gap:8px; }.onboarding-card-head svg { width:16px;height:16px;color:var(--fdc-signal); }.onboarding-card-head strong { font-size:12px;font-weight:550; }.onboarding-card p { margin:8px 0 12px;color:var(--fdc-muted);font-size:12px;line-height:1.5; }.onboarding-steps { display:grid;grid-template-columns:repeat(3,1fr);gap:4px; }.onboarding-steps span { padding:8px 4px;border:1px solid var(--fdc-line);border-radius:8px;color:#555;font-size:8px;line-height:1.35;text-align:center; }.onboarding-actions { display:flex;justify-content:flex-end;gap:4px;margin-top:12px; }.onboarding-actions button { height:28px;padding:0 8px;border:1px solid var(--fdc-line);border-radius:4px;background:white;font-size:8px;cursor:pointer; }.onboarding-actions .onboarding-start { color:white;border-color:var(--fdc-ink);background:var(--fdc-ink); }
|
|
1131
|
+
.review-details { margin-top:8px;color:var(--fdc-muted);font-size:8px; }.review-details summary { cursor:pointer;user-select:none; }.review-details[open] summary { margin-bottom:4px; }.review-details .review-source { display:block; }.review-details .impact-list { display:grid;gap:4px;margin-top:4px; }
|
|
1055
1132
|
@keyframes fdc-pulse { 50% { opacity:.35; } }
|
|
1056
1133
|
@media (prefers-reduced-motion:reduce){.session-status i,.section-head>svg,.toast,.tool-select::after,.tab::after{animation:none!important;transition:none!important}}
|
|
1057
1134
|
.multi-outline { position:fixed;z-index:2147483644;border:1px dashed var(--fdc-signal);background:rgb(0 112 243 / 4%);pointer-events:none; }
|
|
1058
1135
|
.hover-outline { position:fixed;z-index:2147483643;border:1px solid rgb(0 112 243 / 62%);background:rgb(0 112 243 / 4%);pointer-events:none; }
|
|
1059
|
-
.hover-outline::after { content:attr(data-label);position:absolute;left:-1px;top:-1px;transform:translateY(-100%);max-width:180px;padding:4px
|
|
1136
|
+
.hover-outline::after { content:attr(data-label);position:absolute;left:-1px;top:-1px;transform:translateY(-100%);max-width:180px;padding:4px 8px;border-radius:4px 4px 4px 0;background:#0070f3;color:white;font:500 12px/1.2 var(--fdc-font);overflow:hidden;text-overflow:ellipsis;white-space:nowrap; }
|
|
1060
1137
|
.snap-guide { position:fixed;z-index:2147483644;pointer-events:none;background:#ff4d8d;box-shadow:0 0 0 1px rgb(255 255 255 / 80%); }.snap-guide.vertical { width:1px;top:0;bottom:0; }.snap-guide.horizontal { height:1px;left:0;right:0; }
|
|
1061
|
-
.spacing-guide { position:fixed;z-index:2147483644;pointer-events:auto;background:#ff4d8d; }.spacing-guide.horizontal { height:1px;cursor:ew-resize; }.spacing-guide.vertical { width:1px;cursor:ns-resize; }.spacing-guide span { position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);padding:
|
|
1062
|
-
.selection-path { display:flex;align-items:center;gap:4px;margin-top:
|
|
1138
|
+
.spacing-guide { position:fixed;z-index:2147483644;pointer-events:auto;background:#ff4d8d; }.spacing-guide.horizontal { height:1px;cursor:ew-resize; }.spacing-guide.vertical { width:1px;cursor:ns-resize; }.spacing-guide span { position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);padding:4px 4px;border-radius:4px;background:#ff4d8d;color:white;font:600 8px/1 var(--fdc-font);white-space:nowrap; }
|
|
1139
|
+
.selection-path { display:flex;align-items:center;gap:4px;margin-top:8px;min-width:0; }.selection-path button { min-width:0;height:24px;display:flex;align-items:center;gap:4px;padding:0 8px;border:1px solid var(--fdc-line);border-radius:4px;background:var(--fdc-paper);color:#555;font-size:8px;cursor:pointer; }.selection-path button:hover { color:var(--fdc-ink);border-color:#d2d2d2;background:white; }.selection-path button:disabled { opacity:.4;cursor:default; }.selection-path svg { width:12px;height:12px; }.selection-path .path-name { flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap; }
|
|
1063
1140
|
.selection-path[hidden] { display:none; }
|
|
1064
|
-
.layers-panel { position:fixed;z-index:2147483646;top:12px;left:12px;width:252px;max-height:calc(100vh - 24px);display:flex;flex-direction:column;overflow:hidden;border:1px solid var(--fdc-line);border-radius:
|
|
1065
|
-
.layers-switch { display:grid;grid-template-columns:1fr 1fr;gap:
|
|
1066
|
-
.layer-row { grid-template-columns:16px minmax(0,1fr);gap:1px;padding-right:4px;cursor:default; }.layer-row.dragging { opacity:.45; }.layer-row.drop-target { box-shadow:inset 0
|
|
1067
|
-
.health-panel { position:fixed;z-index:2147483646;top:12px;left:12px;width:304px;max-height:calc(100vh - 24px);display:flex;flex-direction:column;overflow:hidden;border:1px solid var(--fdc-line);border-radius:
|
|
1141
|
+
.layers-panel { position:fixed;z-index:2147483646;top:12px;left:12px;width:252px;max-height:calc(100vh - 24px);display:flex;flex-direction:column;overflow:hidden;border:1px solid var(--fdc-line);border-radius:12px;background:var(--fdc-surface);box-shadow:0 1px 4px rgb(0 0 0 / 5%),0 12px 28px rgb(0 0 0 / 10%);pointer-events:auto; }.layers-panel[hidden] { display:none; }.layers-head { min-height:44px;display:flex;flex:none;align-items:center;gap:8px;padding:0 8px 0 12px;border-bottom:1px solid var(--fdc-line); }.layers-head svg { width:16px;height:16px; }.layers-head strong { font-size:12px;font-weight:550; }.layers-head span { color:var(--fdc-muted);font-size:8px; }.layers-head .icon-button { margin-left:auto; }.layers-search { flex:none;padding:8px;border-bottom:1px solid var(--fdc-line);background:var(--fdc-paper); }.layers-search input { width:100%;height:32px;padding:0 8px;border:1px solid var(--fdc-line);border-radius:4px;background:white;color:var(--fdc-ink);font-size:12px;outline:none; }.layers-search input:focus { border-color:var(--fdc-signal);box-shadow:0 0 0 4px rgb(0 112 243 / 10%); }.layer-tree { min-height:120px;overflow:auto;padding:4px; }.layer-row { width:100%;height:32px;display:grid;grid-template-columns:16px 16px minmax(0,1fr) auto;align-items:center;gap:4px;padding:0 8px 0 calc(8px + var(--layer-depth) * 12px);border:0;border-radius:4px;background:transparent;color:#474747;text-align:left;cursor:pointer; }.layer-row:hover { background:var(--fdc-subtle); }.layer-row.selected { color:#075fc5;background:#eaf3ff; }.layer-row .chevron,.layer-row .layer-icon { width:12px;height:12px;color:#8a8a8a; }.layer-row .layer-label { overflow:hidden;text-overflow:ellipsis;font-size:12px;white-space:nowrap; }.layer-row .layer-meta { padding:4px 4px;border-radius:4px;background:#f0f0f0;color:#747474;font-size:8px;text-transform:uppercase; }.layer-row.selected .layer-meta { color:#075fc5;background:#d8e9ff; }.layers-empty { padding:36px 20px;color:var(--fdc-muted);font-size:12px;line-height:1.5;text-align:center; }
|
|
1142
|
+
.layers-switch { display:grid;grid-template-columns:1fr 1fr;gap:4px;padding:4px;border-bottom:1px solid var(--fdc-line);background:var(--fdc-paper); }.layers-switch button { min-width:0;height:28px;display:flex;align-items:center;justify-content:center;gap:8px;padding:0 8px;border:0;border-radius:4px;background:transparent;color:var(--fdc-muted);font-size:12px;cursor:pointer; }.layers-switch button:hover { color:var(--fdc-ink);background:white; }.layers-switch button.active { color:var(--fdc-ink);background:white;box-shadow:0 0 0 1px var(--fdc-line),0 1px 4px rgb(0 0 0 / 5%); }.layers-switch button span { min-width:16px;height:16px;display:grid;place-items:center;padding:0 4px;border-radius:1000px;background:var(--fdc-subtle);color:#747474;font-size:8px; }.layers-switch button[data-layer-view="components"].active { color:#5f46bf; }.layers-switch button[data-layer-view="components"].active span { color:#5f46bf;background:var(--fdc-component-soft); }.component-list { display:grid;gap:4px;padding:4px; }.component-card { overflow:hidden;border:1px solid var(--fdc-line);border-radius:8px;background:white; }.component-card:hover { border-color:#d7d0f3;box-shadow:0 4px 8px rgb(52 38 110 / 6%); }.component-card.selected { border-color:#c8bdf1;background:#fbfaff;box-shadow:0 0 0 4px rgb(114 87 217 / 8%); }.component-main { width:100%;min-height:52px;display:grid;grid-template-columns:32px minmax(0,1fr) auto;align-items:center;gap:8px;padding:8px 8px;border:0;background:transparent;color:var(--fdc-ink);text-align:left;cursor:pointer; }.component-main:disabled { cursor:default; }.component-mark { width:28px;height:28px;display:grid;place-items:center;border-radius:8px;color:var(--fdc-component);background:var(--fdc-component-soft); }.component-mark svg { width:16px;height:16px; }.component-copy { min-width:0; }.component-copy strong,.component-copy span { display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap; }.component-copy strong { font-size:12px;font-weight:550; }.component-copy span { margin-top:4px;color:var(--fdc-muted);font-size:8px; }.component-status { display:flex;flex-direction:column;align-items:flex-end;gap:4px; }.component-status span { padding:4px 4px;border-radius:1000px;color:#5f46bf;background:var(--fdc-component-soft);font-size:8px;white-space:nowrap; }.component-status small { color:#8b8b8b;font-size:8px;white-space:nowrap; }.component-variants { display:flex;gap:4px;overflow:hidden;padding:0 8px 8px 44px; }.component-variants span { max-width:88px;overflow:hidden;text-overflow:ellipsis;padding:4px 4px;border:1px solid #e6e1f7;border-radius:4px;color:#66598c;background:#faf9ff;font-size:8px;white-space:nowrap; }
|
|
1143
|
+
.layer-tree { position:relative;scrollbar-gutter:stable; }.layer-selection-glide { position:absolute;z-index:0;top:0;right:4px;left:4px;height:32px;border:1px solid rgb(72 132 220 / 22%);border-radius:8px;background:var(--fdc-signal-soft);transform:translateY(var(--layer-glide-y));transition:transform .24s cubic-bezier(.16,1,.3,1),height .18s cubic-bezier(.16,1,.3,1);pointer-events:none; }.layer-row { position:relative;z-index:1;grid-template-columns:16px minmax(0,1fr);gap:1px;padding-right:4px;cursor:default; }.layer-row.entering { animation:layer-row-enter .22s cubic-bezier(.16,1,.3,1) both;animation-delay:min(calc(var(--layer-position) * 18ms),90ms); }.layer-row.selected { background:transparent; }.layer-row.dragging { opacity:.45; }.layer-row.drop-target { box-shadow:inset 0 4px 0 var(--fdc-signal); }.layer-branch { position:absolute;z-index:-1;top:-1px;bottom:-1px;left:var(--layer-branch-left);width:1px;background:var(--fdc-line);transform-origin:top;animation:layer-branch-draw .28s cubic-bezier(.16,1,.3,1) both;pointer-events:none; }.layer-spacer { width:1px;pointer-events:none; }.layer-toggle { width:16px;height:28px;display:grid;place-items:center;padding:0;border:0;background:transparent;color:#858585;cursor:pointer;transition:color .16s ease; }.layer-toggle:hover { color:var(--fdc-ink); }.layer-toggle:disabled { visibility:hidden; }.layer-toggle svg { width:12px;height:12px;transition:transform .24s cubic-bezier(.16,1,.3,1); }.layer-select { min-width:0;height:28px;display:grid;grid-template-columns:16px minmax(0,1fr) auto;align-items:center;gap:4px;padding:0 4px;border:0;border-radius:4px;background:transparent;color:inherit;text-align:left;cursor:pointer;outline:none; }.layer-select:focus-visible { box-shadow:inset 0 0 0 1px var(--fdc-signal); }.layer-select:active .layer-icon { transform:scale(.88); }.layer-icon { transition:color .16s ease,transform .16s cubic-bezier(.16,1,.3,1); }@keyframes layer-row-enter { from { opacity:0;transform:translateY(-4px); } to { opacity:1;transform:translateY(0); } }@keyframes layer-branch-draw { from { opacity:0;transform:scaleY(0); } to { opacity:1;transform:scaleY(1); } }
|
|
1144
|
+
.health-panel { position:fixed;z-index:2147483646;top:12px;left:12px;width:304px;max-height:calc(100vh - 24px);display:flex;flex-direction:column;overflow:hidden;border:1px solid var(--fdc-line);border-radius:12px;background:var(--fdc-surface);box-shadow:0 1px 4px rgb(0 0 0 / 5%),0 12px 28px rgb(0 0 0 / 10%);pointer-events:auto; }.health-panel[hidden] { display:none; }.health-head { min-height:48px;display:flex;align-items:center;gap:8px;padding:0 8px 0 12px;border-bottom:1px solid var(--fdc-line); }.health-head>svg { width:16px;height:16px; }.health-head strong { font-size:12px;font-weight:550; }.health-head .icon-button { margin-left:auto; }.health-summary { display:grid;grid-template-columns:56px minmax(0,1fr);gap:12px;padding:12px;border-bottom:1px solid var(--fdc-line); }.health-score { width:56px;height:56px;display:grid;place-items:center;border-radius:50%;background:conic-gradient(var(--score-color) calc(var(--score) * 1%),#ececec 0); }.health-score::before { content:"";grid-area:1/1;width:44px;height:44px;border-radius:50%;background:white; }.health-score strong { z-index:1;grid-area:1/1;font-size:16px;font-weight:600; }.health-summary-copy { min-width:0;align-self:center; }.health-summary-copy strong { display:block;font-size:12px;font-weight:550; }.health-summary-copy span { display:block;margin-top:4px;color:var(--fdc-muted);font-size:8px;line-height:1.4; }.health-filters { display:flex;gap:4px;padding:8px 8px;border-bottom:1px solid var(--fdc-line);background:var(--fdc-paper); }.health-filters button { min-height:28px;padding:0 8px;border:1px solid transparent;border-radius:4px;background:transparent;color:#666;font-size:8px;cursor:pointer; }.health-filters button:hover,.health-filters button.active { border-color:var(--fdc-line);background:white;color:var(--fdc-ink); }.health-list { min-height:120px;overflow:auto;padding:8px; }.health-card { padding:12px;border:1px solid var(--fdc-line);border-radius:8px;background:white; }.health-card+.health-card { margin-top:8px; }.health-card-top { display:flex;align-items:center;gap:8px; }.health-severity { width:8px;height:8px;flex:none;border-radius:50%;background:#a3a3a3; }.health-severity.high { background:#d15d43; }.health-severity.medium { background:#d69b3c; }.health-severity.low { background:#4b84cb; }.health-card-top strong { min-width:0;overflow:hidden;text-overflow:ellipsis;font-size:12px;font-weight:550;white-space:nowrap; }.health-card-top span:last-child { margin-left:auto;color:var(--fdc-muted);font-size:8px;text-transform:capitalize; }.health-card p { margin:8px 0 0;color:#5d5d5d;font-size:8px;line-height:1.45; }.health-evidence { margin-top:8px;padding:8px;border-radius:4px;background:var(--fdc-paper);color:#777;font-size:8px;line-height:1.4; }.health-actions { display:flex;gap:4px;margin-top:8px; }.health-actions button { min-height:28px;padding:0 8px;border:1px solid var(--fdc-line);border-radius:4px;background:white;color:#555;font-size:8px;cursor:pointer; }.health-actions button:hover { border-color:#c8c8c8;color:var(--fdc-ink); }.health-actions .health-fix { margin-left:auto;color:white;border-color:var(--fdc-ink);background:var(--fdc-ink); }.health-actions .health-fix.previewed { color:#23715c;border-color:#bcded2;background:#edf8f4;cursor:default; }.health-actions .health-fix:disabled { opacity:1; }.health-actions .health-ignore { padding:0 8px;color:#888;border-color:transparent; }.health-footer { display:flex;align-items:center;gap:4px;padding:8px;border-top:1px solid var(--fdc-line); }.health-footer button { min-height:32px;padding:0 8px;border:1px solid var(--fdc-line);border-radius:4px;background:white;color:#555;font-size:8px;cursor:pointer; }.health-footer .health-rescan { flex:1;color:white;border-color:var(--fdc-ink);background:var(--fdc-ink); }.health-empty { padding:36px 20px;text-align:center;color:var(--fdc-muted);font-size:12px;line-height:1.5; }.health-empty svg { display:block;width:24px;height:24px;margin:0 auto 12px;color:#2b9a76; }
|
|
1068
1145
|
.tool-select:disabled { color:#b6b6b6;cursor:default; }.tool-select:disabled:hover { background:transparent; }
|
|
1069
|
-
.token-row { grid-column:2;display:flex;flex-wrap:wrap;gap:
|
|
1070
|
-
.section-head .section-action { width:24px;height:24px;display:grid;place-items:center;margin-left:auto;padding:0;border:0;border-radius:4px;background:transparent;color:var(--fdc-muted);cursor:pointer; }.section-head .section-action:hover,.section-head .section-action.active { color:var(--fdc-signal);background:#edf6ff; }.section-head .section-action svg { width:
|
|
1071
|
-
.context-tools { display:flex;flex-wrap:wrap;gap:
|
|
1072
|
-
.native-panel { padding:
|
|
1073
|
-
.variant-list { display:grid;gap:
|
|
1074
|
-
.compare-bar { position:fixed;z-index:2147483646;left:50%;bottom:
|
|
1075
|
-
.command-palette { position:fixed;z-index:2147483647;left:50%;top:18%;transform:translateX(-50%);width:min(480px,calc(100vw - 32px));overflow:hidden;border:1px solid #d8d8d8;border-radius:
|
|
1076
|
-
.comparison-stage { position:fixed;z-index:2147483647;inset:
|
|
1077
|
-
.impact-list { display:grid;gap:4px;margin-top:
|
|
1078
|
-
.mapping-chooser { grid-column:2;margin-top:
|
|
1079
|
-
.workbench { position:fixed;z-index:2147483647;inset:
|
|
1080
|
-
.library-panel { position:fixed;z-index:2147483647;top:12px;right:352px;width:300px;max-height:calc(100vh - 24px);display:flex;flex-direction:column;overflow:hidden;border:1px solid var(--fdc-line);border-radius:
|
|
1081
|
-
.review-visual { display:grid;grid-template-columns:1fr 1fr;gap:
|
|
1082
|
-
.workbench-matrix { display:grid;grid-template-columns:92px repeat(var(--matrix-columns),minmax(
|
|
1083
|
-
.component-actions { display:flex;gap:4px;padding:0 8px 8px
|
|
1084
|
-
.token-provenance { grid-column:2;display:flex;align-items:center;gap:
|
|
1146
|
+
.token-row { grid-column:2;display:flex;flex-wrap:wrap;gap:4px;margin-top:8px; }.token-chip { height:24px;padding:0 8px;border:1px solid var(--fdc-line);border-radius:1000px;background:var(--fdc-paper);color:#4d4d4d;font-size:8px;cursor:pointer; }.token-chip:hover { border-color:#b7d5ff;color:#0761d1;background:#edf6ff; }
|
|
1147
|
+
.section-head .section-action { width:24px;height:24px;display:grid;place-items:center;margin-left:auto;padding:0;border:0;border-radius:4px;background:transparent;color:var(--fdc-muted);cursor:pointer; }.section-head .section-action:hover,.section-head .section-action.active { color:var(--fdc-signal);background:#edf6ff; }.section-head .section-action svg { width:12px;height:12px; }
|
|
1148
|
+
.context-tools { display:flex;flex-wrap:wrap;gap:4px;padding:8px 12px;border-bottom:1px solid var(--fdc-line);background:var(--fdc-surface); }.context-tools button { min-height:28px;display:flex;align-items:center;gap:4px;padding:0 8px;border:1px solid var(--fdc-line);border-radius:4px;background:white;color:#4d4d4d;font-size:8px;cursor:pointer; }.context-tools button:hover { border-color:#c8c8c8;color:var(--fdc-ink); }.context-tools button.active { color:#075fc5;border-color:#bad7ff;background:#edf6ff; }.context-tools svg { width:12px;height:12px; }
|
|
1149
|
+
.native-panel { padding:12px 12px;border-bottom:1px solid var(--fdc-line); }.native-panel-head { display:flex;align-items:center;margin-bottom:8px; }.native-panel-head strong { font-size:12px;font-weight:550; }.native-panel-head span { margin-left:auto;color:var(--fdc-muted);font-size:8px; }.native-search { width:100%;height:28px;padding:0 8px;border:1px solid var(--fdc-line);border-radius:4px;background:var(--fdc-paper);font-size:8px;outline:none; }.native-search:focus { border-color:var(--fdc-signal); }.native-grid { display:flex;flex-wrap:wrap;gap:4px;margin-top:8px; }.native-chip { min-height:28px;display:flex;align-items:center;gap:4px;padding:0 8px;border:1px solid var(--fdc-line);border-radius:4px;background:white;color:#4d4d4d;font-size:8px;cursor:pointer; }.native-chip:hover { border-color:#bdd7fb;background:#f5f9ff; }.native-chip .swatch { width:12px;height:12px;border:1px solid rgb(0 0 0 / 10%);border-radius:4px;background:var(--token-color); }.design-health { display:flex;align-items:center;gap:8px;margin-top:8px;padding:8px 8px;border-radius:4px;background:#f3f3f3;color:#575757;font-size:8px; }.design-health.pass { color:#23715c;background:#edf8f4; }.design-health.fail { color:#9a4930;background:#fff0e9; }.design-health svg { width:12px;height:12px; }
|
|
1150
|
+
.variant-list { display:grid;gap:4px;margin-top:8px; }.variant-button { min-height:32px;display:flex;align-items:center;justify-content:space-between;gap:8px;padding:0 8px;border:1px solid var(--fdc-line);border-radius:4px;background:white;font-size:8px;cursor:pointer; }.variant-button code { overflow:hidden;text-overflow:ellipsis;color:var(--fdc-muted);font-size:8px;white-space:nowrap; }
|
|
1151
|
+
.compare-bar { position:fixed;z-index:2147483646;left:50%;bottom:80px;transform:translateX(-50%);display:flex;align-items:center;gap:4px;padding:8px;border:1px solid var(--fdc-line);border-radius:8px;background:white;box-shadow:0 8px 24px rgb(0 0 0 / 14%);pointer-events:auto; }.compare-bar[hidden] { display:none; }.compare-bar strong { padding:0 8px;font-size:12px;font-weight:550; }.compare-bar button { height:32px;display:flex;align-items:center;gap:4px;padding:0 8px;border:0;border-radius:4px;background:transparent;color:#555;font-size:8px;cursor:pointer; }.compare-bar button:hover,.compare-bar button.active { color:white;background:var(--fdc-ink); }.compare-bar svg { width:12px;height:12px; }.compare-bar input[type="range"] { width:96px;accent-color:var(--fdc-ink);cursor:ew-resize; }
|
|
1152
|
+
.command-palette { position:fixed;z-index:2147483647;left:50%;top:18%;transform:translateX(-50%);width:min(480px,calc(100vw - 32px));overflow:hidden;border:1px solid #d8d8d8;border-radius:12px;background:white;box-shadow:0 24px 80px rgb(0 0 0 / 24%);pointer-events:auto; }.command-palette[hidden] { display:none; }.command-search { display:flex;align-items:center;border-bottom:1px solid var(--fdc-line);background:var(--fdc-paper); }.command-palette input { min-width:0;flex:1;width:auto;height:48px;padding:0 16px;border:0;font-size:12px;outline:none; }.command-search .icon-button { flex:none;margin-right:8px; }.command-list { max-height:320px;overflow:auto;padding:4px; }.command-item { width:100%;min-height:40px;display:flex;align-items:center;gap:8px;padding:0 12px;border:0;border-radius:8px;background:transparent;color:#333;text-align:left;font-size:12px;cursor:pointer; }.command-item:hover,.command-item.active { background:var(--fdc-subtle); }.command-item svg { width:16px;height:16px;color:#777; }.command-item small { margin-left:auto;color:#8a8a8a;font-size:8px; }
|
|
1153
|
+
.comparison-stage { position:fixed;z-index:2147483647;inset:12px;display:flex;flex-direction:column;overflow:hidden;border:1px solid #303030;border-radius:12px;background:#111;box-shadow:0 24px 80px rgb(0 0 0 / 35%);pointer-events:auto; }.comparison-stage[hidden] { display:none; }.comparison-stage header { min-height:48px;display:flex;align-items:center;padding:0 8px 0 16px;color:white;border-bottom:1px solid #292929;background:#171717; }.comparison-stage header strong { font-size:12px;font-weight:550; }.comparison-stage header span { margin-left:8px;color:#8d8d8d;font-size:12px; }.comparison-stage header button { width:32px;height:32px;display:grid;place-items:center;margin-left:auto;padding:0;border:0;border-radius:4px;background:transparent;color:#aaa;cursor:pointer; }.comparison-stage header button:hover { color:white;background:#292929; }.comparison-stage header svg { width:16px;height:16px; }.comparison-frames { min-height:0;flex:1;display:grid;grid-template-columns:1fr 1fr;gap:1px;background:#343434; }.comparison-frame { position:relative;min-width:0;min-height:0;background:white; }.comparison-frame span { position:absolute;z-index:1;top:12px;left:12px;padding:4px 8px;border-radius:4px;background:rgb(17 17 17 / 88%);color:white;font-size:8px; }.comparison-frame iframe { width:100%;height:100%;display:block;border:0;background:white; }
|
|
1154
|
+
.impact-list { display:grid;gap:4px;margin-top:8px; }.impact-item { display:flex;align-items:flex-start;gap:4px;color:#666;font-size:8px;line-height:1.35; }.impact-item::before { content:"";width:4px;height:4px;flex:none;margin-top:4px;border-radius:50%;background:#9a9a9a; }.impact-item.warning { color:#985033; }.impact-item.warning::before { background:#d16d51; }
|
|
1155
|
+
.mapping-chooser { grid-column:2;margin-top:8px;padding:8px;background:#fff9ed;border:1px solid #f4ddb2;border-radius:8px; }.mapping-chooser>strong { display:block;margin-bottom:4px;color:#80561c;font-size:8px;font-weight:550; }.mapping-option { display:flex;align-items:flex-start;gap:8px;padding:4px 0;color:#5f4b2d;font-size:8px;line-height:1.35;cursor:pointer; }.mapping-option input { margin:1px 0 0;accent-color:var(--fdc-signal); }.mapping-option small { display:block;color:#8b7758;font-size:8px; }
|
|
1156
|
+
.workbench { position:fixed;z-index:2147483647;inset:12px;display:flex;flex-direction:column;overflow:hidden;border:1px solid #303030;border-radius:12px;background:#111;box-shadow:0 24px 80px rgb(0 0 0 / 35%);pointer-events:auto; }.workbench[hidden] { display:none; }.workbench-head { min-height:48px;display:flex;align-items:center;gap:8px;padding:0 8px 0 16px;color:white;border-bottom:1px solid #292929;background:#171717; }.workbench-head strong { font-size:12px;font-weight:550; }.workbench-context { color:#8d8d8d;font-size:12px; }.workbench-controls { display:flex;align-items:center;gap:4px;margin-left:auto; }.workbench-controls button,.workbench-controls select { height:32px;padding:0 8px;border:1px solid #343434;border-radius:8px;background:#202020;color:#d7d7d7;font-size:12px;cursor:pointer; }.workbench-controls button.active { color:#111;background:white;border-color:white; }.workbench-controls .icon-button { width:32px;padding:0; }.workbench-stage { min-height:0;flex:1;display:flex;align-items:flex-start;justify-content:center;overflow:auto;padding:28px;background-color:#151515;background-image:linear-gradient(#202020 1px,transparent 1px),linear-gradient(90deg,#202020 1px,transparent 1px);background-size:24px 24px; }.frame-shell { position:relative;flex:none;border-radius:8px;background:white;box-shadow:0 0 0 1px #333,0 20px 60px rgb(0 0 0 / 35%);overflow:hidden; }.frame-label { position:absolute;left:0;top:-20px;color:#8f8f8f;font-size:8px; }.frame-shell iframe { display:block;width:100%;height:100%;border:0;background:white; }.workbench-warning { position:absolute;left:50%;bottom:20px;transform:translateX(-50%);max-width:520px;padding:8px 12px;border:1px solid #3b3b3b;border-radius:8px;background:#1d1d1d;color:#aaa;font-size:12px;line-height:1.4; }
|
|
1157
|
+
.library-panel { position:fixed;z-index:2147483647;top:12px;right:352px;width:300px;max-height:calc(100vh - 24px);display:flex;flex-direction:column;overflow:hidden;border:1px solid var(--fdc-line);border-radius:12px;background:white;box-shadow:0 12px 32px rgb(0 0 0 / 14%);pointer-events:auto; }.library-panel[hidden] { display:none; }.library-head { min-height:48px;display:flex;align-items:center;gap:8px;padding:0 8px 0 12px;border-bottom:1px solid var(--fdc-line); }.library-head svg { width:16px;height:16px; }.library-head strong { font-size:12px;font-weight:550; }.library-head span { color:var(--fdc-muted);font-size:8px; }.library-head .icon-button { margin-left:auto; }.library-actions { display:flex;gap:4px;padding:8px;border-bottom:1px solid var(--fdc-line); }.library-actions button { min-height:32px;display:flex;align-items:center;justify-content:center;gap:8px;flex:1;border:1px solid var(--fdc-line);border-radius:4px;background:white;color:#4d4d4d;font-size:8px;cursor:pointer; }.library-actions button:disabled { opacity:.45;cursor:not-allowed; }.library-actions svg { width:12px;height:12px; }.library-body { min-height:120px;overflow:auto;padding:8px; }.library-section+.library-section { margin-top:12px; }.library-section-head { display:flex;align-items:center;margin:0 4px 8px;color:#666;font-size:8px;text-transform:uppercase;letter-spacing:.04em; }.library-section-head span { margin-left:auto;text-transform:none;letter-spacing:0; }.memory-card { padding:8px;border:1px solid var(--fdc-line);border-radius:8px;background:white; }.memory-card+.memory-card { margin-top:4px; }.memory-card-top { display:flex;align-items:center;gap:8px; }.memory-card-top strong { min-width:0;overflow:hidden;text-overflow:ellipsis;font-size:12px;font-weight:550;white-space:nowrap; }.memory-status { width:8px;height:8px;flex:none;border-radius:50%;background:#2ca67f; }.memory-card p { margin:4px 0 0;color:var(--fdc-muted);font-size:8px;line-height:1.45; }.memory-card-actions { display:flex;gap:4px;margin-top:8px; }.memory-card-actions button { height:28px;padding:0 8px;border:1px solid var(--fdc-line);border-radius:4px;background:white;color:#555;font-size:8px;cursor:pointer; }.memory-card-actions button:first-child { flex:1;color:white;border-color:var(--fdc-ink);background:var(--fdc-ink); }.library-empty { padding:20px 12px;color:var(--fdc-muted);font-size:8px;line-height:1.5;text-align:center; }
|
|
1158
|
+
.review-visual { display:grid;grid-template-columns:1fr 1fr;gap:4px;margin-top:8px; }.review-sample { position:relative;min-height:36px;display:grid;place-items:center;overflow:hidden;border:1px solid var(--fdc-line);border-radius:4px;background:#fafafa;color:#777;font-size:8px; }.review-sample::after { content:attr(data-label);position:absolute;left:4px;bottom:4px;padding:4px 4px;border-radius:4px;background:rgb(255 255 255 / 84%);color:#777;font-size:8px; }.review-sample>i { width:32px;height:16px;display:block;border:1px solid #bbb;background:var(--sample-color,#e8e8e8);border-radius:var(--sample-radius,4px);transform:scale(var(--sample-scale,1)); }.review-card.locating { background:#f5f9ff; }.review-group-title .included-count { margin-left:auto;color:#23715c; }.review-group-title .group-total { margin-left:4px; }.baseline-badge { display:inline-flex;align-items:center;gap:4px;margin-top:8px;padding:4px 8px;border-radius:4px;color:#23715c;background:#edf8f4;font-size:8px; }.baseline-badge::before { content:"";width:4px;height:4px;border-radius:50%;background:#2ca67f; }
|
|
1159
|
+
.workbench-matrix { display:grid;grid-template-columns:92px repeat(var(--matrix-columns),minmax(112px,1fr));gap:1px;width:min(900px,100%);margin:0 auto 24px;padding:1px;background:#303030;border-radius:8px;overflow:hidden; }.matrix-cell { min-height:52px;display:flex;flex-direction:column;align-items:flex-start;justify-content:center;gap:4px;padding:8px;border:0;background:#1d1d1d;color:#ddd;font-size:8px;text-align:left;cursor:pointer; }.matrix-cell:hover { background:#252525; }.matrix-cell.header { min-height:32px;color:#888;background:#181818;cursor:default; }.matrix-cell strong { font-size:8px;font-weight:500; }.matrix-cell span { color:#777;font-size:8px; }.matrix-cell.verified span { color:#68caa8; }.workbench-stage.matrix-mode { display:block; }.workbench-stage.matrix-mode .frame-shell { margin:0 auto; }
|
|
1160
|
+
.component-actions { display:flex;gap:4px;padding:0 8px 8px 44px; }.component-actions button,.component-variants button { height:24px;padding:0 8px;border:1px solid #e6e1f7;border-radius:4px;color:#66598c;background:#faf9ff;font-size:8px;cursor:pointer; }.component-actions button:hover,.component-variants button:hover { border-color:#c8bdf1;background:#f4f0ff; }
|
|
1161
|
+
.token-provenance { grid-column:2;display:flex;align-items:center;gap:4px;margin-top:4px;color:#23715c;font-size:8px; }.token-provenance.literal { color:#985033; }.token-provenance::before { content:"";width:4px;height:4px;border-radius:50%;background:currentColor; }
|
|
1085
1162
|
.workbench-matrix[hidden] { display:none; }
|
|
1086
1163
|
/* Foundry dark instrument theme */
|
|
1087
|
-
.panel,.layers-panel,.health-panel,.library-panel,.tool-shelf,.onboarding-card,.compare-bar,.command-palette,.status-popover,.canvas-variant { color:var(--fdc-ink);background:var(--fdc-surface);border-color:var(--fdc-line);box-shadow:0 1px
|
|
1164
|
+
.panel,.layers-panel,.health-panel,.library-panel,.tool-shelf,.onboarding-card,.compare-bar,.command-palette,.status-popover,.canvas-variant { color:var(--fdc-ink);background:var(--fdc-surface);border-color:var(--fdc-line);box-shadow:0 1px 4px rgb(0 0 0 / 24%),0 16px 36px rgb(0 0 0 / 36%); }
|
|
1088
1165
|
.top,.top-actions,.selection,.scope,.controls,.property-section,.change-dock,.footer,.review-view,.review-head,.review-group,.review-group-title,.review-actions,.layers-head,.layer-tree,.health-head,.health-list,.health-footer,.library-head,.library-actions,.library-body,.memory-card,.context-tools,.native-panel { color:var(--fdc-ink);background:var(--fdc-surface);border-color:var(--fdc-line); }
|
|
1089
1166
|
.inspector-heading,.review-toolbar { background:rgb(28 28 31 / 96%);border-color:var(--fdc-line); }
|
|
1090
1167
|
.section-head,.section-head:hover,.onboarding-step { color:var(--fdc-ink);background:var(--fdc-surface); }
|
|
@@ -1099,106 +1176,216 @@ var PANEL_CSS = `
|
|
|
1099
1176
|
.scope select,.property-control input,.property-control select,.compact-control input,.compact-control select,.native-search,.command-palette input { color:var(--fdc-ink);background:var(--fdc-paper);border-color:var(--fdc-line); }
|
|
1100
1177
|
.scope select option,.property-control select option,.compact-control select option { color:var(--fdc-ink);background:var(--fdc-paper); }
|
|
1101
1178
|
.property-label,.section-head,.color-value,.layer-row,.command-item,.library-section-head { color:#d1d1d6; }.field-prefix,.unit,.control-field .unit-select,.section-toggle>svg,.layer-row .chevron,.layer-row .layer-icon,.command-item svg,.command-item small { color:var(--fdc-muted); }
|
|
1102
|
-
.layer-row.selected { color:#a9cbff;background:
|
|
1179
|
+
.layer-row.selected { color:#a9cbff;background:transparent; }.layer-row.selected .layer-icon { color:#86b7ff; }
|
|
1103
1180
|
.layer-row .layer-meta { color:#b5b5bc;background:var(--fdc-elevated); }.layer-row.selected .layer-meta { color:#a9cbff;background:#23416d; }
|
|
1104
1181
|
.layers-search { background:var(--fdc-paper); }.layers-switch { background:var(--fdc-paper);border-color:var(--fdc-line); }.layers-switch button { color:var(--fdc-muted);background:transparent; }.layers-switch button:hover,.layers-switch button.active { color:var(--fdc-ink);background:var(--fdc-elevated); }.layers-switch button[data-layer-view="components"].active,.layers-switch button[data-layer-view="components"].active span { color:#c1b5f5;background:var(--fdc-component-soft); }
|
|
1105
1182
|
.token-chip { color:#d1d1d6;background:var(--fdc-paper);border-color:var(--fdc-line); }.token-chip:hover { color:#9ec5ff;background:var(--fdc-signal-soft);border-color:#315482; }
|
|
1183
|
+
.section-actions { flex:none;display:flex;align-items:center;gap:4px; }.section-action,.section-actions button,.category-action,.token-menu-trigger { height:28px;display:inline-flex;align-items:center;justify-content:center;gap:4px;padding:0 8px;border:1px solid transparent;border-radius:4px;color:var(--fdc-muted);background:transparent;font:500 8px/1 var(--fdc-font);cursor:pointer; }.section-action:hover,.section-actions button:hover,.category-action:hover,.token-menu-trigger:hover { color:var(--fdc-ink);background:var(--fdc-elevated);border-color:var(--fdc-line); }.section-action svg,.category-action svg,.token-menu-trigger svg { width:12px;height:12px; }.type-presets button { width:28px;padding:0; }.category-action { margin-left:auto; }.inspector-heading .property-count { margin-left:4px; }
|
|
1184
|
+
.token-menu-wrap { position:relative;display:flex;align-items:center;margin-top:4px; }.token-menu-trigger { width:28px;padding:0; }.token-menu { position:absolute;z-index:8;top:32px;right:0;width:232px;max-height:220px;overflow:auto;padding:4px;border:1px solid var(--fdc-line);border-radius:8px;background:var(--fdc-elevated);box-shadow:0 16px 36px rgb(0 0 0 / 38%); }.token-menu[hidden] { display:none; }.token-option { width:100%;height:32px;display:grid;grid-template-columns:16px minmax(0,1fr) auto;align-items:center;gap:8px;padding:0 8px;border:0;border-radius:4px;color:var(--fdc-ink);background:transparent;text-align:left;cursor:pointer; }.token-option:hover { background:var(--fdc-subtle); }.token-option .swatch { width:12px;height:12px;border:1px solid rgb(255 255 255 / 14%);border-radius:4px;background:var(--token-color); }.token-option span { overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:8px; }.token-option code { color:var(--fdc-muted);font:400 8px/1 var(--fdc-font); }
|
|
1185
|
+
.color-picker-trigger { width:100%;height:32px;display:flex;align-items:center;gap:8px;padding:0 32px 0 8px;border:0;background:transparent;color:var(--fdc-ink);font:400 12px/1 var(--fdc-font);text-align:left;cursor:pointer; }.color-picker-trigger .color-swatch { margin-left:0; }.color-popover { position:fixed;z-index:2147483647;width:280px;padding:12px;border:1px solid var(--fdc-line);border-radius:12px;color:var(--fdc-ink);background:var(--fdc-elevated);box-shadow:0 20px 60px rgb(0 0 0 / 48%);pointer-events:auto; }.color-popover[hidden] { display:none; }.color-popover-head { height:28px;display:flex;align-items:center;margin-bottom:8px; }.color-popover-head strong { font-size:12px;font-weight:550; }.color-popover-head button { margin-left:auto; }.color-plane { position:relative;width:100%;height:152px;overflow:hidden;border-radius:8px;background:linear-gradient(to top,#000,transparent),linear-gradient(to right,#fff,transparent),hsl(var(--picker-hue) 100% 50%);cursor:crosshair;touch-action:none; }.color-plane-handle { position:absolute;width:12px;height:12px;transform:translate(-50%,-50%);border:4px solid white;border-radius:50%;box-shadow:0 1px 4px #000;pointer-events:none; }.color-sliders { display:grid;gap:8px;margin-top:8px; }.color-sliders label { display:grid;grid-template-columns:36px minmax(0,1fr);align-items:center;gap:8px;color:var(--fdc-muted);font-size:8px; }.color-sliders input[type="range"] { width:100%;accent-color:var(--fdc-signal); }.color-fields { display:grid;grid-template-columns:minmax(0,1fr) 36px;gap:8px;margin-top:8px; }.color-fields input { min-width:0;height:32px;padding:0 8px;border:1px solid var(--fdc-line);border-radius:4px;color:var(--fdc-ink);background:var(--fdc-paper);font:400 12px/1 var(--fdc-font);outline:none; }.color-fields button { height:32px;display:grid;place-items:center;padding:0;border:1px solid var(--fdc-line);border-radius:4px;color:var(--fdc-muted);background:var(--fdc-paper);cursor:pointer; }.color-fields button:hover { color:var(--fdc-ink); }.color-fields svg { width:12px;height:12px; }.color-popover-section { margin-top:12px;padding-top:8px;border-top:1px solid var(--fdc-line); }.color-popover-section strong { display:block;margin-bottom:8px;color:var(--fdc-muted);font-size:8px;font-weight:500;text-transform:uppercase;letter-spacing:.04em; }.color-swatches { display:flex;flex-wrap:wrap;gap:8px; }.color-swatches button { width:24px;height:24px;padding:0;border:1px solid rgb(255 255 255 / 16%);border-radius:4px;background:var(--picker-swatch);cursor:pointer; }.color-swatches button:hover { box-shadow:0 0 0 4px var(--fdc-signal); }
|
|
1106
1186
|
.review-summary { background:var(--fdc-paper); }.review-before { color:var(--fdc-muted);background:var(--fdc-subtle); }.review-sample { color:var(--fdc-muted);background:var(--fdc-paper);border-color:var(--fdc-line); }.review-sample::after { color:var(--fdc-muted);background:rgb(28 28 31 / 88%); }
|
|
1107
1187
|
.confidence-pill { color:#9ec5ff;background:var(--fdc-signal-soft); }.confidence-pill.unresolved { color:#f2ae8f;background:#42271d; }
|
|
1108
1188
|
.session-status,.session-status.saved { color:#83d8bb;background:#18352c; }.session-status.saving { color:#efd979;background:#3d3417; }.session-status.error,.session-status.offline { color:#f0a18b;background:#40251f; }
|
|
1109
1189
|
.empty-state-icon { color:#9ec5ff;background:var(--fdc-signal-soft); }.onboarding-card-head span { color:#e7a68c;background:#36231d; }
|
|
1110
1190
|
.change-count { color:var(--fdc-ink);background:var(--fdc-paper); }
|
|
1191
|
+
.compare-bar button:hover { color:var(--fdc-ink);background:var(--fdc-subtle); }
|
|
1192
|
+
.compare-bar button.active { color:#141416;background:#f0f1f3; }
|
|
1193
|
+
.toast { color:#141416;background:#f0f1f3;border:1px solid #ffffff;box-shadow:0 8px 20px rgb(0 0 0 / 30%); }
|
|
1111
1194
|
.change-dock .dock-review,.footer .review,.review-actions .apply,.onboarding-actions .onboarding-start,.health-actions .health-fix,.health-footer .health-rescan,.memory-card-actions button:first-child,.empty-state-actions button:first-child { color:#141416;background:#f0f1f3;border-color:#f0f1f3; }.change-dock .dock-review:hover,.footer .review:hover,.review-actions .apply:hover { color:#141416;background:#ffffff; }
|
|
1112
1195
|
.panel-resizer::after { background:#4a4a50; }
|
|
1113
|
-
.empty-state { min-height:220px;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:36px
|
|
1114
|
-
.review-head strong { font-size:
|
|
1115
|
-
.onboarding-card { bottom:
|
|
1196
|
+
.empty-state { min-height:220px;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:36px 32px;text-align:center; }.empty-state-icon { width:40px;height:40px;display:grid;place-items:center;margin-bottom:16px;border-radius:12px; }.empty-state-icon svg { width:16px;height:16px; }.empty-state strong { font-size:12px;font-weight:550;letter-spacing:-.01em; }.empty-state p { max-width:232px;margin:8px 0 0;color:var(--fdc-muted);font-size:12px;line-height:1.55; }.empty-state-actions { display:flex;align-items:center;gap:8px;margin-top:16px; }.empty-state-actions button { height:32px;padding:0 12px;border:1px solid var(--fdc-line);border-radius:8px;font-size:12px;cursor:pointer; }.empty-state kbd { margin-top:12px;color:var(--fdc-muted);font:400 8px/1.4 var(--fdc-font); }
|
|
1197
|
+
.review-head strong { font-size:12px; }.review-summary { padding:12px 16px 12px;border-bottom:1px solid var(--fdc-line);background:#fcfcfc; }.review-summary strong { display:block;font-size:12px;font-weight:550;letter-spacing:-.01em; }.review-summary span { display:block;margin-top:4px;color:var(--fdc-muted);font-size:12px;line-height:1.45; }.review-summary.attention strong { color:#8b4d3d; }.review-toolbar button { height:28px;padding:0 8px;font-size:12px; }
|
|
1198
|
+
.onboarding-card { bottom:84px;width:340px;padding:16px;border-radius:12px;box-shadow:0 16px 40px rgb(0 0 0 / 15%); }.onboarding-card-head { gap:8px; }.onboarding-card-head span { width:28px;height:28px;display:grid;place-items:center;color:#a75031;background:#fbf1ed;border-radius:8px; }.onboarding-card-head svg { width:16px;height:16px;color:inherit; }.onboarding-card-head strong { font-size:12px;letter-spacing:-.015em; }.onboarding-card>p { margin:8px 0 16px;font-size:12px;line-height:1.55; }.onboarding-steps { display:grid;grid-template-columns:1fr;gap:1px;border:1px solid var(--fdc-line);border-radius:8px;overflow:hidden;background:var(--fdc-line); }.onboarding-step { display:grid;grid-template-columns:24px 1fr;gap:8px;padding:8px 12px;background:white;text-align:left; }.onboarding-step b { color:#8a8a8a;font-size:8px;font-weight:500;letter-spacing:.04em; }.onboarding-step strong { display:block;font-size:12px;font-weight:550; }.onboarding-step small { display:block;margin-top:4px;color:var(--fdc-muted);font-size:8px;line-height:1.35; }.onboarding-actions { gap:8px;margin-top:12px; }.onboarding-actions button { height:32px;padding:0 12px;border-radius:8px;font-size:12px; }
|
|
1116
1199
|
.onboarding-card,.onboarding-step { color:var(--fdc-ink);background:var(--fdc-surface); }.onboarding-card-head span { color:#e7a68c;background:#36231d; }.onboarding-actions button { color:var(--fdc-ink);background:var(--fdc-elevated);border-color:var(--fdc-line); }.onboarding-actions .onboarding-start { color:#141416;background:#f0f1f3;border-color:#f0f1f3; }
|
|
1117
1200
|
.review-summary { color:var(--fdc-ink);background:var(--fdc-paper); }.empty-state-icon { color:#9ec5ff;background:var(--fdc-signal-soft); }
|
|
1118
|
-
.component-card,.health-card { color:var(--fdc-ink);background:var(--fdc-surface);border-color:var(--fdc-line); }.component-card.selected { color:var(--fdc-ink);background:var(--fdc-component-soft);border-color:#5c4b9e;box-shadow:0 0 0
|
|
1201
|
+
.component-card,.health-card { color:var(--fdc-ink);background:var(--fdc-surface);border-color:var(--fdc-line); }.component-card.selected { color:var(--fdc-ink);background:var(--fdc-component-soft);border-color:#5c4b9e;box-shadow:0 0 0 4px rgb(155 135 245 / 10%); }.component-variants span,.component-actions button,.component-variants button { color:#c1b5f5;background:var(--fdc-component-soft);border-color:#4a406f; }.health-score::before { background:var(--fdc-surface); }.health-filters button { color:var(--fdc-muted); }.health-filters button:hover,.health-filters button.active { color:var(--fdc-ink);background:var(--fdc-elevated);border-color:var(--fdc-line); }.health-card p,.health-evidence { color:var(--fdc-muted); }.mapping-chooser { color:#f0c9a8;background:#35291d;border-color:#665039; }.mapping-chooser>strong,.mapping-option,.mapping-option small { color:#e7c29f; }.review-card.locating { background:var(--fdc-signal-soft); }.baseline-badge { color:#83d8bb;background:#18352c; }
|
|
1119
1202
|
/* Workspace ownership */
|
|
1120
|
-
.workspace-bar { position:fixed;z-index:2147483647;top:12px;left:50%;min-height:48px;display:flex;align-items:center;gap:
|
|
1121
|
-
.panel { top:72px;bottom:12px;max-height:none;overflow:clip; }.panel[hidden] { display:none; }.inspector-head { min-height:44px;display:flex;align-items:center;gap:
|
|
1203
|
+
.workspace-bar { position:fixed;z-index:2147483647;top:12px;left:50%;min-height:48px;display:flex;align-items:center;gap:4px;padding:8px;transform:translateX(-50%);color:var(--fdc-ink);background:rgb(28 28 31 / 96%);border:1px solid var(--fdc-line);border-radius:12px;box-shadow:0 4px 4px rgb(0 0 0 / 20%),0 16px 36px rgb(0 0 0 / 34%);backdrop-filter:blur(16px);pointer-events:auto; }.workspace-bar .brand { flex:none;padding:0 8px; }.workspace-bar .brand-copy b { font-size:12px; }.workspace-bar .session-status { margin:0; }.workspace-actions { display:flex;align-items:center;gap:4px; }.workspace-divider { width:1px;height:24px;background:var(--fdc-line); }.workspace-bar .icon-button { width:36px;height:36px; }.workspace-bar .icon-button.active { color:#9ec5ff;background:var(--fdc-signal-soft); }.workspace-bar .status-popover { top:44px;right:auto;left:60px; }
|
|
1204
|
+
.panel { top:72px;bottom:12px;max-height:none;overflow:clip; }.panel[hidden] { display:none; }.inspector-head { min-height:44px;display:flex;align-items:center;gap:8px;padding:0 8px 0 12px;border-bottom:1px solid var(--fdc-line);background:var(--fdc-surface); }.inspector-head strong { font-size:12px;font-weight:550; }.inspector-head span { color:var(--fdc-muted);font-size:8px; }.inspector-head .icon-button { margin-left:auto; }.controls { min-height:0;flex:1; }.inspector-baseline { padding:0 12px 8px;border-bottom:1px solid var(--fdc-line); }.inspector-category { border-bottom:1px solid var(--fdc-line); }.inspector-category>.inspector-heading { position:sticky;top:0;z-index:2;height:44px;padding:0 8px 0 4px;background:rgb(28 28 31 / 97%); }.inspector-category>.inspector-heading .section-toggle { min-height:44px; }.inspector-category>.inspector-heading .section-toggle>svg { width:16px;height:16px;color:var(--fdc-muted);transform:none; }.inspector-category.collapsed>.category-body { display:none; }.inspector-category.collapsed>.inspector-heading .section-toggle>svg { transform:rotate(-90deg); }.inspector-category .property-section { margin-left:12px;border-left:1px solid var(--fdc-line); }.inspector-category .property-section .section-head { padding-left:8px; }.inspector-category[data-category="position"]>.category-body>.property-section>.section-head { display:none; }.category-body>.context-tools,.category-body>.native-panel,.category-body>.design-health { margin-left:12px; }
|
|
1122
1205
|
.inspector-scroll { min-width:0;min-height:0;flex:1;overflow-x:hidden;overflow-y:auto;background:var(--fdc-surface); }
|
|
1123
|
-
.inspector-scroll::-webkit-scrollbar { width:
|
|
1206
|
+
.inspector-scroll::-webkit-scrollbar { width:8px; }
|
|
1124
1207
|
.inspector-scroll::-webkit-scrollbar-track { background:transparent; }
|
|
1125
|
-
.inspector-scroll::-webkit-scrollbar-thumb { background:#4a4a50;border-radius:
|
|
1208
|
+
.inspector-scroll::-webkit-scrollbar-thumb { background:#4a4a50;border-radius:1000px; }
|
|
1126
1209
|
.inspector-scroll::-webkit-scrollbar-thumb:hover { background:#626269; }
|
|
1127
1210
|
.inspector-scroll>.selection,.inspector-scroll>.scope,.inspector-scroll>.controls { width:100%;box-sizing:border-box; }
|
|
1128
1211
|
.inspector-scroll>.controls { min-height:0;overflow:visible;flex:none; }
|
|
1129
1212
|
/* Fluid inspector controls */
|
|
1130
1213
|
.inspector-category .property-section { width:100%;margin-left:0;border-left:0; }
|
|
1131
|
-
.inspector-category .property-section .section-head { padding-left:
|
|
1214
|
+
.inspector-category .property-section .section-head { padding-left:12px; }
|
|
1132
1215
|
.category-body>.context-tools,.category-body>.native-panel { width:100%;margin-left:0; }
|
|
1133
|
-
.category-body>.design-health { width:auto;margin:12px
|
|
1134
|
-
.section-grid { width:100%;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;padding:12px
|
|
1216
|
+
.category-body>.design-health { width:auto;margin:12px 16px 0; }
|
|
1217
|
+
.section-grid { width:100%;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;padding:12px 16px 16px;box-sizing:border-box; }
|
|
1135
1218
|
.section-grid.stacked { grid-template-columns:minmax(0,1fr); }
|
|
1136
1219
|
.section-grid:not(.stacked)>:last-child:nth-child(odd) { grid-column:1/-1; }
|
|
1137
|
-
.property-control { min-width:0;display:flex;flex-direction:column;align-items:stretch;gap:
|
|
1138
|
-
.property-control .property-label { min-height:
|
|
1139
|
-
.property-control .control-field { width:100%;height:
|
|
1220
|
+
.property-control { min-width:0;display:flex;flex-direction:column;align-items:stretch;gap:8px; }
|
|
1221
|
+
.property-control .property-label { min-height:12px;line-height:12px; }
|
|
1222
|
+
.property-control .control-field { width:100%;height:36px; }
|
|
1140
1223
|
.property-control input,.property-control select { height:32px; }
|
|
1141
1224
|
.section-grid.two { grid-template-columns:repeat(2,minmax(0,1fr));gap:8px; }
|
|
1142
1225
|
.section-grid.two .compact-control { position:relative;display:block; }
|
|
1143
|
-
.compact-control .field-prefix,.compact-control .field-prefix.wide { width:44px;min-width:44px;box-sizing:border-box;padding-left:
|
|
1144
|
-
.section-grid.two .control-field { width:100%;height:
|
|
1226
|
+
.compact-control .field-prefix,.compact-control .field-prefix.wide { width:44px;min-width:44px;box-sizing:border-box;padding-left:12px; }
|
|
1227
|
+
.section-grid.two .control-field { width:100%;height:36px; }
|
|
1145
1228
|
.section-grid.two .compact-control input,.section-grid.two .compact-control select { height:32px; }
|
|
1146
1229
|
.control-field>.control-reset { position:absolute;z-index:2;top:4px;right:4px;width:24px;height:24px; }
|
|
1147
1230
|
.control-field>input { padding-right:32px; }
|
|
1148
|
-
.control-field:has(>select:not(.unit-select))>.control-reset { right:
|
|
1231
|
+
.control-field:has(>select:not(.unit-select))>.control-reset { right:32px; }
|
|
1149
1232
|
.control-field:has(>select:not(.unit-select))>select:not(.unit-select) { padding-right:56px!important; }
|
|
1150
|
-
.control-field:has(>.unit-select)>.control-reset { right:
|
|
1151
|
-
.control-field:has(>.unit)>.control-reset { right:
|
|
1233
|
+
.control-field:has(>.unit-select)>.control-reset { right:48px; }
|
|
1234
|
+
.control-field:has(>.unit)>.control-reset { right:24px; }
|
|
1152
1235
|
/* Consistent inset chevrons for every select input */
|
|
1153
|
-
select { -webkit-appearance:none;appearance:none;padding-right:
|
|
1154
|
-
.control-field .unit-select { width:48px;padding-right:20px!important;padding-left:
|
|
1155
|
-
.layers-panel { top:72px;bottom:
|
|
1156
|
-
|
|
1157
|
-
.
|
|
1236
|
+
select { -webkit-appearance:none;appearance:none;padding-right:32px!important;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='%238f8f96' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 6.5 3 3 3-3'/%3E%3C/svg%3E")!important;background-repeat:no-repeat!important;background-position:right 8px center!important;background-size:16px 16px!important; }
|
|
1237
|
+
.control-field .unit-select { width:48px;padding-right:20px!important;padding-left:4px!important;background-position:right 4px center!important;background-size:16px 16px!important; }
|
|
1238
|
+
.layers-panel { top:72px;bottom:auto;max-height:min(560px,calc(100vh - 84px)); }.layers-panel[hidden] { display:none; }.layers-switch { flex:none; }
|
|
1239
|
+
/* Compact tree rhythm: rows use the panel width while hierarchy remains legible. */
|
|
1240
|
+
.layer-tree { padding:4px 4px;scrollbar-gutter:auto;scrollbar-width:thin;scrollbar-color:#4a4a50 transparent; }
|
|
1241
|
+
.layer-tree::-webkit-scrollbar { width:8px; }
|
|
1242
|
+
.layer-tree::-webkit-scrollbar-track { background:transparent; }
|
|
1243
|
+
.layer-tree::-webkit-scrollbar-thumb { background:#4a4a50;border-radius:1000px; }
|
|
1244
|
+
.layer-row { padding-left:calc(4px + var(--layer-depth) * 12px);padding-right:4px; }
|
|
1245
|
+
.layer-selection-glide { right:4px;left:4px; }
|
|
1246
|
+
.layer-toggle,.layer-select { height:32px; }
|
|
1247
|
+
.layer-select { padding-right:4px;padding-left:4px; }
|
|
1248
|
+
.layer-row .layer-meta { margin-right:4px; }
|
|
1249
|
+
.component-list { padding-right:0;padding-left:0; }
|
|
1250
|
+
.component-main { padding-right:8px;padding-left:8px; }
|
|
1251
|
+
.component-variants,.component-actions { padding-right:8px;padding-left:40px; }
|
|
1252
|
+
/* Keep every inspector field on the same 36px interaction rhythm. */
|
|
1253
|
+
.property-control .control-field,.section-grid.two .control-field,.compact-control .control-field { height:36px; }
|
|
1254
|
+
.property-control input,.property-control select,.section-grid.two .compact-control input,.section-grid.two .compact-control select { height:36px; }
|
|
1255
|
+
.control-field>.control-reset { top:4px; }
|
|
1256
|
+
.utility-panel { z-index:2147483646;max-height:none;min-width:280px;min-height:280px;box-shadow:0 4px 4px rgb(0 0 0 / 22%),0 20px 48px rgb(0 0 0 / 42%); }.utility-panel[hidden] { display:none; }.utility-handle { cursor:grab;user-select:none;touch-action:none; }.utility-handle:active { cursor:grabbing; }.utility-resizer { position:absolute;right:4px;bottom:4px;width:20px;height:20px;padding:0;border:0;background:transparent;cursor:nwse-resize; }.utility-resizer::after { content:"";position:absolute;right:4px;bottom:4px;width:8px;height:8px;border-right:1px solid var(--fdc-muted);border-bottom:1px solid var(--fdc-muted); }.health-panel,.library-panel { right:auto;max-height:none; }.health-list,.library-body { min-height:0;flex:1; }
|
|
1257
|
+
.change-tray { position:fixed;z-index:2147483646;right:var(--fdc-canvas-right,376px);bottom:12px;left:var(--fdc-canvas-left,276px);min-height:0;display:flex;flex-direction:column;align-items:stretch;padding:0;overflow:hidden;color:var(--fdc-ink);background:var(--fdc-surface);border:1px solid var(--fdc-line);border-radius:12px;box-shadow:0 4px 4px rgb(0 0 0 / 22%),0 20px 48px rgb(0 0 0 / 36%);pointer-events:auto; }.change-tray[hidden] { display:none; }.change-tray-summary { min-height:48px;display:grid;grid-template-columns:minmax(0,1fr) auto 32px 72px;align-items:center;gap:8px;padding:8px 8px 8px 12px; }.change-tray-summary .change-count { margin-right:4px; }.change-tray-summary button { height:32px;border:1px solid var(--fdc-line);border-radius:8px;color:var(--fdc-ink);background:var(--fdc-elevated);cursor:pointer; }.change-tray-summary .tray-compare { width:32px;display:grid;place-items:center;padding:0; }.change-tray-summary .tray-compare svg { width:12px;height:12px; }.change-tray-summary button:disabled { opacity:.35;cursor:not-allowed; }.change-tray.expanded { height:min(420px,45vh); }.change-tray.expanded .change-tray-summary { border-bottom:1px solid var(--fdc-line); }.change-tray .review-view:not([hidden]) { min-width:0;min-height:0;display:flex; }.change-tray .review-body { min-height:0;flex:1;display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));align-content:start;overflow:auto; }.change-tray .review-group { min-width:0;border-right:1px solid var(--fdc-line); }.change-tray .review-actions { flex:none;grid-template-columns:100px minmax(180px,280px);justify-content:end; }.change-tray .review-head { flex:none; }.change-tray .review-head span { margin-left:auto; }
|
|
1158
1258
|
/* Focused review list */
|
|
1159
1259
|
.change-tray .review-body { display:block;overflow-x:hidden;overflow-y:auto; }
|
|
1160
|
-
.review-overview { position:sticky;top:0;z-index:3;display:flex;align-items:center;gap:12px;min-height:52px;padding:8px 12px;border-bottom:1px solid var(--fdc-line);background:rgb(20 20 22 / 97%);backdrop-filter:blur(
|
|
1260
|
+
.review-overview { position:sticky;top:0;z-index:3;display:flex;align-items:center;gap:12px;min-height:52px;padding:8px 12px;border-bottom:1px solid var(--fdc-line);background:rgb(20 20 22 / 97%);backdrop-filter:blur(12px); }
|
|
1161
1261
|
.review-overview .review-summary { min-width:0;flex:1;padding:0;border:0;background:transparent; }
|
|
1162
|
-
.review-overview .review-summary strong { overflow:hidden;text-overflow:ellipsis;font-size:
|
|
1163
|
-
.review-overview .review-summary span { overflow:hidden;text-overflow:ellipsis;margin-top:
|
|
1262
|
+
.review-overview .review-summary strong { overflow:hidden;text-overflow:ellipsis;font-size:12px;white-space:nowrap; }
|
|
1263
|
+
.review-overview .review-summary span { overflow:hidden;text-overflow:ellipsis;margin-top:4px;font-size:8px;white-space:nowrap; }
|
|
1164
1264
|
.review-overview .review-toolbar { position:static;z-index:auto;flex:none;padding:0;border:0;background:transparent;backdrop-filter:none; }
|
|
1165
|
-
.review-overview .review-toolbar button { height:28px;padding:0 8px;border-color:transparent;background:transparent;color:var(--fdc-muted);font-size:
|
|
1265
|
+
.review-overview .review-toolbar button { height:28px;padding:0 8px;border-color:transparent;background:transparent;color:var(--fdc-muted);font-size:8px; }
|
|
1166
1266
|
.review-overview .review-toolbar button:hover { color:var(--fdc-ink);background:var(--fdc-elevated);border-color:var(--fdc-line); }
|
|
1267
|
+
.review-agent { display:grid;grid-template-columns:8px minmax(0,1fr) auto;align-items:center;gap:12px;margin:12px 16px 0;padding:12px;border:1px solid var(--fdc-line);border-radius:8px;background:var(--fdc-paper); }
|
|
1268
|
+
.review-agent>i { width:8px;height:8px;border-radius:50%;background:#d16d51; }
|
|
1269
|
+
.review-agent.connected>i { background:#2ca67f;box-shadow:0 0 0 4px rgb(44 166 127 / 12%); }
|
|
1270
|
+
.review-agent strong,.review-agent span { display:block; }
|
|
1271
|
+
.review-agent strong { font-size:12px;font-weight:600; }
|
|
1272
|
+
.review-agent span { margin-top:4px;color:var(--fdc-muted);font-size:12px;line-height:1.45; }
|
|
1273
|
+
.review-agent button { height:32px;padding:0 12px;border:1px solid var(--fdc-line);border-radius:8px;background:var(--fdc-elevated);color:var(--fdc-ink);font-size:12px;cursor:pointer; }
|
|
1274
|
+
.review-agent button:hover { border-color:var(--fdc-muted); }
|
|
1167
1275
|
.change-tray .review-group { border-right:0;border-bottom:1px solid var(--fdc-line); }
|
|
1168
|
-
.change-tray .review-group-title { position:sticky;top:52px;z-index:2;min-height:
|
|
1169
|
-
.review-group-title .included-count { font-size:
|
|
1170
|
-
.review-card { min-height:
|
|
1171
|
-
.review-card input[type="checkbox"] { margin-top:
|
|
1172
|
-
.review-card-line { min-height:32px;display:grid;grid-template-columns:minmax(120px,1fr) auto minmax(220px,320px)
|
|
1173
|
-
.review-card-line>strong { font-size:
|
|
1174
|
-
.review-values { min-width:0;display:grid;grid-template-columns:minmax(72px,1fr) 12px minmax(
|
|
1175
|
-
.review-before { min-width:0;overflow:hidden;text-overflow:ellipsis;padding:0 8px;color:var(--fdc-muted);background:transparent;font-size:
|
|
1176
|
-
.review-after { height:
|
|
1276
|
+
.change-tray .review-group-title { position:sticky;top:52px;z-index:2;min-height:40px;padding:0 12px;background:rgb(28 28 31 / 98%); }
|
|
1277
|
+
.review-group-title .included-count { font-size:8px; }
|
|
1278
|
+
.review-card { min-height:52px;grid-template-columns:20px minmax(0,1fr);align-items:start;gap:8px;padding:8px 12px; }
|
|
1279
|
+
.review-card input[type="checkbox"] { margin-top:8px; }
|
|
1280
|
+
.review-card-line { min-height:32px;display:grid;grid-template-columns:minmax(120px,1fr) auto minmax(220px,320px) 32px;align-items:center;gap:8px; }
|
|
1281
|
+
.review-card-line>strong { font-size:12px; }
|
|
1282
|
+
.review-values { min-width:0;display:grid;grid-template-columns:minmax(72px,1fr) 12px minmax(92px,1fr);align-items:center;gap:4px;margin:0; }
|
|
1283
|
+
.review-before { min-width:0;overflow:hidden;text-overflow:ellipsis;padding:0 8px;color:var(--fdc-muted);background:transparent;font-size:12px;text-align:right;white-space:nowrap; }
|
|
1284
|
+
.review-after { height:32px;font-size:12px; }
|
|
1177
1285
|
.review-more { position:relative;justify-self:end; }
|
|
1178
|
-
.review-more>summary { width:28px;height:28px;display:grid;place-items:center;border:1px solid transparent;border-radius:
|
|
1286
|
+
.review-more>summary { width:28px;height:28px;display:grid;place-items:center;border:1px solid transparent;border-radius:4px;color:var(--fdc-muted);font:600 12px/1 var(--fdc-font);letter-spacing:1px;list-style:none;cursor:pointer; }
|
|
1179
1287
|
.review-more>summary::-webkit-details-marker { display:none; }
|
|
1180
1288
|
.review-more>summary:hover,.review-more[open]>summary { color:var(--fdc-ink);background:var(--fdc-elevated);border-color:var(--fdc-line); }
|
|
1181
|
-
.review-more .review-card-tools { position:absolute;z-index:5;right:0;top:32px;width:260px;display:flex;flex-direction:column;gap:
|
|
1182
|
-
.review-more .review-card-tools button { width:100%;height:
|
|
1289
|
+
.review-more .review-card-tools { position:absolute;z-index:5;right:0;top:32px;width:260px;display:flex;flex-direction:column;gap:4px;padding:4px;border:1px solid var(--fdc-line);border-radius:8px;background:var(--fdc-elevated);box-shadow:0 12px 24px rgb(0 0 0 / 34%); }
|
|
1290
|
+
.review-more .review-card-tools button { width:100%;height:28px;padding:0 8px;border:0;text-align:left;background:transparent; }
|
|
1183
1291
|
.review-more .review-card-tools button:hover { background:var(--fdc-subtle); }
|
|
1184
|
-
.review-details { margin-top:
|
|
1185
|
-
.review-details summary { min-height:
|
|
1186
|
-
.review-details[open] { padding-bottom:
|
|
1187
|
-
.review-details .review-source,.review-details .impact-list { padding-right:
|
|
1292
|
+
.review-details { margin-top:4px;padding-top:4px;border-top:1px solid var(--fdc-line); }
|
|
1293
|
+
.review-details summary { min-height:28px;display:flex;align-items:center;padding:0 8px;color:var(--fdc-muted);font-size:8px;cursor:pointer; }
|
|
1294
|
+
.review-details[open] { padding-bottom:8px; }
|
|
1295
|
+
.review-details .review-source,.review-details .impact-list { padding-right:8px;padding-left:8px; }
|
|
1188
1296
|
.review-visual { display:none; }
|
|
1189
|
-
@media (max-width:980px){.review-card-line{grid-template-columns:minmax(100px,1fr) auto minmax(
|
|
1190
|
-
@media (max-width:760px){.review-card-line{grid-template-columns:minmax(0,1fr) auto
|
|
1191
|
-
.tool-shelf { bottom:calc(
|
|
1192
|
-
.compare-bar { bottom:calc(
|
|
1193
|
-
@keyframes fdc-selection-title { from { opacity:.45;transform:translateY(
|
|
1194
|
-
@media (max-width:680px){.workspace-bar{right:8px;left:8px;transform:none;overflow-x:auto}.workspace-bar .brand{padding-right:
|
|
1195
|
-
|
|
1297
|
+
@media (max-width:980px){.review-card-line{grid-template-columns:minmax(100px,1fr) auto minmax(192px,260px) 32px}.review-overview .review-summary span{display:none}}
|
|
1298
|
+
@media (max-width:760px){.review-card-line{grid-template-columns:minmax(0,1fr) auto 32px}.review-values{grid-column:1/-1;width:100%}.review-overview{align-items:flex-start}.review-overview .review-toolbar{margin-left:auto}.review-overview .review-toolbar button{padding:0 8px}}
|
|
1299
|
+
.tool-shelf { bottom:calc(20px + var(--fdc-tray-lift,0px)); }.tool-select[hidden],.tool-divider[hidden] { display:none; }.tool-select.interact.active { color:white;background:var(--fdc-signal); }.mode-copy { min-width:88px; }.canvas-actions-divider { margin-left:4px; }.multi-actions-divider { margin-left:4px; }
|
|
1300
|
+
.compare-bar { bottom:calc(80px + var(--fdc-tray-lift,0px)); }
|
|
1301
|
+
@keyframes fdc-selection-title { from { opacity:.45;transform:translateY(4px) } to { opacity:1;transform:none } }
|
|
1302
|
+
@media (max-width:680px){.workspace-bar{right:8px;left:8px;transform:none;overflow-x:auto}.workspace-bar .brand{padding-right:4px}.workspace-bar .brand-copy{display:none}.workspace-bar .status-popover{position:fixed;top:68px;right:8px;left:8px;width:auto}.panel{top:calc(76px + min(32vh,220px));right:8px;bottom:76px;left:8px;width:auto;max-height:none}.layers-panel{top:68px;right:8px;bottom:auto;left:8px;width:auto;max-height:min(32vh,220px)}.utility-panel{top:68px!important;right:8px!important;bottom:76px!important;left:8px!important;width:auto!important;height:auto!important;max-height:none}.utility-resizer{display:none}.scope{display:grid;grid-template-columns:1fr 1fr}.controls{min-height:172px}.change-tray{right:8px;bottom:8px;left:8px}.change-tray.expanded{height:48vh}.change-tray .review-body{grid-template-columns:1fr}.tool-shelf{right:8px;bottom:calc(8px + var(--fdc-tray-lift,0px));left:8px;transform:none;max-width:none}.mode-copy{min-width:0}.mode-copy span{display:none}.tool-select::after,.tab::after{display:none}.onboarding-card{right:12px;bottom:68px;left:12px;width:auto;transform:none}.workbench-controls select,.workbench-controls button:not(.icon-button){max-width:92px}.workbench-matrix{grid-template-columns:76px repeat(var(--matrix-columns),minmax(92px,1fr))}}
|
|
1303
|
+
/* Shared dock geometry and focused review takeover. */
|
|
1304
|
+
.panel,.layers-panel,.change-tray { width:var(--fdc-dock-width,384px); }
|
|
1305
|
+
.layers-panel { max-width:min(520px,calc(100vw - 24px)); }
|
|
1306
|
+
.change-tray { z-index:2147483647;right:12px;bottom:12px;left:auto;height:48px;min-height:48px; }
|
|
1307
|
+
.change-tray-summary { min-height:48px;grid-template-columns:minmax(0,1fr) 32px 72px; }
|
|
1308
|
+
.panel.change-summary-visible .inspector-scroll { padding-bottom:60px;scroll-padding-bottom:60px; }
|
|
1309
|
+
.tool-shelf { bottom:20px; }
|
|
1310
|
+
.compare-bar { bottom:80px; }
|
|
1311
|
+
.review-takeover { position:fixed;z-index:2147483647;inset:0;display:grid;place-items:center;padding:24px;background:rgb(8 8 10 / 72%);backdrop-filter:blur(8px);pointer-events:auto; }
|
|
1312
|
+
.review-takeover[hidden] { display:none; }
|
|
1313
|
+
.review-modal { width:min(1120px,calc(100vw - 48px));height:min(760px,calc(100vh - 48px));display:flex;overflow:hidden;color:var(--fdc-ink);background:var(--fdc-surface);border:1px solid var(--fdc-line);border-radius:12px;box-shadow:0 24px 80px rgb(0 0 0 / 52%); }
|
|
1314
|
+
.review-modal .review-view { width:100%;min-width:0;min-height:0;display:flex; }
|
|
1315
|
+
.review-modal .review-head { flex:none;min-height:52px;padding:0 16px; }
|
|
1316
|
+
.review-modal .review-body { min-height:0;flex:1;display:block;overflow-x:hidden;overflow-y:auto; }
|
|
1317
|
+
.review-modal .review-group { border-right:0;border-bottom:1px solid var(--fdc-line); }
|
|
1318
|
+
.review-modal .review-group-title { position:sticky;top:52px;z-index:2;background:rgb(28 28 31 / 98%); }
|
|
1319
|
+
.review-modal .review-actions { position:sticky;bottom:0;flex:none;grid-template-columns:100px minmax(220px,320px);justify-content:end;padding:12px 16px;background:rgb(28 28 31 / 98%);backdrop-filter:blur(12px); }
|
|
1320
|
+
/* Review is a reading surface, so it uses a larger type scale than the dense inspector. */
|
|
1321
|
+
.review-modal .review-head strong { font-size:16px;font-weight:600;letter-spacing:-.015em; }
|
|
1322
|
+
.review-modal .review-head span { font-size:12px; }
|
|
1323
|
+
.review-modal .review-overview { min-height:60px;padding:12px 16px; }
|
|
1324
|
+
.review-modal .review-overview .review-summary strong { font-size:16px;font-weight:600; }
|
|
1325
|
+
.review-modal .review-overview .review-summary span { margin-top:4px;font-size:12px;line-height:1.45; }
|
|
1326
|
+
.review-modal .review-overview .review-toolbar button { height:36px;padding:0 12px;font-size:12px; }
|
|
1327
|
+
.review-modal .review-group-title { min-height:48px;padding:0 16px;font-size:12px; }
|
|
1328
|
+
.review-modal .review-group-title strong { font-size:16px;font-weight:600; }
|
|
1329
|
+
.review-modal .review-group-title .included-count,.review-modal .review-group-title .group-total { font-size:12px; }
|
|
1330
|
+
.review-modal .review-card { min-height:64px;gap:12px;padding:12px 16px; }
|
|
1331
|
+
.review-modal .review-card input[type="checkbox"] { width:16px;height:16px;margin-top:8px; }
|
|
1332
|
+
.review-modal .review-card-line { min-height:36px;gap:12px; }
|
|
1333
|
+
.review-modal .review-card-line>strong { font-size:16px;font-weight:550; }
|
|
1334
|
+
.review-modal .confidence-pill { padding:4px 8px;font-size:12px; }
|
|
1335
|
+
.review-modal .review-before { padding:0 8px;font-size:12px; }
|
|
1336
|
+
.review-modal .review-after { height:36px;padding:0 12px;font-size:12px; }
|
|
1337
|
+
.review-modal .review-more>summary { width:32px;height:32px;font-size:12px; }
|
|
1338
|
+
.review-modal .review-more .review-card-tools { top:36px; }
|
|
1339
|
+
.review-modal .review-more .review-card-tools button { height:32px;padding:0 8px;font-size:12px; }
|
|
1340
|
+
.review-modal .review-details summary { min-height:32px;font-size:12px; }
|
|
1341
|
+
.review-modal .review-source { font-size:12px;line-height:1.5; }
|
|
1342
|
+
.review-modal .impact-list,.review-modal .impact-list li { font-size:12px;line-height:1.5; }
|
|
1343
|
+
.review-modal .review-actions button { min-height:44px;font-size:12px;font-weight:550; }
|
|
1344
|
+
.review-modal .run-state strong { font-size:16px; }
|
|
1345
|
+
.review-modal .run-summary p { font-size:12px; }
|
|
1346
|
+
.review-modal .run-step strong,.review-modal .result-row { font-size:12px; }
|
|
1347
|
+
.review-modal .run-step p,.review-modal .run-files { font-size:12px; }
|
|
1348
|
+
.interface-theme-menu { position:fixed;z-index:2147483647;width:164px;padding:4px;border:1px solid var(--fdc-line);border-radius:8px;color:var(--fdc-ink);background:var(--fdc-elevated);box-shadow:0 16px 36px rgb(0 0 0 / 34%);pointer-events:auto; }
|
|
1349
|
+
.interface-theme-menu[hidden] { display:none; }
|
|
1350
|
+
.interface-theme-menu button { width:100%;height:32px;display:grid;grid-template-columns:minmax(0,1fr) 16px;align-items:center;padding:0 8px;border:0;border-radius:4px;color:var(--fdc-muted);background:transparent;font-size:12px;text-align:left;cursor:pointer; }
|
|
1351
|
+
.interface-theme-menu button:hover { color:var(--fdc-ink);background:var(--fdc-subtle); }
|
|
1352
|
+
.interface-theme-menu button[aria-checked="true"] { color:var(--fdc-ink);background:var(--fdc-subtle); }
|
|
1353
|
+
.interface-theme-menu button svg { width:12px;height:12px;color:var(--fdc-signal); }
|
|
1354
|
+
.interface-theme-menu button:not([aria-checked="true"]) svg { visibility:hidden; }
|
|
1355
|
+
:host([data-interface-theme="light"]) { color-scheme:light;--fdc-ink:#141416;--fdc-paper:#f0f1f3;--fdc-surface:#ffffff;--fdc-subtle:#f0f1f3;--fdc-elevated:#f7f7f8;--fdc-line:#dedfe2;--fdc-signal:#2563eb;--fdc-signal-soft:#e8f0ff;--fdc-component:#7259d6;--fdc-component-soft:#f0edff;--fdc-muted:#666870; }
|
|
1356
|
+
:host([data-interface-theme="light"]) .panel,:host([data-interface-theme="light"]) .layers-panel,:host([data-interface-theme="light"]) .health-panel,:host([data-interface-theme="light"]) .library-panel,:host([data-interface-theme="light"]) .tool-shelf,:host([data-interface-theme="light"]) .onboarding-card,:host([data-interface-theme="light"]) .compare-bar,:host([data-interface-theme="light"]) .command-palette,:host([data-interface-theme="light"]) .status-popover,:host([data-interface-theme="light"]) .canvas-variant,:host([data-interface-theme="light"]) .workspace-bar,:host([data-interface-theme="light"]) .change-tray,:host([data-interface-theme="light"]) .review-modal { color:var(--fdc-ink);background:rgb(255 255 255 / 97%);border-color:var(--fdc-line);box-shadow:0 1px 4px rgb(0 0 0 / 8%),0 16px 36px rgb(0 0 0 / 14%); }
|
|
1357
|
+
:host([data-interface-theme="light"]) .top,:host([data-interface-theme="light"]) .top-actions,:host([data-interface-theme="light"]) .selection,:host([data-interface-theme="light"]) .scope,:host([data-interface-theme="light"]) .controls,:host([data-interface-theme="light"]) .property-section,:host([data-interface-theme="light"]) .change-dock,:host([data-interface-theme="light"]) .footer,:host([data-interface-theme="light"]) .review-view,:host([data-interface-theme="light"]) .review-head,:host([data-interface-theme="light"]) .review-group,:host([data-interface-theme="light"]) .review-group-title,:host([data-interface-theme="light"]) .review-actions,:host([data-interface-theme="light"]) .layers-head,:host([data-interface-theme="light"]) .layer-tree,:host([data-interface-theme="light"]) .health-head,:host([data-interface-theme="light"]) .health-list,:host([data-interface-theme="light"]) .health-footer,:host([data-interface-theme="light"]) .library-head,:host([data-interface-theme="light"]) .library-actions,:host([data-interface-theme="light"]) .library-body,:host([data-interface-theme="light"]) .memory-card,:host([data-interface-theme="light"]) .context-tools,:host([data-interface-theme="light"]) .native-panel,:host([data-interface-theme="light"]) .section-head,:host([data-interface-theme="light"]) .onboarding-step { color:var(--fdc-ink);background:var(--fdc-surface);border-color:var(--fdc-line); }
|
|
1358
|
+
:host([data-interface-theme="light"]) .inspector-heading,:host([data-interface-theme="light"]) .review-toolbar,:host([data-interface-theme="light"]) .review-overview,:host([data-interface-theme="light"]) .review-modal .review-actions { color:var(--fdc-ink);background:rgb(255 255 255 / 97%);border-color:var(--fdc-line); }
|
|
1359
|
+
:host([data-interface-theme="light"]) .review-modal .review-group-title { background:rgb(247 247 248 / 98%); }
|
|
1360
|
+
:host([data-interface-theme="light"]) .property-label,:host([data-interface-theme="light"]) .section-head,:host([data-interface-theme="light"]) .color-value,:host([data-interface-theme="light"]) .layer-row,:host([data-interface-theme="light"]) .command-item,:host([data-interface-theme="light"]) .library-section-head { color:#303136; }
|
|
1361
|
+
:host([data-interface-theme="light"]) .selection-path button,:host([data-interface-theme="light"]) .control-field,:host([data-interface-theme="light"]) .native-search,:host([data-interface-theme="light"]) .layers-search input,:host([data-interface-theme="light"]) .review-after,:host([data-interface-theme="light"]) .review-toolbar button,:host([data-interface-theme="light"]) .review-card-tools button,:host([data-interface-theme="light"]) .review-actions button,:host([data-interface-theme="light"]) .change-dock button,:host([data-interface-theme="light"]) .footer button,:host([data-interface-theme="light"]) .status-popover button,:host([data-interface-theme="light"]) .health-actions button,:host([data-interface-theme="light"]) .health-footer button,:host([data-interface-theme="light"]) .context-tools button,:host([data-interface-theme="light"]) .native-chip,:host([data-interface-theme="light"]) .variant-button,:host([data-interface-theme="light"]) .library-actions button,:host([data-interface-theme="light"]) .memory-card-actions button,:host([data-interface-theme="light"]) .empty-state-actions button,:host([data-interface-theme="light"]) .onboarding-actions button { color:var(--fdc-ink);background:var(--fdc-elevated);border-color:var(--fdc-line); }
|
|
1362
|
+
:host([data-interface-theme="light"]) .scope select,:host([data-interface-theme="light"]) .property-control input,:host([data-interface-theme="light"]) .property-control select,:host([data-interface-theme="light"]) .compact-control input,:host([data-interface-theme="light"]) .compact-control select,:host([data-interface-theme="light"]) .native-search,:host([data-interface-theme="light"]) .command-palette input { color:var(--fdc-ink);background:var(--fdc-surface);border-color:var(--fdc-line); }
|
|
1363
|
+
:host([data-interface-theme="light"]) .scope select option,:host([data-interface-theme="light"]) .property-control select option,:host([data-interface-theme="light"]) .compact-control select option { color:var(--fdc-ink);background:var(--fdc-surface); }
|
|
1364
|
+
:host([data-interface-theme="light"]) .layers-search,:host([data-interface-theme="light"]) .layers-switch,:host([data-interface-theme="light"]) .review-summary { background:var(--fdc-paper);border-color:var(--fdc-line); }
|
|
1365
|
+
:host([data-interface-theme="light"]) .layers-switch button:hover,:host([data-interface-theme="light"]) .layers-switch button.active,:host([data-interface-theme="light"]) .health-filters button:hover,:host([data-interface-theme="light"]) .health-filters button.active { color:var(--fdc-ink);background:var(--fdc-surface); }
|
|
1366
|
+
:host([data-interface-theme="light"]) .component-card,:host([data-interface-theme="light"]) .health-card { color:var(--fdc-ink);background:var(--fdc-surface);border-color:var(--fdc-line); }
|
|
1367
|
+
:host([data-interface-theme="light"]) .layer-row.selected { color:#1458b8;background:transparent; }
|
|
1368
|
+
:host([data-interface-theme="light"]) .layer-row .layer-meta { color:#65676e;background:var(--fdc-subtle); }
|
|
1369
|
+
:host([data-interface-theme="light"]) .layer-row.selected .layer-meta { color:#1458b8;background:#dceaff; }
|
|
1370
|
+
:host([data-interface-theme="light"]) .tool-select.active,:host([data-interface-theme="light"]) .tab.active { color:var(--fdc-ink);background:#e4e5e8; }
|
|
1371
|
+
:host([data-interface-theme="light"]) .tool-select.inspect.active { color:white;background:var(--fdc-signal); }
|
|
1372
|
+
:host([data-interface-theme="light"]) .change-dock .dock-review,:host([data-interface-theme="light"]) .review-actions .apply,:host([data-interface-theme="light"]) .health-footer .health-rescan { color:#f0f1f3;background:#141416;border-color:#141416; }
|
|
1373
|
+
:host([data-interface-theme="light"]) .session-status,:host([data-interface-theme="light"]) .session-status.saved { color:#236c59;background:#eef8f4; }
|
|
1374
|
+
:host([data-interface-theme="light"]) .interface-theme-menu { color:var(--fdc-ink);background:var(--fdc-surface);border-color:var(--fdc-line);box-shadow:0 16px 36px rgb(0 0 0 / 16%); }
|
|
1375
|
+
.control-field { height:36px;border-radius:8px; }
|
|
1376
|
+
.compact-control .field-prefix,.compact-control .field-prefix.wide { width:36px;min-width:36px;height:100%;display:grid;place-items:center;padding:0;border-right:1px solid var(--fdc-line);background:rgb(255 255 255 / 2%);color:var(--fdc-muted);font:500 12px/1 var(--fdc-font); }
|
|
1377
|
+
.field-prefix svg { width:12px;height:12px; }
|
|
1378
|
+
.property-control input,.property-control select,.compact-control input,.compact-control select { height:36px;font-size:12px;font-weight:400; }
|
|
1379
|
+
.control-field .unit-select { width:44px;padding-right:24px!important;background-position:right 8px center!important; }
|
|
1380
|
+
.section-grid { gap:8px;padding:0 16px 16px; }
|
|
1381
|
+
@media (max-width:680px){.panel,.layers-panel{width:auto}.panel-resizer{display:none}.change-tray{right:8px;bottom:8px;left:auto;width:calc(100vw - 16px)}.review-takeover{padding:8px}.review-modal{width:calc(100vw - 16px);height:calc(100vh - 16px);border-radius:12px}.review-modal .review-actions{grid-template-columns:84px 1fr}.panel.change-summary-visible .inspector-scroll{padding-bottom:60px}}
|
|
1382
|
+
@media (prefers-reduced-motion:reduce){*{transition-duration:.01ms!important}.layer-row.entering,.layer-branch{animation:none!important}}
|
|
1196
1383
|
`;
|
|
1197
1384
|
function numberFrom(value) {
|
|
1198
1385
|
const parsed = Number.parseFloat(value);
|
|
1199
1386
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
1200
1387
|
}
|
|
1201
|
-
function pxControl(element, computed, category, property, label, max = 600) {
|
|
1388
|
+
function pxControl(element, computed, category, property, label, max = 600, min = 0) {
|
|
1202
1389
|
return {
|
|
1203
1390
|
category,
|
|
1204
1391
|
property: String(property),
|
|
@@ -1206,7 +1393,7 @@ function pxControl(element, computed, category, property, label, max = 600) {
|
|
|
1206
1393
|
kind: "number",
|
|
1207
1394
|
value: numberFrom(String(computed[property])),
|
|
1208
1395
|
unit: "px",
|
|
1209
|
-
min
|
|
1396
|
+
min,
|
|
1210
1397
|
max,
|
|
1211
1398
|
step: 1,
|
|
1212
1399
|
read: () => numberFrom(getComputedStyle(element)[property]),
|
|
@@ -1248,6 +1435,45 @@ function numberStyleControl(element, computed, category, property, label, min, m
|
|
|
1248
1435
|
)
|
|
1249
1436
|
};
|
|
1250
1437
|
}
|
|
1438
|
+
function degreeControl(element, computed) {
|
|
1439
|
+
const read = () => numberFrom(getComputedStyle(element).rotate || "0");
|
|
1440
|
+
return {
|
|
1441
|
+
category: "layout",
|
|
1442
|
+
property: "rotate",
|
|
1443
|
+
label: "Rotation",
|
|
1444
|
+
kind: "number",
|
|
1445
|
+
value: numberFrom(computed.rotate || "0"),
|
|
1446
|
+
unit: "deg",
|
|
1447
|
+
min: -360,
|
|
1448
|
+
max: 360,
|
|
1449
|
+
step: 1,
|
|
1450
|
+
read,
|
|
1451
|
+
apply: (value) => element.style.setProperty("rotate", `${value}deg`)
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1454
|
+
function scaleAxisControl(element, computed, axis) {
|
|
1455
|
+
const readScale = () => {
|
|
1456
|
+
const raw = getComputedStyle(element).scale;
|
|
1457
|
+
if (!raw || raw === "none") return [1, 1];
|
|
1458
|
+
const values = raw.split(/\s+/).map(numberFrom);
|
|
1459
|
+
return [values[0] || 1, values[1] ?? values[0] ?? 1];
|
|
1460
|
+
};
|
|
1461
|
+
const initial = computed.scale && computed.scale !== "none" ? readScale() : [1, 1];
|
|
1462
|
+
return {
|
|
1463
|
+
category: "layout",
|
|
1464
|
+
property: axis === "x" ? "scaleX" : "scaleY",
|
|
1465
|
+
label: axis === "x" ? "Flip horizontal" : "Flip vertical",
|
|
1466
|
+
kind: "select",
|
|
1467
|
+
options: ["1", "-1"],
|
|
1468
|
+
value: String(axis === "x" ? initial[0] : initial[1]),
|
|
1469
|
+
read: () => String(axis === "x" ? readScale()[0] : readScale()[1]),
|
|
1470
|
+
apply: (value) => {
|
|
1471
|
+
const current = readScale();
|
|
1472
|
+
current[axis === "x" ? 0 : 1] = Number(value);
|
|
1473
|
+
element.style.setProperty("scale", `${current[0]} ${current[1]}`);
|
|
1474
|
+
}
|
|
1475
|
+
};
|
|
1476
|
+
}
|
|
1251
1477
|
function sizingModeControl(element, computed, axis) {
|
|
1252
1478
|
const capitalized = axis === "width" ? "Width" : "Height";
|
|
1253
1479
|
const minProperty = axis === "width" ? "minWidth" : "minHeight";
|
|
@@ -1292,6 +1518,10 @@ function sizingModeControl(element, computed, axis) {
|
|
|
1292
1518
|
function controlsFor(element) {
|
|
1293
1519
|
const computed = getComputedStyle(element);
|
|
1294
1520
|
const controls = [
|
|
1521
|
+
...computed.position !== "static" ? [
|
|
1522
|
+
pxControl(element, computed, "layout", "left", "X position", 5e3, -5e3),
|
|
1523
|
+
pxControl(element, computed, "layout", "top", "Y position", 5e3, -5e3)
|
|
1524
|
+
] : [],
|
|
1295
1525
|
sizingModeControl(element, computed, "width"),
|
|
1296
1526
|
sizingModeControl(element, computed, "height"),
|
|
1297
1527
|
styleControl(element, computed, "layout", "minWidth", "Minimum width"),
|
|
@@ -1304,6 +1534,9 @@ function controlsFor(element) {
|
|
|
1304
1534
|
"auto",
|
|
1305
1535
|
"scroll"
|
|
1306
1536
|
]),
|
|
1537
|
+
degreeControl(element, computed),
|
|
1538
|
+
scaleAxisControl(element, computed, "x"),
|
|
1539
|
+
scaleAxisControl(element, computed, "y"),
|
|
1307
1540
|
pxControl(element, computed, "layout", "width", "Width", 1600),
|
|
1308
1541
|
pxControl(element, computed, "layout", "height", "Height", 1200),
|
|
1309
1542
|
styleControl(element, computed, "layout", "display", "Display", "select", [
|
|
@@ -1360,6 +1593,10 @@ function controlsFor(element) {
|
|
|
1360
1593
|
numberStyleControl(element, computed, "color", "opacity", "Opacity", 0, 1, 0.05),
|
|
1361
1594
|
styleControl(element, computed, "color", "backgroundImage", "Gradient"),
|
|
1362
1595
|
pxControl(element, computed, "effects", "borderRadius", "Corner radius", 200),
|
|
1596
|
+
pxControl(element, computed, "effects", "borderTopLeftRadius", "Top left radius", 200),
|
|
1597
|
+
pxControl(element, computed, "effects", "borderTopRightRadius", "Top right radius", 200),
|
|
1598
|
+
pxControl(element, computed, "effects", "borderBottomLeftRadius", "Bottom left radius", 200),
|
|
1599
|
+
pxControl(element, computed, "effects", "borderBottomRightRadius", "Bottom right radius", 200),
|
|
1363
1600
|
pxControl(element, computed, "effects", "borderWidth", "Border width", 32),
|
|
1364
1601
|
styleControl(element, computed, "effects", "borderColor", "Border color", "color"),
|
|
1365
1602
|
styleControl(element, computed, "effects", "boxShadow", "Shadow"),
|
|
@@ -1465,15 +1702,67 @@ function controlsFor(element) {
|
|
|
1465
1702
|
}
|
|
1466
1703
|
});
|
|
1467
1704
|
}
|
|
1468
|
-
|
|
1705
|
+
const textBearing = element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || Boolean(element.textContent?.trim());
|
|
1706
|
+
const editableText = element.childElementCount === 0 || ["BUTTON", "A", "LABEL", "SUMMARY"].includes(element.tagName);
|
|
1707
|
+
return controls.filter((control) => {
|
|
1708
|
+
if (control.category === "typography") return textBearing;
|
|
1709
|
+
if (control.property === "textContent") return textBearing && editableText;
|
|
1710
|
+
if (control.property === "color") return textBearing;
|
|
1711
|
+
return true;
|
|
1712
|
+
});
|
|
1469
1713
|
}
|
|
1470
1714
|
function colorForInput(value) {
|
|
1471
1715
|
const canvas = document.createElement("canvas");
|
|
1472
1716
|
canvas.width = canvas.height = 1;
|
|
1473
1717
|
const context = canvas.getContext("2d");
|
|
1474
1718
|
if (!context) return "#000000";
|
|
1719
|
+
context.fillStyle = "#000000";
|
|
1475
1720
|
context.fillStyle = value;
|
|
1476
|
-
|
|
1721
|
+
const normalized = context.fillStyle;
|
|
1722
|
+
if (normalized.startsWith("#")) return normalized;
|
|
1723
|
+
const channels = normalized.match(/[\d.]+/g)?.slice(0, 3).map(Number);
|
|
1724
|
+
if (!channels || channels.length < 3) return "#000000";
|
|
1725
|
+
return `#${channels.map((channel2) => Math.round(channel2).toString(16).padStart(2, "0")).join("")}`;
|
|
1726
|
+
}
|
|
1727
|
+
function hexChannels(value) {
|
|
1728
|
+
const hex = colorForInput(value).slice(1);
|
|
1729
|
+
return [
|
|
1730
|
+
Number.parseInt(hex.slice(0, 2), 16),
|
|
1731
|
+
Number.parseInt(hex.slice(2, 4), 16),
|
|
1732
|
+
Number.parseInt(hex.slice(4, 6), 16)
|
|
1733
|
+
];
|
|
1734
|
+
}
|
|
1735
|
+
function colorAlpha(value) {
|
|
1736
|
+
const slash = value.match(/\/\s*([\d.]+)%?\s*\)/);
|
|
1737
|
+
if (slash) return value.includes("%") ? Number(slash[1]) / 100 : Number(slash[1]);
|
|
1738
|
+
const comma = value.match(/rgba?\([^)]*,\s*([\d.]+)\s*\)$/);
|
|
1739
|
+
return comma ? Number(comma[1]) : isTransparentColor(value) ? 0 : 1;
|
|
1740
|
+
}
|
|
1741
|
+
function rgbToHsv(red, green, blue) {
|
|
1742
|
+
const r = red / 255;
|
|
1743
|
+
const g = green / 255;
|
|
1744
|
+
const b = blue / 255;
|
|
1745
|
+
const maximum = Math.max(r, g, b);
|
|
1746
|
+
const minimum = Math.min(r, g, b);
|
|
1747
|
+
const delta = maximum - minimum;
|
|
1748
|
+
let hue = 0;
|
|
1749
|
+
if (delta) {
|
|
1750
|
+
if (maximum === r) hue = 60 * ((g - b) / delta % 6);
|
|
1751
|
+
else if (maximum === g) hue = 60 * ((b - r) / delta + 2);
|
|
1752
|
+
else hue = 60 * ((r - g) / delta + 4);
|
|
1753
|
+
}
|
|
1754
|
+
if (hue < 0) hue += 360;
|
|
1755
|
+
return [hue, maximum ? delta / maximum : 0, maximum];
|
|
1756
|
+
}
|
|
1757
|
+
function hsvToRgb(hue, saturation, value) {
|
|
1758
|
+
const chroma = value * saturation;
|
|
1759
|
+
const x = chroma * (1 - Math.abs(hue / 60 % 2 - 1));
|
|
1760
|
+
const match = value - chroma;
|
|
1761
|
+
const [r, g, b] = hue < 60 ? [chroma, x, 0] : hue < 120 ? [x, chroma, 0] : hue < 180 ? [0, chroma, x] : hue < 240 ? [0, x, chroma] : hue < 300 ? [x, 0, chroma] : [chroma, 0, x];
|
|
1762
|
+
return [r, g, b].map((channel2) => Math.round((channel2 + match) * 255));
|
|
1763
|
+
}
|
|
1764
|
+
function rgbHex(red, green, blue) {
|
|
1765
|
+
return `#${[red, green, blue].map((channel2) => Math.max(0, Math.min(255, channel2)).toString(16).padStart(2, "0")).join("")}`;
|
|
1477
1766
|
}
|
|
1478
1767
|
function escapeHtml(value) {
|
|
1479
1768
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
@@ -1497,7 +1786,7 @@ function renderControlInput(control, index) {
|
|
|
1497
1786
|
const rawValue = String(control.value);
|
|
1498
1787
|
const value2 = colorForInput(rawValue);
|
|
1499
1788
|
const transparent = isTransparentColor(rawValue);
|
|
1500
|
-
return `<span class="color-swatch ${transparent ? "transparent" : ""}" style="--swatch-color:${value2}"></span><span class="color-value">${transparent ? "Transparent" : escapeHtml(value2.toUpperCase())}</span
|
|
1789
|
+
return `<button type="button" class="color-picker-trigger" id="${id}" data-color-control="${index}" aria-label="Edit ${escapeHtml(control.label)}"><span class="color-swatch ${transparent ? "transparent" : ""}" style="--swatch-color:${value2}"></span><span class="color-value">${transparent ? "Transparent" : escapeHtml(value2.toUpperCase())}</span></button>`;
|
|
1501
1790
|
}
|
|
1502
1791
|
const type = control.kind === "number" ? "number" : "text";
|
|
1503
1792
|
const value = displayValue(control);
|
|
@@ -1513,7 +1802,9 @@ function renderPropertyControl(control, index, prefix) {
|
|
|
1513
1802
|
const reset = `<button type="button" class="control-reset" data-reset-control="${index}" title="Reset ${escapeHtml(control.label)}" aria-label="Reset ${escapeHtml(control.label)}"><i data-foundry-icon="rotate-ccw"></i></button>`;
|
|
1514
1803
|
const scrubAttributes = control.kind === "number" ? ` data-scrub-for="${index}" title="Drag left or right to adjust ${escapeHtml(control.label)}. Hold Shift for larger steps or Option for finer steps."` : "";
|
|
1515
1804
|
if (prefix) {
|
|
1516
|
-
|
|
1805
|
+
const prefixIcon = prefix === "Ratio" ? "maximize-2" : prefix === "Clip" ? "box" : void 0;
|
|
1806
|
+
const prefixContent = prefixIcon ? `<i data-foundry-icon="${prefixIcon}"></i>` : escapeHtml(prefix);
|
|
1807
|
+
return `<label class="compact-control"><span class="sr-only">${escapeHtml(control.label)}</span><span class="control-field"><span class="field-prefix" aria-hidden="true" title="${escapeHtml(control.label)}"${scrubAttributes}>${prefixContent}</span>${field}${reset}</span></label>`;
|
|
1517
1808
|
}
|
|
1518
1809
|
return `<label class="property-control"><span class="property-label"${scrubAttributes}>${escapeHtml(control.label)}</span><span class="control-field">${field}${reset}</span></label>`;
|
|
1519
1810
|
}
|
|
@@ -1535,7 +1826,14 @@ function installFoundryInspector(options = {}) {
|
|
|
1535
1826
|
const sessionId = options.sessionId ?? query.get("__foundry_session") ?? "";
|
|
1536
1827
|
const token = options.token ?? query.get("__foundry_token") ?? "";
|
|
1537
1828
|
const host = document.createElement("div");
|
|
1829
|
+
const interfaceThemeKey = "__foundry_interface_theme";
|
|
1830
|
+
const systemDarkTheme = window.matchMedia("(prefers-color-scheme: dark)");
|
|
1831
|
+
const storedInterfaceTheme = localStorage.getItem(interfaceThemeKey);
|
|
1832
|
+
let interfaceThemePreference = storedInterfaceTheme === "light" || storedInterfaceTheme === "dark" ? storedInterfaceTheme : "system";
|
|
1833
|
+
const resolvedInterfaceTheme = () => resolveInterfaceTheme(interfaceThemePreference, systemDarkTheme.matches);
|
|
1538
1834
|
host.dataset.foundryOverlay = "true";
|
|
1835
|
+
host.dataset.interfaceTheme = resolvedInterfaceTheme();
|
|
1836
|
+
host.dataset.interfaceThemePreference = interfaceThemePreference;
|
|
1539
1837
|
const shadow = host.attachShadow({ mode: "open" });
|
|
1540
1838
|
shadow.innerHTML = `<style>${PANEL_CSS}</style>
|
|
1541
1839
|
<div class="outline" hidden><span class="cross"></span><span class="measure"></span></div>
|
|
@@ -1551,13 +1849,15 @@ function installFoundryInspector(options = {}) {
|
|
|
1551
1849
|
<button class="icon-button open-workbench" title="State workbench" aria-label="Open state workbench" aria-pressed="false"><i data-foundry-icon="play"></i></button>
|
|
1552
1850
|
<button class="icon-button open-library" title="Design memory" aria-label="Open design memory" aria-pressed="false"><i data-foundry-icon="bookmark"></i></button>
|
|
1553
1851
|
<button class="icon-button open-commands" title="Commands" aria-label="Open commands"><i data-foundry-icon="command"></i></button>
|
|
1852
|
+
<button class="icon-button interface-theme-trigger" title="Interface theme: System" aria-label="Interface theme: System" aria-haspopup="menu" aria-expanded="false"><i data-foundry-icon="contrast"></i></button>
|
|
1554
1853
|
</nav>
|
|
1555
1854
|
<span class="workspace-divider"></span>
|
|
1556
1855
|
<button class="icon-button close" title="Exit Foundry" aria-label="Exit Foundry"><i data-foundry-icon="x"></i></button>
|
|
1557
1856
|
</header>
|
|
1857
|
+
<div class="interface-theme-menu" role="menu" aria-label="Interface theme" hidden><button role="menuitemradio" aria-checked="true" data-interface-theme-choice="system"><span>System</span><i data-foundry-icon="check"></i></button><button role="menuitemradio" aria-checked="false" data-interface-theme-choice="light"><span>Light</span><i data-foundry-icon="check"></i></button><button role="menuitemradio" aria-checked="false" data-interface-theme-choice="dark"><span>Dark</span><i data-foundry-icon="check"></i></button></div>
|
|
1558
1858
|
<aside class="layers-panel" aria-label="Foundry layers" hidden>
|
|
1559
1859
|
<div class="layers-head"><i data-foundry-icon="layers-3"></i><strong>Layers</strong><span data-layer-count></span><button class="icon-button close-layers" aria-label="Close layers"><i data-foundry-icon="x"></i></button></div>
|
|
1560
|
-
<div class="layers-search"><input type="search" aria-label="Search layers" placeholder="Search layers" /></div><div class="layer-tree"></div>
|
|
1860
|
+
<div class="layers-search"><input type="search" aria-label="Search layers" placeholder="Search layers" /></div><div class="layer-tree" role="tree" aria-label="Canvas layers"></div>
|
|
1561
1861
|
</aside>
|
|
1562
1862
|
<aside class="health-panel utility-panel" data-utility="health" aria-label="Design health" hidden>
|
|
1563
1863
|
<div class="health-head utility-handle"><i data-foundry-icon="activity"></i><strong>Design health</strong><button class="icon-button close-health" aria-label="Close design health"><i data-foundry-icon="x"></i></button></div>
|
|
@@ -1572,8 +1872,10 @@ function installFoundryInspector(options = {}) {
|
|
|
1572
1872
|
</div>
|
|
1573
1873
|
</aside>
|
|
1574
1874
|
<section class="change-dock change-tray" aria-label="Foundry changes" hidden>
|
|
1575
|
-
<div class="change-tray-summary"><div class="change-dock-copy"><strong data-dock-count>No changes</strong><span data-dock-last>Edits will appear here</span></div><
|
|
1576
|
-
|
|
1875
|
+
<div class="change-tray-summary"><div class="change-dock-copy"><strong data-dock-count>No changes</strong><span data-dock-last>Edits will appear here</span></div><button class="tray-compare" title="Compare changes" aria-label="Compare changes" disabled><i data-foundry-icon="contrast"></i></button><button class="dock-review">Review</button></div>
|
|
1876
|
+
</section>
|
|
1877
|
+
<section class="review-takeover" role="dialog" aria-modal="true" aria-label="Review and apply changes" hidden>
|
|
1878
|
+
<div class="review-modal"><div class="review-view"><div class="review-head"><button class="review-back" aria-label="Back to canvas"><i data-foundry-icon="arrow-left"></i></button><strong>Review and apply</strong><span class="review-count"></span></div><div class="review-body"></div><div class="review-actions"><button class="review-cancel">Back</button><button class="apply">Apply with agent</button></div></div></div>
|
|
1577
1879
|
</section>
|
|
1578
1880
|
<div class="tool-shelf" role="toolbar" aria-label="Canvas tools"><button class="tool-select inspect" data-tooltip="Select" title="Select: click any element" aria-label="Select mode" aria-pressed="true"><i data-foundry-icon="mouse-pointer-2"></i></button><button class="tool-select interact" data-tooltip="Interact" title="Interact with the app" aria-label="Interact mode" aria-pressed="false"><i data-foundry-icon="interact"></i></button></div>
|
|
1579
1881
|
<div class="toast"></div>`;
|
|
@@ -1611,8 +1913,15 @@ function installFoundryInspector(options = {}) {
|
|
|
1611
1913
|
commandPalette.className = "command-palette";
|
|
1612
1914
|
commandPalette.hidden = true;
|
|
1613
1915
|
commandPalette.setAttribute("aria-label", "Foundry commands");
|
|
1614
|
-
commandPalette.
|
|
1916
|
+
commandPalette.setAttribute("role", "dialog");
|
|
1917
|
+
commandPalette.innerHTML = `<div class="command-search"><input type="search" aria-label="Search commands" placeholder="Type a command"/><button class="icon-button close-commands" aria-label="Close commands" title="Close commands"><i data-foundry-icon="x"></i></button></div><div class="command-list"></div>`;
|
|
1615
1918
|
shadow.append(commandPalette);
|
|
1919
|
+
const colorPopover = document.createElement("section");
|
|
1920
|
+
colorPopover.className = "color-popover";
|
|
1921
|
+
colorPopover.hidden = true;
|
|
1922
|
+
colorPopover.setAttribute("role", "dialog");
|
|
1923
|
+
colorPopover.setAttribute("aria-label", "Color editor");
|
|
1924
|
+
shadow.append(colorPopover);
|
|
1616
1925
|
const libraryPanel = document.createElement("aside");
|
|
1617
1926
|
libraryPanel.className = "library-panel utility-panel";
|
|
1618
1927
|
libraryPanel.dataset.utility = "memory";
|
|
@@ -1673,6 +1982,10 @@ function installFoundryInspector(options = {}) {
|
|
|
1673
1982
|
const inspectButton = shadow.querySelector(".inspect");
|
|
1674
1983
|
const interactButton = shadow.querySelector(".interact");
|
|
1675
1984
|
const workspaceBar = shadow.querySelector(".workspace-bar");
|
|
1985
|
+
const interfaceThemeTrigger = shadow.querySelector(
|
|
1986
|
+
".interface-theme-trigger"
|
|
1987
|
+
);
|
|
1988
|
+
const interfaceThemeMenu = shadow.querySelector(".interface-theme-menu");
|
|
1676
1989
|
const sessionStatus = shadow.querySelector(".session-status");
|
|
1677
1990
|
const statusPopover = shadow.querySelector(".status-popover");
|
|
1678
1991
|
const changeDock = shadow.querySelector(".change-dock");
|
|
@@ -1709,21 +2022,66 @@ function installFoundryInspector(options = {}) {
|
|
|
1709
2022
|
const healthSummary = shadow.querySelector(".health-summary");
|
|
1710
2023
|
const healthFilters = shadow.querySelector(".health-filters");
|
|
1711
2024
|
const healthList = shadow.querySelector(".health-list");
|
|
1712
|
-
const changeCount = shadow.querySelector(".change-count");
|
|
1713
2025
|
const scope = shadow.querySelector("[data-scope]");
|
|
1714
2026
|
const breakpoint = shadow.querySelector("[data-breakpoint]");
|
|
1715
2027
|
const theme = shadow.querySelector("[data-theme]");
|
|
1716
2028
|
const state = shadow.querySelector("[data-state]");
|
|
1717
|
-
const
|
|
2029
|
+
const reviewTakeover = shadow.querySelector(".review-takeover");
|
|
2030
|
+
const reviewModal = shadow.querySelector(".review-modal");
|
|
1718
2031
|
const reviewBody = shadow.querySelector(".review-body");
|
|
1719
2032
|
const reviewCount = shadow.querySelector(".review-count");
|
|
1720
2033
|
const applyButton = shadow.querySelector(".review-actions .apply");
|
|
1721
2034
|
const reviewCancel = shadow.querySelector(".review-cancel");
|
|
2035
|
+
function applyInterfaceTheme(preference, persist = true) {
|
|
2036
|
+
interfaceThemePreference = preference;
|
|
2037
|
+
const resolved = resolvedInterfaceTheme();
|
|
2038
|
+
host.dataset.interfaceTheme = resolved;
|
|
2039
|
+
host.dataset.interfaceThemePreference = preference;
|
|
2040
|
+
const label = preference[0].toUpperCase() + preference.slice(1);
|
|
2041
|
+
interfaceThemeTrigger.title = `Interface theme: ${label}`;
|
|
2042
|
+
interfaceThemeTrigger.setAttribute("aria-label", `Interface theme: ${label}`);
|
|
2043
|
+
interfaceThemeMenu.querySelectorAll("[data-interface-theme-choice]").forEach(
|
|
2044
|
+
(button) => button.setAttribute(
|
|
2045
|
+
"aria-checked",
|
|
2046
|
+
String(button.dataset.interfaceThemeChoice === preference)
|
|
2047
|
+
)
|
|
2048
|
+
);
|
|
2049
|
+
if (persist) localStorage.setItem(interfaceThemeKey, preference);
|
|
2050
|
+
}
|
|
2051
|
+
function closeInterfaceThemeMenu(restoreFocus = false) {
|
|
2052
|
+
interfaceThemeMenu.hidden = true;
|
|
2053
|
+
interfaceThemeTrigger.setAttribute("aria-expanded", "false");
|
|
2054
|
+
if (restoreFocus) interfaceThemeTrigger.focus();
|
|
2055
|
+
}
|
|
2056
|
+
function openInterfaceThemeMenu() {
|
|
2057
|
+
const rect = interfaceThemeTrigger.getBoundingClientRect();
|
|
2058
|
+
const menuWidth = 164;
|
|
2059
|
+
interfaceThemeMenu.style.top = `${Math.round(rect.bottom + 8)}px`;
|
|
2060
|
+
interfaceThemeMenu.style.left = `${Math.round(Math.min(window.innerWidth - menuWidth - 8, Math.max(8, rect.right - menuWidth)))}px`;
|
|
2061
|
+
interfaceThemeMenu.hidden = false;
|
|
2062
|
+
interfaceThemeTrigger.setAttribute("aria-expanded", "true");
|
|
2063
|
+
interfaceThemeMenu.querySelector('[aria-checked="true"]')?.focus();
|
|
2064
|
+
}
|
|
2065
|
+
applyInterfaceTheme(interfaceThemePreference, false);
|
|
2066
|
+
const handleSystemThemeChange = () => {
|
|
2067
|
+
if (interfaceThemePreference === "system") applyInterfaceTheme("system", false);
|
|
2068
|
+
};
|
|
2069
|
+
systemDarkTheme.addEventListener("change", handleSystemThemeChange);
|
|
2070
|
+
const handleInterfaceThemeDismiss = (event) => {
|
|
2071
|
+
if (interfaceThemeMenu.hidden) return;
|
|
2072
|
+
const path = event.composedPath();
|
|
2073
|
+
if (!path.includes(interfaceThemeMenu) && !path.includes(interfaceThemeTrigger)) {
|
|
2074
|
+
closeInterfaceThemeMenu();
|
|
2075
|
+
}
|
|
2076
|
+
};
|
|
2077
|
+
document.addEventListener("pointerdown", handleInterfaceThemeDismiss, true);
|
|
1722
2078
|
let workspaceState = { ...DEFAULT_WORKSPACE_STATE };
|
|
1723
2079
|
let selected = null;
|
|
1724
2080
|
let selectedElements = [];
|
|
1725
2081
|
let layerEntries = [];
|
|
1726
2082
|
const collapsedLayers = /* @__PURE__ */ new WeakSet();
|
|
2083
|
+
let visibleLayerElements = /* @__PURE__ */ new Set();
|
|
2084
|
+
let layerGlideTop = null;
|
|
1727
2085
|
const layerViewPreferenceKey = "__foundry_layer_view";
|
|
1728
2086
|
let layerView = sessionStorage.getItem(layerViewPreferenceKey) === "components" ? "components" : "layers";
|
|
1729
2087
|
let clickCycle = { x: -1, y: -1, at: 0, index: -1, signature: "" };
|
|
@@ -1731,6 +2089,7 @@ function installFoundryInspector(options = {}) {
|
|
|
1731
2089
|
let selectedControls = [];
|
|
1732
2090
|
let resizeObserver;
|
|
1733
2091
|
let activeReviewPayload = null;
|
|
2092
|
+
let activeAgentPresence = { connected: false, presence: null };
|
|
1734
2093
|
const reviewDraftKey = `__foundry_review_draft:${sessionId || "local"}`;
|
|
1735
2094
|
let reviewDraft = (() => {
|
|
1736
2095
|
try {
|
|
@@ -1740,18 +2099,34 @@ function installFoundryInspector(options = {}) {
|
|
|
1740
2099
|
}
|
|
1741
2100
|
})();
|
|
1742
2101
|
let reviewShowRejected = false;
|
|
2102
|
+
const collapsedReviewGroups = /* @__PURE__ */ new Set();
|
|
1743
2103
|
let reviewPoll;
|
|
1744
2104
|
let lastReviewTrigger = null;
|
|
2105
|
+
let reviewScrollTop = 0;
|
|
2106
|
+
let reviewSuspended = false;
|
|
2107
|
+
let suspendedPreviewRestore;
|
|
2108
|
+
const dockReviewButton = shadow.querySelector(".dock-review");
|
|
2109
|
+
const dockWidthKey = "__foundry_dock_width";
|
|
2110
|
+
const legacyDockWidthKey = "__foundry_panel_width";
|
|
2111
|
+
const storedDockWidth = Number(
|
|
2112
|
+
localStorage.getItem(dockWidthKey) ?? localStorage.getItem(legacyDockWidthKey)
|
|
2113
|
+
);
|
|
2114
|
+
let dockWidth = Number.isFinite(storedDockWidth) ? Math.max(340, Math.min(520, storedDockWidth)) : 384;
|
|
2115
|
+
host.style.setProperty("--fdc-dock-width", `${Math.round(dockWidth)}px`);
|
|
1745
2116
|
let designGraph = null;
|
|
1746
2117
|
const previewHistory = [];
|
|
1747
2118
|
let historyCursor = 0;
|
|
1748
2119
|
let paddingLinked = true;
|
|
2120
|
+
let radiusLinked = true;
|
|
1749
2121
|
let tokenOnly = false;
|
|
1750
2122
|
let activeControlProperty = "";
|
|
2123
|
+
const recentColors = /* @__PURE__ */ new Set();
|
|
2124
|
+
let activeColor;
|
|
1751
2125
|
let lastRecordedSummary = "";
|
|
1752
2126
|
let statusResetTimer;
|
|
1753
2127
|
let hydratedOnce = false;
|
|
1754
2128
|
const collapsedSections = /* @__PURE__ */ new Set();
|
|
2129
|
+
let sectionPreferenceTouched = sessionStorage.getItem("__foundry_collapsed_sections") != null;
|
|
1755
2130
|
try {
|
|
1756
2131
|
for (const key of JSON.parse(sessionStorage.getItem("__foundry_collapsed_sections") ?? "[]")) {
|
|
1757
2132
|
if (typeof key === "string") collapsedSections.add(key);
|
|
@@ -1841,10 +2216,7 @@ function installFoundryInspector(options = {}) {
|
|
|
1841
2216
|
"--fdc-canvas-right",
|
|
1842
2217
|
`${Math.max(12, window.innerWidth - bounds.right)}px`
|
|
1843
2218
|
);
|
|
1844
|
-
host.style.setProperty(
|
|
1845
|
-
"--fdc-tray-lift",
|
|
1846
|
-
workspaceState.tray !== "hidden" ? `${Math.max(0, window.innerHeight - bounds.bottom)}px` : "0px"
|
|
1847
|
-
);
|
|
2219
|
+
host.style.setProperty("--fdc-tray-lift", "0px");
|
|
1848
2220
|
if (workspaceState.utility) applyUtilityRect(workspaceState.utility);
|
|
1849
2221
|
}
|
|
1850
2222
|
function persistUtilityRect(utility, rect) {
|
|
@@ -1958,15 +2330,17 @@ function installFoundryInspector(options = {}) {
|
|
|
1958
2330
|
}
|
|
1959
2331
|
}
|
|
1960
2332
|
function updateChangeCount(count, latest) {
|
|
1961
|
-
changeCount.textContent = String(count);
|
|
1962
|
-
changeCount.hidden = count === 0;
|
|
1963
2333
|
changeDock.hidden = count === 0;
|
|
1964
2334
|
workspaceState = updateWorkspace(workspaceState, {
|
|
1965
|
-
type: "set-
|
|
1966
|
-
|
|
2335
|
+
type: "set-change-summary",
|
|
2336
|
+
visible: count > 0
|
|
1967
2337
|
});
|
|
1968
|
-
|
|
1969
|
-
|
|
2338
|
+
panel.classList.toggle("change-summary-visible", count > 0);
|
|
2339
|
+
if (count === 0) {
|
|
2340
|
+
reviewTakeover.hidden = true;
|
|
2341
|
+
reviewSuspended = false;
|
|
2342
|
+
dockReviewButton.textContent = "Review";
|
|
2343
|
+
}
|
|
1970
2344
|
trayCompare.disabled = count === 0 || historyCursor === 0;
|
|
1971
2345
|
changeDock.querySelector("[data-dock-count]").textContent = `${count} ${count === 1 ? "change" : "changes"} recorded`;
|
|
1972
2346
|
if (latest) {
|
|
@@ -2511,15 +2885,38 @@ function installFoundryInspector(options = {}) {
|
|
|
2511
2885
|
);
|
|
2512
2886
|
const range = visible.length > 120 && !queryValue ? virtualRange(visible.length, previousScrollTop, layerTree.clientHeight || 320, 30, 8) : { start: 0, end: visible.length, before: 0, after: 0 };
|
|
2513
2887
|
const rendered = visible.slice(range.start, range.end);
|
|
2514
|
-
|
|
2888
|
+
const hasRenderedSelection = rendered.some((entry) => selectedElements.includes(entry.element));
|
|
2889
|
+
const previouslyVisible = visibleLayerElements;
|
|
2890
|
+
visibleLayerElements = new Set(visible.map((entry) => entry.element));
|
|
2891
|
+
layerTree.innerHTML = visible.length ? `${range.before ? `<div class="layer-spacer" style="height:${range.before}px"></div>` : ""}${rendered.map((entry, renderedPosition) => {
|
|
2515
2892
|
const index = layerEntries.indexOf(entry);
|
|
2516
2893
|
const meta = entry.instrumented ? "Mapped" : entry.kind;
|
|
2517
2894
|
const collapsed = collapsedLayers.has(entry.element);
|
|
2518
|
-
|
|
2895
|
+
const position = range.start + renderedPosition;
|
|
2896
|
+
const depth = Math.min(entry.depth, 8);
|
|
2897
|
+
const selectedLayer = selectedElements.includes(entry.element);
|
|
2898
|
+
const entering = previouslyVisible.has(entry.element) ? "" : " entering";
|
|
2899
|
+
const branch = depth ? `<span class="layer-branch" aria-hidden="true"></span>` : "";
|
|
2900
|
+
return `<div class="layer-row ${selectedLayer ? "selected" : ""}${entering}" style="--layer-depth:${depth};--layer-position:${Math.min(renderedPosition, 5)};--layer-branch-left:${10 + (depth - 1) * 10}px" data-layer-row="${index}" draggable="true">${branch}<button class="layer-toggle" data-layer-toggle="${index}" aria-label="${collapsed ? "Expand" : "Collapse"} ${escapeHtml(entry.label)}" aria-expanded="${!collapsed}" ${entry.hasChildren ? "" : "disabled"}><i data-foundry-icon="${collapsed ? "chevron-right" : "chevron-down"}"></i></button><button class="layer-select" data-layer-index="${index}" role="treeitem" aria-level="${depth + 1}" aria-posinset="${position + 1}" aria-setsize="${visible.length}" aria-selected="${selectedLayer}" ${entry.hasChildren ? `aria-expanded="${!collapsed}"` : ""} tabindex="${selectedLayer || !hasRenderedSelection && renderedPosition === 0 ? "0" : "-1"}" aria-label="Select ${escapeHtml(entry.label)}"><i class="layer-icon" data-foundry-icon="${entry.kind === "component" ? "component" : "box"}"></i><span class="layer-label">${escapeHtml(entry.label)}</span><span class="layer-meta">${escapeHtml(meta)}</span></button></div>`;
|
|
2519
2901
|
}).join(
|
|
2520
2902
|
""
|
|
2521
2903
|
)}${range.after ? `<div class="layer-spacer" style="height:${range.after}px"></div>` : ""}` : '<div class="layers-empty">No visible layers match this search.</div>';
|
|
2522
2904
|
layerTree.scrollTop = previousScrollTop;
|
|
2905
|
+
const selectedRow = layerTree.querySelector(".layer-row.selected");
|
|
2906
|
+
if (selectedRow) {
|
|
2907
|
+
const targetTop = selectedRow.offsetTop;
|
|
2908
|
+
const glide = document.createElement("span");
|
|
2909
|
+
glide.className = "layer-selection-glide";
|
|
2910
|
+
glide.setAttribute("aria-hidden", "true");
|
|
2911
|
+
glide.style.setProperty("--layer-glide-y", `${layerGlideTop ?? targetTop}px`);
|
|
2912
|
+
layerTree.prepend(glide);
|
|
2913
|
+
requestAnimationFrame(() => {
|
|
2914
|
+
glide.style.setProperty("--layer-glide-y", `${targetTop}px`);
|
|
2915
|
+
});
|
|
2916
|
+
layerGlideTop = targetTop;
|
|
2917
|
+
} else {
|
|
2918
|
+
layerGlideTop = null;
|
|
2919
|
+
}
|
|
2523
2920
|
renderIcons(layerTree);
|
|
2524
2921
|
layerTree.querySelectorAll("[data-layer-row]").forEach((row) => {
|
|
2525
2922
|
const entry = layerEntries[Number(row.dataset.layerRow)];
|
|
@@ -2601,6 +2998,73 @@ function installFoundryInspector(options = {}) {
|
|
|
2601
2998
|
select(entry.element, event.shiftKey);
|
|
2602
2999
|
entry.element.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
2603
3000
|
});
|
|
3001
|
+
button.addEventListener("keydown", (event) => {
|
|
3002
|
+
const buttons = [...layerTree.querySelectorAll("[data-layer-index]")];
|
|
3003
|
+
const currentIndex = buttons.indexOf(button);
|
|
3004
|
+
const focusAt = (index) => {
|
|
3005
|
+
const target = buttons[index];
|
|
3006
|
+
if (!target) return;
|
|
3007
|
+
buttons.forEach((item) => item.tabIndex = item === target ? 0 : -1);
|
|
3008
|
+
target.focus();
|
|
3009
|
+
target.closest(".layer-row")?.scrollIntoView({ block: "nearest" });
|
|
3010
|
+
};
|
|
3011
|
+
if (event.key === "ArrowDown") {
|
|
3012
|
+
event.preventDefault();
|
|
3013
|
+
focusAt(Math.min(buttons.length - 1, currentIndex + 1));
|
|
3014
|
+
return;
|
|
3015
|
+
}
|
|
3016
|
+
if (event.key === "ArrowUp") {
|
|
3017
|
+
event.preventDefault();
|
|
3018
|
+
focusAt(Math.max(0, currentIndex - 1));
|
|
3019
|
+
return;
|
|
3020
|
+
}
|
|
3021
|
+
if (event.key === "Home") {
|
|
3022
|
+
event.preventDefault();
|
|
3023
|
+
focusAt(0);
|
|
3024
|
+
return;
|
|
3025
|
+
}
|
|
3026
|
+
if (event.key === "End") {
|
|
3027
|
+
event.preventDefault();
|
|
3028
|
+
focusAt(buttons.length - 1);
|
|
3029
|
+
return;
|
|
3030
|
+
}
|
|
3031
|
+
if (event.key === "ArrowRight" && entry.hasChildren) {
|
|
3032
|
+
event.preventDefault();
|
|
3033
|
+
if (collapsedLayers.has(entry.element)) {
|
|
3034
|
+
collapsedLayers.delete(entry.element);
|
|
3035
|
+
renderLayers(false);
|
|
3036
|
+
requestAnimationFrame(
|
|
3037
|
+
() => layerTree.querySelector(
|
|
3038
|
+
`[data-layer-index="${layerEntries.indexOf(entry)}"]`
|
|
3039
|
+
)?.focus()
|
|
3040
|
+
);
|
|
3041
|
+
} else {
|
|
3042
|
+
const next = buttons[currentIndex + 1];
|
|
3043
|
+
const nextEntry = next ? layerEntries[Number(next.dataset.layerIndex)] : void 0;
|
|
3044
|
+
if (nextEntry && nextEntry.depth > entry.depth) focusAt(currentIndex + 1);
|
|
3045
|
+
}
|
|
3046
|
+
return;
|
|
3047
|
+
}
|
|
3048
|
+
if (event.key !== "ArrowLeft") return;
|
|
3049
|
+
event.preventDefault();
|
|
3050
|
+
if (entry.hasChildren && !collapsedLayers.has(entry.element)) {
|
|
3051
|
+
collapsedLayers.add(entry.element);
|
|
3052
|
+
renderLayers(false);
|
|
3053
|
+
requestAnimationFrame(
|
|
3054
|
+
() => layerTree.querySelector(
|
|
3055
|
+
`[data-layer-index="${layerEntries.indexOf(entry)}"]`
|
|
3056
|
+
)?.focus()
|
|
3057
|
+
);
|
|
3058
|
+
return;
|
|
3059
|
+
}
|
|
3060
|
+
for (let index = currentIndex - 1; index >= 0; index -= 1) {
|
|
3061
|
+
const candidate2 = layerEntries[Number(buttons[index]?.dataset.layerIndex)];
|
|
3062
|
+
if (candidate2 && candidate2.depth < entry.depth) {
|
|
3063
|
+
focusAt(index);
|
|
3064
|
+
break;
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
});
|
|
2604
3068
|
});
|
|
2605
3069
|
}
|
|
2606
3070
|
const layersPreferenceKey = "__foundry_layers_visibility";
|
|
@@ -2939,8 +3403,29 @@ function installFoundryInspector(options = {}) {
|
|
|
2939
3403
|
const count = group.querySelector(".included-count");
|
|
2940
3404
|
if (count) count.textContent = `${included} included`;
|
|
2941
3405
|
});
|
|
2942
|
-
applyButton.textContent = selectedCount ? `Apply ${selectedCount} with agent` : "Apply with agent";
|
|
2943
|
-
applyButton.disabled = selectedCount === 0;
|
|
3406
|
+
applyButton.textContent = !activeAgentPresence.connected ? "Connect agent to apply" : selectedCount ? `Apply ${selectedCount} with agent` : "Apply with agent";
|
|
3407
|
+
applyButton.disabled = selectedCount === 0 || !activeAgentPresence.connected;
|
|
3408
|
+
}
|
|
3409
|
+
function agentConnectionMarkup() {
|
|
3410
|
+
const agentName = activeAgentPresence.presence?.agent?.name;
|
|
3411
|
+
return `<div class="review-agent ${activeAgentPresence.connected ? "connected" : "disconnected"}" aria-live="polite"><i></i><div><strong>${activeAgentPresence.connected ? `${escapeHtml(agentName ?? "Coding agent")} is ready` : "Connect your coding agent"}</strong><span>${activeAgentPresence.connected ? "Apply requests will be claimed automatically while this agent keeps listening." : "In the same project, ask your agent to start Foundry and keep listening for Apply requests."}</span></div>${activeAgentPresence.connected ? "" : "<button data-copy-agent-listener>Copy instruction</button>"}</div>`;
|
|
3412
|
+
}
|
|
3413
|
+
function updateAgentConnection() {
|
|
3414
|
+
const current = reviewBody.querySelector(".review-agent");
|
|
3415
|
+
if (!current) {
|
|
3416
|
+
updateReviewSelection();
|
|
3417
|
+
return;
|
|
3418
|
+
}
|
|
3419
|
+
const replacement = document.createElement("div");
|
|
3420
|
+
replacement.innerHTML = agentConnectionMarkup();
|
|
3421
|
+
current.replaceWith(replacement.firstElementChild);
|
|
3422
|
+
reviewBody.querySelector("[data-copy-agent-listener]")?.addEventListener("click", () => {
|
|
3423
|
+
void navigator.clipboard.writeText(
|
|
3424
|
+
"Start Foundry for this project and keep listening for Apply with agent requests."
|
|
3425
|
+
);
|
|
3426
|
+
showToast("Agent instruction copied");
|
|
3427
|
+
});
|
|
3428
|
+
updateReviewSelection();
|
|
2944
3429
|
}
|
|
2945
3430
|
function renderReviewList(changes) {
|
|
2946
3431
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -2954,11 +3439,12 @@ function installFoundryInspector(options = {}) {
|
|
|
2954
3439
|
const unresolvedCount = changes.filter(
|
|
2955
3440
|
(change) => change.confidence === "unresolved" || change.mappingCandidates?.length > 1 && !change.selectedMappingId
|
|
2956
3441
|
).length;
|
|
2957
|
-
reviewBody.innerHTML = changes.length || rejectedCount ? `<div class="review-overview"><div class="review-summary" data-unresolved="${unresolvedCount}" aria-live="polite"><strong>Preparing review\u2026</strong><span>Only included, exactly mapped changes will be sent to your agent.</span></div><div class="review-toolbar"><button data-review-approve-exact>Include all</button><button data-review-toggle-rejected>${reviewShowRejected ? "Hide removed" : rejectedCount ? `${rejectedCount} removed` : "Removed"}</button></div></div>${[
|
|
3442
|
+
reviewBody.innerHTML = changes.length || rejectedCount ? `<div class="review-overview"><div class="review-summary" data-unresolved="${unresolvedCount}" aria-live="polite"><strong>Preparing review\u2026</strong><span>Only included, exactly mapped changes will be sent to your agent.</span></div><div class="review-toolbar"><button data-review-compare>Compare</button><button data-review-approve-exact>Include all</button><button data-review-toggle-rejected>${reviewShowRejected ? "Hide removed" : rejectedCount ? `${rejectedCount} removed` : "Removed"}</button></div></div>${agentConnectionMarkup()}${[
|
|
2958
3443
|
...groups.values()
|
|
2959
3444
|
].map((group) => {
|
|
2960
3445
|
const target = group[0].target;
|
|
2961
|
-
|
|
3446
|
+
const collapsed = collapsedReviewGroups.has(target.id);
|
|
3447
|
+
return `<section class="review-group ${collapsed ? "collapsed" : ""}" data-review-target="${escapeHtml(target.id)}"><button class="review-group-title" aria-expanded="${String(!collapsed)}"><i data-foundry-icon="chevron-down"></i><strong>${escapeHtml(target.label)}</strong><span class="included-count"></span><span class="group-total">${group.length}</span></button>${group.map((change) => {
|
|
2962
3448
|
const unresolved = change.confidence === "unresolved" || change.mappingCandidates?.length > 1 && !change.selectedMappingId;
|
|
2963
3449
|
const selectable = !unresolved && change.status !== "rejected";
|
|
2964
3450
|
const checked = selectable && reviewSelection(reviewDraft, change.id, true);
|
|
@@ -2987,6 +3473,12 @@ function installFoundryInspector(options = {}) {
|
|
|
2987
3473
|
reviewCancel.dataset.action = "back";
|
|
2988
3474
|
reviewCancel.textContent = "Back";
|
|
2989
3475
|
renderIcons(reviewBody);
|
|
3476
|
+
reviewBody.querySelector("[data-copy-agent-listener]")?.addEventListener("click", () => {
|
|
3477
|
+
void navigator.clipboard.writeText(
|
|
3478
|
+
"Start Foundry for this project and keep listening for Apply with agent requests."
|
|
3479
|
+
);
|
|
3480
|
+
showToast("Agent instruction copied");
|
|
3481
|
+
});
|
|
2990
3482
|
reviewBody.querySelectorAll("[data-review-change]").forEach(
|
|
2991
3483
|
(field) => field.addEventListener("change", () => {
|
|
2992
3484
|
reviewDraft.selections[field.dataset.reviewChange] = field.checked;
|
|
@@ -3005,6 +3497,9 @@ function installFoundryInspector(options = {}) {
|
|
|
3005
3497
|
const group = button.closest(".review-group");
|
|
3006
3498
|
if (!group) return;
|
|
3007
3499
|
group.classList.toggle("collapsed");
|
|
3500
|
+
const targetId = group.dataset.reviewTarget;
|
|
3501
|
+
if (group.classList.contains("collapsed")) collapsedReviewGroups.add(targetId);
|
|
3502
|
+
else collapsedReviewGroups.delete(targetId);
|
|
3008
3503
|
button.setAttribute("aria-expanded", String(!group.classList.contains("collapsed")));
|
|
3009
3504
|
})
|
|
3010
3505
|
);
|
|
@@ -3020,6 +3515,10 @@ function installFoundryInspector(options = {}) {
|
|
|
3020
3515
|
reviewShowRejected = !reviewShowRejected;
|
|
3021
3516
|
if (activeReviewPayload) renderReviewPayload(activeReviewPayload);
|
|
3022
3517
|
});
|
|
3518
|
+
reviewBody.querySelector("[data-review-compare]")?.addEventListener("click", () => {
|
|
3519
|
+
suspendReview();
|
|
3520
|
+
showComparison("after");
|
|
3521
|
+
});
|
|
3023
3522
|
reviewBody.querySelectorAll("[data-review-status]").forEach(
|
|
3024
3523
|
(button) => button.addEventListener("click", async () => {
|
|
3025
3524
|
button.disabled = true;
|
|
@@ -3043,10 +3542,15 @@ function installFoundryInspector(options = {}) {
|
|
|
3043
3542
|
if (!element) return;
|
|
3044
3543
|
const before = () => rawElementValue(element, change.property, change.before, change.unit);
|
|
3045
3544
|
const after = () => rawElementValue(element, change.property, change.after, change.unit);
|
|
3046
|
-
button.
|
|
3047
|
-
button.addEventListener("
|
|
3048
|
-
|
|
3049
|
-
|
|
3545
|
+
button.title = "Show the before value on the canvas";
|
|
3546
|
+
button.addEventListener("click", () => {
|
|
3547
|
+
before();
|
|
3548
|
+
suspendedPreviewRestore = after;
|
|
3549
|
+
select(element);
|
|
3550
|
+
suspendReview();
|
|
3551
|
+
element.scrollIntoView({ behavior: "smooth", block: "center", inline: "center" });
|
|
3552
|
+
showToast("Before value shown \xB7 Return to continue reviewing");
|
|
3553
|
+
});
|
|
3050
3554
|
});
|
|
3051
3555
|
reviewBody.querySelectorAll("[data-review-locate]").forEach((button) => {
|
|
3052
3556
|
const change = changes.find((item) => item.id === button.dataset.reviewLocate);
|
|
@@ -3061,9 +3565,9 @@ function installFoundryInspector(options = {}) {
|
|
|
3061
3565
|
button.addEventListener("mouseleave", () => previewLayer(null));
|
|
3062
3566
|
button.addEventListener("click", () => {
|
|
3063
3567
|
select(element);
|
|
3064
|
-
|
|
3568
|
+
suspendReview();
|
|
3065
3569
|
element.scrollIntoView({ behavior: "smooth", block: "center", inline: "center" });
|
|
3066
|
-
showToast("Located on the live canvas");
|
|
3570
|
+
showToast("Located on the live canvas \xB7 Return to continue reviewing");
|
|
3067
3571
|
});
|
|
3068
3572
|
});
|
|
3069
3573
|
reviewBody.querySelectorAll("[data-mapping-choice]").forEach(
|
|
@@ -3085,7 +3589,7 @@ function installFoundryInspector(options = {}) {
|
|
|
3085
3589
|
updateReviewSelection();
|
|
3086
3590
|
}
|
|
3087
3591
|
const runStateLabels = {
|
|
3088
|
-
queued: "
|
|
3592
|
+
queued: "Queued for agent",
|
|
3089
3593
|
claimed: "Agent connected",
|
|
3090
3594
|
applying: "Applying source changes",
|
|
3091
3595
|
rebuilding: "Rebuilding and checking",
|
|
@@ -3134,11 +3638,11 @@ function installFoundryInspector(options = {}) {
|
|
|
3134
3638
|
const attention = ["needs_attention", "failed"].includes(run.state);
|
|
3135
3639
|
const passed = run.state === "passed";
|
|
3136
3640
|
const active = ["queued", "claimed", "applying", "rebuilding", "verifying"].includes(run.state);
|
|
3137
|
-
const latestMessage = run.messages.at(-1)?.message ?? run.error ?? "Apply run created.";
|
|
3641
|
+
const latestMessage = run.state === "queued" ? "The reviewed changes are queued. Keep your coding agent active, or ask it to apply your reviewed Foundry changes." : run.messages.at(-1)?.message ?? run.error ?? "Apply run created.";
|
|
3138
3642
|
captureVerifiedRun(run);
|
|
3139
3643
|
reviewCount.textContent = `Attempt ${run.attempts}`;
|
|
3140
3644
|
reviewBody.innerHTML = `<div class="run-summary"><div class="run-state"><i class="${passed ? "passed" : attention ? "attention" : active ? "active" : ""}"></i><strong>${escapeHtml(runStateLabels[run.state] ?? run.state)}</strong></div><p>${escapeHtml(latestMessage)}</p></div><div class="run-steps">${run.messages.map(
|
|
3141
|
-
(message, index) => `<div class="run-step"><span>${String(index + 1).padStart(2, "0")}</span><div><strong>${escapeHtml(runStateLabels[message.state] ?? message.state)}</strong><p>${escapeHtml(message.message)}</p></div></div>`
|
|
3645
|
+
(message, index) => `<div class="run-step"><span>${String(index + 1).padStart(2, "0")}</span><div><strong>${escapeHtml(runStateLabels[message.state] ?? message.state)}</strong><p>${escapeHtml(message.state === "queued" ? "Ready for the active coding agent to claim." : message.message)}</p></div></div>`
|
|
3142
3646
|
).join(
|
|
3143
3647
|
""
|
|
3144
3648
|
)}</div>${run.changedFiles.length ? `<div class="run-files"><strong>Changed files</strong>${run.changedFiles.map((file) => `<code>${escapeHtml(file)}</code>`).join("")}</div>` : ""}${run.validationResults.length ? `<div class="result-list">${run.validationResults.map((result) => `<div class="result-row ${result.passed ? "pass" : "fail"}"><span>${result.passed ? "Passed" : "Failed"} \xB7 ${escapeHtml(result.name)}</span><span>${escapeHtml(result.summary ?? "")}</span></div>`).join("")}</div>` : ""}${run.verificationResults.length ? `<div class="result-list">${run.verificationResults.map((result) => `<div class="result-row ${result.passed ? "pass" : "fail"}"><span>${result.passed ? "Matched" : "Mismatch"} \xB7 ${escapeHtml(result.property)}</span><span>${escapeHtml(reviewValue(result.requested))} \u2192 ${escapeHtml(reviewValue(result.rendered))}</span></div>`).join("")}</div>` : ""}`;
|
|
@@ -3168,6 +3672,7 @@ function installFoundryInspector(options = {}) {
|
|
|
3168
3672
|
}
|
|
3169
3673
|
}
|
|
3170
3674
|
function renderReviewPayload(payload) {
|
|
3675
|
+
const preservedScrollTop = reviewBody.scrollTop || reviewScrollTop;
|
|
3171
3676
|
activeReviewPayload = payload;
|
|
3172
3677
|
const activeChanges = payload.changeSet.changes.filter(
|
|
3173
3678
|
(change) => change.status !== "rejected" && String(change.before) !== String(change.after)
|
|
@@ -3183,16 +3688,27 @@ function installFoundryInspector(options = {}) {
|
|
|
3183
3688
|
"needs_attention",
|
|
3184
3689
|
"failed"
|
|
3185
3690
|
].includes(latestRun.state);
|
|
3691
|
+
if (activeOrAttention) {
|
|
3692
|
+
const latestMessage = latestRun.messages?.at(-1)?.message;
|
|
3693
|
+
changeDock.querySelector("[data-dock-last]").textContent = latestMessage ?? runStateLabels[latestRun.state] ?? latestRun.state;
|
|
3694
|
+
}
|
|
3186
3695
|
const pending = payload.changeSet.changes.filter(
|
|
3187
3696
|
(change) => String(change.before) !== String(change.after) && (["draft", "approved", "unresolved"].includes(change.status) || reviewShowRejected && change.status === "rejected")
|
|
3188
3697
|
);
|
|
3189
3698
|
if (activeOrAttention || !pending.length && latestRun) renderApplyRun(latestRun);
|
|
3190
3699
|
else renderReviewList(pending);
|
|
3700
|
+
requestAnimationFrame(() => {
|
|
3701
|
+
if (workspaceState.reviewOpen) reviewBody.scrollTop = preservedScrollTop;
|
|
3702
|
+
});
|
|
3191
3703
|
}
|
|
3192
3704
|
async function refreshReview() {
|
|
3193
|
-
if (workspaceState.
|
|
3705
|
+
if (!workspaceState.reviewOpen || !sessionId || !token) return;
|
|
3194
3706
|
try {
|
|
3195
|
-
const payload = await
|
|
3707
|
+
const [payload, presence] = await Promise.all([
|
|
3708
|
+
sessionRequest(),
|
|
3709
|
+
sessionRequest("/agent-presence")
|
|
3710
|
+
]);
|
|
3711
|
+
activeAgentPresence = presence;
|
|
3196
3712
|
const latestRun = payload.applyRuns?.at(-1);
|
|
3197
3713
|
const runNeedsRefresh = latestRun && [
|
|
3198
3714
|
"queued",
|
|
@@ -3205,6 +3721,7 @@ function installFoundryInspector(options = {}) {
|
|
|
3205
3721
|
].includes(latestRun.state);
|
|
3206
3722
|
if (applyButton.dataset.action === "apply" && !runNeedsRefresh) {
|
|
3207
3723
|
activeReviewPayload = payload;
|
|
3724
|
+
updateAgentConnection();
|
|
3208
3725
|
return;
|
|
3209
3726
|
}
|
|
3210
3727
|
renderReviewPayload(payload);
|
|
@@ -3217,29 +3734,43 @@ function installFoundryInspector(options = {}) {
|
|
|
3217
3734
|
showToast("Session connection is missing");
|
|
3218
3735
|
return;
|
|
3219
3736
|
}
|
|
3220
|
-
if (shadow.activeElement instanceof HTMLElement)
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3737
|
+
if (!reviewSuspended && shadow.activeElement instanceof HTMLElement)
|
|
3738
|
+
lastReviewTrigger = shadow.activeElement;
|
|
3739
|
+
suspendedPreviewRestore?.();
|
|
3740
|
+
suspendedPreviewRestore = void 0;
|
|
3741
|
+
reviewSuspended = false;
|
|
3742
|
+
dockReviewButton.textContent = "Review";
|
|
3743
|
+
workspaceState = updateWorkspace(workspaceState, { type: "set-review", open: true });
|
|
3744
|
+
reviewTakeover.hidden = false;
|
|
3224
3745
|
outline.hidden = true;
|
|
3225
|
-
|
|
3226
|
-
|
|
3746
|
+
const [payload, presence] = await Promise.all([
|
|
3747
|
+
sessionRequest(),
|
|
3748
|
+
sessionRequest("/agent-presence")
|
|
3749
|
+
]);
|
|
3750
|
+
activeAgentPresence = presence;
|
|
3751
|
+
renderReviewPayload(payload);
|
|
3752
|
+
requestAnimationFrame(() => {
|
|
3753
|
+
reviewBody.scrollTop = reviewScrollTop;
|
|
3754
|
+
shadow.querySelector(".review-back")?.focus();
|
|
3755
|
+
});
|
|
3227
3756
|
clearInterval(reviewPoll);
|
|
3228
3757
|
clearInterval(healthPoll);
|
|
3229
3758
|
reviewPoll = setInterval(() => void refreshReview(), 1e3);
|
|
3230
3759
|
}
|
|
3231
|
-
function closeReview() {
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
});
|
|
3236
|
-
changeDock.classList.remove("expanded");
|
|
3237
|
-
reviewView.hidden = true;
|
|
3760
|
+
function closeReview(restoreFocus = true) {
|
|
3761
|
+
reviewScrollTop = reviewBody.scrollTop;
|
|
3762
|
+
workspaceState = updateWorkspace(workspaceState, { type: "set-review", open: false });
|
|
3763
|
+
reviewTakeover.hidden = true;
|
|
3238
3764
|
clearInterval(reviewPoll);
|
|
3239
3765
|
reviewPoll = void 0;
|
|
3240
3766
|
updateOutline();
|
|
3241
|
-
|
|
3242
|
-
|
|
3767
|
+
if (restoreFocus) lastReviewTrigger?.focus();
|
|
3768
|
+
}
|
|
3769
|
+
function suspendReview() {
|
|
3770
|
+
reviewSuspended = true;
|
|
3771
|
+
dockReviewButton.textContent = "Return";
|
|
3772
|
+
closeReview(false);
|
|
3773
|
+
dockReviewButton.focus();
|
|
3243
3774
|
}
|
|
3244
3775
|
async function submitReviewedRun() {
|
|
3245
3776
|
if (!activeReviewPayload) return;
|
|
@@ -3258,6 +3789,12 @@ function installFoundryInspector(options = {}) {
|
|
|
3258
3789
|
);
|
|
3259
3790
|
applyButton.disabled = true;
|
|
3260
3791
|
try {
|
|
3792
|
+
activeAgentPresence = await sessionRequest("/agent-presence");
|
|
3793
|
+
if (!activeAgentPresence.connected) {
|
|
3794
|
+
updateAgentConnection();
|
|
3795
|
+
showToast("No coding agent is listening yet");
|
|
3796
|
+
return;
|
|
3797
|
+
}
|
|
3261
3798
|
const payload = await sessionRequest("/apply-runs", {
|
|
3262
3799
|
method: "POST",
|
|
3263
3800
|
body: JSON.stringify({
|
|
@@ -3716,13 +4253,8 @@ function installFoundryInspector(options = {}) {
|
|
|
3716
4253
|
}
|
|
3717
4254
|
function renderContextPanel(category) {
|
|
3718
4255
|
if (!selected) return "";
|
|
3719
|
-
const supportsTokens = ["layout", "typography", "color", "effects"].includes(category);
|
|
3720
|
-
const tokens = supportsTokens ? tokensForCategory(category).slice(0, 24) : [];
|
|
3721
4256
|
const component = selectedComponent();
|
|
3722
4257
|
const variants = component?.variants ?? [];
|
|
3723
|
-
const tokenPanel = tokens.length ? `<section class="native-panel"><div class="native-panel-head"><strong>Project ${category === "typography" ? "styles" : "tokens"}</strong><span>${tokens.length}</span></div><input class="native-search" type="search" placeholder="Search project values" aria-label="Search project values"/><div class="native-grid">${tokens.map(
|
|
3724
|
-
(token2) => `<button class="native-chip" data-native-token="${escapeHtml(token2.id)}" data-token-search="${escapeHtml(`${token2.name} ${token2.value}`.toLowerCase())}" title="${escapeHtml(token2.value)}">${category === "color" ? `<span class="swatch" style="--token-color:${escapeHtml(token2.value)}"></span>` : ""}<span>${escapeHtml(token2.name)}</span></button>`
|
|
3725
|
-
).join("")}</div></section>` : "";
|
|
3726
4258
|
const contrast = category === "color" ? contrastRatio(getComputedStyle(selected).color, opaqueBackground(selected)) : null;
|
|
3727
4259
|
const health = category === "color" && contrast != null ? `<div class="design-health ${contrast >= 4.5 ? "pass" : "fail"}"><i data-foundry-icon="contrast"></i><span>${contrast}:1 contrast \xB7 ${contrast >= 4.5 ? "AA pass" : "Needs attention"}</span></div>` : "";
|
|
3728
4260
|
const variantPanel = category === "content" && variants.length ? `<section class="native-panel"><div class="native-panel-head"><strong>${escapeHtml(component.name)} variants</strong><span>${component.instances} instances</span></div><div class="variant-list">${variants.map(
|
|
@@ -3730,9 +4262,7 @@ function installFoundryInspector(options = {}) {
|
|
|
3730
4262
|
Object.entries(variant.props).map(([key, value]) => `${key}=${value}`).join(" \xB7 ")
|
|
3731
4263
|
)}</code></button>`
|
|
3732
4264
|
).join("")}</div></section>` : "";
|
|
3733
|
-
|
|
3734
|
-
const tokenMode = category === "layout" && (designGraph?.tokens.length ?? 0) ? `<div class="context-tools"><button class="${tokenOnly ? "active" : ""}" data-token-only><i data-foundry-icon="${tokenOnly ? "lock" : "unlink-2"}"></i>${tokenOnly ? "Token-only on" : "Token-only off"}</button></div>` : "";
|
|
3735
|
-
return `${categoryTools}${tokenMode}${health}${variantPanel}${tokenPanel}`;
|
|
4265
|
+
return `${health}${variantPanel}`;
|
|
3736
4266
|
}
|
|
3737
4267
|
async function applyControlValue(control, value, operationLabel) {
|
|
3738
4268
|
if (!selected) return;
|
|
@@ -3844,13 +4374,18 @@ function installFoundryInspector(options = {}) {
|
|
|
3844
4374
|
});
|
|
3845
4375
|
controlsRoot.querySelectorAll("[data-color-action]").forEach((button) => {
|
|
3846
4376
|
button.addEventListener("click", () => {
|
|
3847
|
-
const
|
|
3848
|
-
if (!
|
|
3849
|
-
|
|
4377
|
+
const gradient = controls.find((item) => item.property === "backgroundImage");
|
|
4378
|
+
if (!gradient) return;
|
|
4379
|
+
if (button.dataset.colorAction === "clear") {
|
|
4380
|
+
const fill = controls.find((item) => item.property === "backgroundColor");
|
|
4381
|
+
if (fill) void applyControlValue(fill, "transparent", "Clear fill color");
|
|
4382
|
+
void applyControlValue(gradient, "none", "Clear fill gradient");
|
|
4383
|
+
return;
|
|
4384
|
+
}
|
|
3850
4385
|
void applyControlValue(
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
4386
|
+
gradient,
|
|
4387
|
+
"linear-gradient(135deg, currentColor 0%, transparent 100%)",
|
|
4388
|
+
"Add linear gradient"
|
|
3854
4389
|
);
|
|
3855
4390
|
});
|
|
3856
4391
|
});
|
|
@@ -3878,6 +4413,13 @@ function installFoundryInspector(options = {}) {
|
|
|
3878
4413
|
const siblings = [...parent.children].filter((item) => item !== element);
|
|
3879
4414
|
const index = Math.max(0, Math.min(Number(value), siblings.length));
|
|
3880
4415
|
parent.insertBefore(element, siblings[index] ?? null);
|
|
4416
|
+
} else if (property === "rotate") element.style.rotate = `${value}${unit ?? "deg"}`;
|
|
4417
|
+
else if (property === "scaleX" || property === "scaleY") {
|
|
4418
|
+
const raw = getComputedStyle(element).scale;
|
|
4419
|
+
const parts = !raw || raw === "none" ? [1, 1] : raw.split(/\s+/).map(numberFrom);
|
|
4420
|
+
const x = property === "scaleX" ? Number(value) : parts[0] ?? 1;
|
|
4421
|
+
const y = property === "scaleY" ? Number(value) : parts[1] ?? parts[0] ?? 1;
|
|
4422
|
+
element.style.scale = `${x} ${y}`;
|
|
3881
4423
|
} else if (property === "textContent") element.textContent = String(value);
|
|
3882
4424
|
else if (["aria-label", "role", "tabindex", "alt", "src"].includes(property))
|
|
3883
4425
|
element.setAttribute(property, String(value));
|
|
@@ -4049,8 +4591,14 @@ function installFoundryInspector(options = {}) {
|
|
|
4049
4591
|
{ id: "redo", label: "Redo preview", shortcut: "\u21E7\u2318Z", icon: "redo-2" },
|
|
4050
4592
|
{ id: "review", label: "Review changes", shortcut: "", icon: "file-text" }
|
|
4051
4593
|
];
|
|
4052
|
-
|
|
4594
|
+
let commandReturnFocus = null;
|
|
4595
|
+
function closeCommands(restoreFocus = true) {
|
|
4053
4596
|
commandPalette.hidden = true;
|
|
4597
|
+
if (restoreFocus && commandReturnFocus?.isConnected) commandReturnFocus.focus();
|
|
4598
|
+
commandReturnFocus = null;
|
|
4599
|
+
}
|
|
4600
|
+
function runCommand(id) {
|
|
4601
|
+
closeCommands(false);
|
|
4054
4602
|
if (id === "health") openHealth();
|
|
4055
4603
|
if (id === "layers") toggleLayers(true);
|
|
4056
4604
|
if (id === "compare") showComparison("after");
|
|
@@ -4082,6 +4630,7 @@ function installFoundryInspector(options = {}) {
|
|
|
4082
4630
|
);
|
|
4083
4631
|
}
|
|
4084
4632
|
function openCommands() {
|
|
4633
|
+
commandReturnFocus = shadow.activeElement;
|
|
4085
4634
|
commandPalette.hidden = false;
|
|
4086
4635
|
const input = commandPalette.querySelector("input");
|
|
4087
4636
|
input.value = "";
|
|
@@ -4388,6 +4937,180 @@ function installFoundryInspector(options = {}) {
|
|
|
4388
4937
|
);
|
|
4389
4938
|
});
|
|
4390
4939
|
}
|
|
4940
|
+
function activeColorValue() {
|
|
4941
|
+
if (!activeColor) return "#000000";
|
|
4942
|
+
const [red, green, blue] = hsvToRgb(activeColor.hue, activeColor.saturation, activeColor.value);
|
|
4943
|
+
return activeColor.alpha >= 0.999 ? rgbHex(red, green, blue) : `rgba(${red}, ${green}, ${blue}, ${Math.round(activeColor.alpha * 100) / 100})`;
|
|
4944
|
+
}
|
|
4945
|
+
function refreshColorPopover() {
|
|
4946
|
+
if (!activeColor) return;
|
|
4947
|
+
const plane = colorPopover.querySelector(".color-plane");
|
|
4948
|
+
const handle = colorPopover.querySelector(".color-plane-handle");
|
|
4949
|
+
const hex = colorPopover.querySelector("[data-color-hex]");
|
|
4950
|
+
const hue = colorPopover.querySelector("[data-color-hue]");
|
|
4951
|
+
const alpha = colorPopover.querySelector("[data-color-alpha]");
|
|
4952
|
+
if (plane) plane.style.setProperty("--picker-hue", String(activeColor.hue));
|
|
4953
|
+
if (handle) {
|
|
4954
|
+
handle.style.left = `${activeColor.saturation * 100}%`;
|
|
4955
|
+
handle.style.top = `${(1 - activeColor.value) * 100}%`;
|
|
4956
|
+
}
|
|
4957
|
+
if (hex) hex.value = colorForInput(activeColorValue()).toUpperCase();
|
|
4958
|
+
if (hue) hue.value = String(Math.round(activeColor.hue));
|
|
4959
|
+
if (alpha) alpha.value = String(Math.round(activeColor.alpha * 100));
|
|
4960
|
+
}
|
|
4961
|
+
function previewActiveColor() {
|
|
4962
|
+
if (!activeColor) return;
|
|
4963
|
+
const value = activeColorValue();
|
|
4964
|
+
activeColor.control.apply(value);
|
|
4965
|
+
const trigger = controlsRoot.querySelector(
|
|
4966
|
+
`[data-color-control="${activeColor.index}"]`
|
|
4967
|
+
);
|
|
4968
|
+
const swatch = trigger?.querySelector(".color-swatch");
|
|
4969
|
+
const label = trigger?.querySelector(".color-value");
|
|
4970
|
+
if (swatch) {
|
|
4971
|
+
swatch.classList.toggle("transparent", activeColor.alpha === 0);
|
|
4972
|
+
swatch.style.setProperty("--swatch-color", colorForInput(value));
|
|
4973
|
+
}
|
|
4974
|
+
if (label) label.textContent = activeColor.alpha === 0 ? "Transparent" : value.toUpperCase();
|
|
4975
|
+
refreshColorPopover();
|
|
4976
|
+
updateOutline();
|
|
4977
|
+
}
|
|
4978
|
+
function commitActiveColor(before) {
|
|
4979
|
+
if (!activeColor) return;
|
|
4980
|
+
const after = activeColor.control.read();
|
|
4981
|
+
if (String(before) === String(after)) return;
|
|
4982
|
+
recentColors.delete(String(after));
|
|
4983
|
+
recentColors.add(String(after));
|
|
4984
|
+
while (recentColors.size > 8) recentColors.delete(recentColors.values().next().value);
|
|
4985
|
+
pushHistory({
|
|
4986
|
+
element: activeColor.element,
|
|
4987
|
+
property: activeColor.control.property,
|
|
4988
|
+
before,
|
|
4989
|
+
after,
|
|
4990
|
+
category: activeColor.control.category,
|
|
4991
|
+
label: activeColor.control.label
|
|
4992
|
+
});
|
|
4993
|
+
void record(activeColor.control, before, after, activeColor.element, "Choose color");
|
|
4994
|
+
}
|
|
4995
|
+
function setActiveColor(value) {
|
|
4996
|
+
if (!activeColor) return;
|
|
4997
|
+
const [red, green, blue] = hexChannels(value);
|
|
4998
|
+
const [hue, saturation, brightness] = rgbToHsv(red, green, blue);
|
|
4999
|
+
activeColor.hue = hue;
|
|
5000
|
+
activeColor.saturation = saturation;
|
|
5001
|
+
activeColor.value = brightness;
|
|
5002
|
+
activeColor.alpha = colorAlpha(value);
|
|
5003
|
+
previewActiveColor();
|
|
5004
|
+
}
|
|
5005
|
+
function openColorEditor(trigger, index) {
|
|
5006
|
+
const control = selectedControls[index];
|
|
5007
|
+
if (!control || control.kind !== "color" || !selected) return;
|
|
5008
|
+
const [red, green, blue] = hexChannels(String(control.read()));
|
|
5009
|
+
const [hue, saturation, value] = rgbToHsv(red, green, blue);
|
|
5010
|
+
activeColor = {
|
|
5011
|
+
index,
|
|
5012
|
+
control,
|
|
5013
|
+
element: selected,
|
|
5014
|
+
hue,
|
|
5015
|
+
saturation,
|
|
5016
|
+
value,
|
|
5017
|
+
alpha: colorAlpha(String(control.read()))
|
|
5018
|
+
};
|
|
5019
|
+
const tokens = tokensForCategory("color").slice(0, 18);
|
|
5020
|
+
colorPopover.innerHTML = `<div class="color-popover-head"><strong>${escapeHtml(control.label)}</strong><button class="icon-button" data-close-color aria-label="Close color editor"><i data-foundry-icon="x"></i></button></div><div class="color-plane" style="--picker-hue:${hue}"><span class="color-plane-handle"></span></div><div class="color-sliders"><label>Hue<input data-color-hue type="range" min="0" max="359" value="${Math.round(hue)}"/></label><label>Alpha<input data-color-alpha type="range" min="0" max="100" value="${Math.round(activeColor.alpha * 100)}"/></label></div><div class="color-fields"><input data-color-hex aria-label="Hex color" value="${colorForInput(String(control.read())).toUpperCase()}"/><button data-eyedropper aria-label="Pick color from page" title="Eyedropper"><i data-foundry-icon="palette"></i></button></div>${tokens.length ? `<div class="color-popover-section"><strong>Project colors</strong><div class="color-swatches">${tokens.map((token2) => `<button data-color-value="${escapeHtml(token2.value)}" style="--picker-swatch:${escapeHtml(token2.value)}" aria-label="Use ${escapeHtml(token2.name)}" title="${escapeHtml(`${token2.name} \xB7 ${token2.value}`)}"></button>`).join("")}</div></div>` : ""}${recentColors.size ? `<div class="color-popover-section"><strong>Recent</strong><div class="color-swatches">${[
|
|
5021
|
+
...recentColors
|
|
5022
|
+
].reverse().map(
|
|
5023
|
+
(color) => `<button data-color-value="${escapeHtml(color)}" style="--picker-swatch:${escapeHtml(color)}" aria-label="Use recent color ${escapeHtml(color)}"></button>`
|
|
5024
|
+
).join("")}</div></div>` : ""}`;
|
|
5025
|
+
renderIcons(colorPopover);
|
|
5026
|
+
colorPopover.hidden = false;
|
|
5027
|
+
const triggerRect = trigger.getBoundingClientRect();
|
|
5028
|
+
const popoverRect = colorPopover.getBoundingClientRect();
|
|
5029
|
+
const left = Math.max(8, Math.min(window.innerWidth - popoverRect.width - 8, triggerRect.left));
|
|
5030
|
+
const preferredTop = triggerRect.bottom + 6;
|
|
5031
|
+
const top = preferredTop + popoverRect.height <= window.innerHeight - 8 ? preferredTop : Math.max(8, triggerRect.top - popoverRect.height - 6);
|
|
5032
|
+
colorPopover.style.left = `${Math.round(left)}px`;
|
|
5033
|
+
colorPopover.style.top = `${Math.round(top)}px`;
|
|
5034
|
+
refreshColorPopover();
|
|
5035
|
+
colorPopover.querySelector("[data-close-color]")?.addEventListener("click", () => {
|
|
5036
|
+
colorPopover.hidden = true;
|
|
5037
|
+
activeColor = void 0;
|
|
5038
|
+
trigger.focus();
|
|
5039
|
+
});
|
|
5040
|
+
const plane = colorPopover.querySelector(".color-plane");
|
|
5041
|
+
plane.addEventListener("pointerdown", (event) => {
|
|
5042
|
+
if (!activeColor) return;
|
|
5043
|
+
const before = activeColor.control.read();
|
|
5044
|
+
plane.setPointerCapture(event.pointerId);
|
|
5045
|
+
const update = (pointer) => {
|
|
5046
|
+
if (!activeColor) return;
|
|
5047
|
+
const rect = plane.getBoundingClientRect();
|
|
5048
|
+
activeColor.saturation = Math.max(
|
|
5049
|
+
0,
|
|
5050
|
+
Math.min(1, (pointer.clientX - rect.left) / rect.width)
|
|
5051
|
+
);
|
|
5052
|
+
activeColor.value = 1 - Math.max(0, Math.min(1, (pointer.clientY - rect.top) / rect.height));
|
|
5053
|
+
previewActiveColor();
|
|
5054
|
+
};
|
|
5055
|
+
const finish = (pointer) => {
|
|
5056
|
+
update(pointer);
|
|
5057
|
+
commitActiveColor(before);
|
|
5058
|
+
plane.removeEventListener("pointermove", update);
|
|
5059
|
+
plane.removeEventListener("pointerup", finish);
|
|
5060
|
+
plane.removeEventListener("pointercancel", finish);
|
|
5061
|
+
};
|
|
5062
|
+
update(event);
|
|
5063
|
+
plane.addEventListener("pointermove", update);
|
|
5064
|
+
plane.addEventListener("pointerup", finish);
|
|
5065
|
+
plane.addEventListener("pointercancel", finish);
|
|
5066
|
+
});
|
|
5067
|
+
const bindSlider = (selector, apply) => {
|
|
5068
|
+
const slider = colorPopover.querySelector(selector);
|
|
5069
|
+
let before = control.read();
|
|
5070
|
+
slider.addEventListener("pointerdown", () => {
|
|
5071
|
+
before = control.read();
|
|
5072
|
+
});
|
|
5073
|
+
slider.addEventListener("input", () => {
|
|
5074
|
+
apply(Number(slider.value));
|
|
5075
|
+
previewActiveColor();
|
|
5076
|
+
});
|
|
5077
|
+
slider.addEventListener("change", () => commitActiveColor(before));
|
|
5078
|
+
};
|
|
5079
|
+
bindSlider("[data-color-hue]", (next) => {
|
|
5080
|
+
if (activeColor) activeColor.hue = next;
|
|
5081
|
+
});
|
|
5082
|
+
bindSlider("[data-color-alpha]", (next) => {
|
|
5083
|
+
if (activeColor) activeColor.alpha = next / 100;
|
|
5084
|
+
});
|
|
5085
|
+
const hexField = colorPopover.querySelector("[data-color-hex]");
|
|
5086
|
+
let hexBefore = control.read();
|
|
5087
|
+
hexField.addEventListener("focus", () => {
|
|
5088
|
+
hexBefore = control.read();
|
|
5089
|
+
});
|
|
5090
|
+
hexField.addEventListener("change", () => {
|
|
5091
|
+
setActiveColor(hexField.value);
|
|
5092
|
+
commitActiveColor(hexBefore);
|
|
5093
|
+
});
|
|
5094
|
+
colorPopover.querySelectorAll("[data-color-value]").forEach((button) => {
|
|
5095
|
+
button.addEventListener("click", () => {
|
|
5096
|
+
const before = control.read();
|
|
5097
|
+
setActiveColor(button.dataset.colorValue ?? "#000000");
|
|
5098
|
+
commitActiveColor(before);
|
|
5099
|
+
});
|
|
5100
|
+
});
|
|
5101
|
+
colorPopover.querySelector("[data-eyedropper]")?.addEventListener("click", async () => {
|
|
5102
|
+
const EyeDropper = window.EyeDropper;
|
|
5103
|
+
if (!EyeDropper) {
|
|
5104
|
+
showToast("Eyedropper is not available in this browser");
|
|
5105
|
+
return;
|
|
5106
|
+
}
|
|
5107
|
+
const before = control.read();
|
|
5108
|
+
const result = await new EyeDropper().open().catch(() => void 0);
|
|
5109
|
+
if (!result) return;
|
|
5110
|
+
setActiveColor(result.sRGBHex);
|
|
5111
|
+
commitActiveColor(before);
|
|
5112
|
+
});
|
|
5113
|
+
}
|
|
4391
5114
|
function renderControls() {
|
|
4392
5115
|
renderToolTabs();
|
|
4393
5116
|
if (!selected) {
|
|
@@ -4405,20 +5128,55 @@ function installFoundryInspector(options = {}) {
|
|
|
4405
5128
|
{
|
|
4406
5129
|
key: "layout",
|
|
4407
5130
|
category: "layout",
|
|
4408
|
-
label: "Layout
|
|
5131
|
+
label: "Layout",
|
|
4409
5132
|
icon: CATEGORY_ICONS.layout,
|
|
4410
5133
|
sectionLabels: ["Flow", "Padding", "Margin"],
|
|
4411
5134
|
showContext: true
|
|
4412
5135
|
},
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
5136
|
+
{
|
|
5137
|
+
key: "typography",
|
|
5138
|
+
category: "typography",
|
|
5139
|
+
label: "Typography",
|
|
5140
|
+
icon: CATEGORY_ICONS.typography,
|
|
5141
|
+
showContext: true
|
|
5142
|
+
},
|
|
5143
|
+
{
|
|
5144
|
+
key: "appearance",
|
|
5145
|
+
category: "color",
|
|
5146
|
+
label: "Appearance",
|
|
5147
|
+
icon: "eye",
|
|
5148
|
+
sectionLabels: ["Appearance"]
|
|
5149
|
+
},
|
|
5150
|
+
{
|
|
5151
|
+
key: "fill",
|
|
5152
|
+
category: "color",
|
|
5153
|
+
label: "Fill",
|
|
5154
|
+
icon: CATEGORY_ICONS.color,
|
|
5155
|
+
sectionLabels: ["Fill"],
|
|
5156
|
+
showContext: true
|
|
5157
|
+
},
|
|
5158
|
+
{
|
|
5159
|
+
key: "stroke",
|
|
5160
|
+
category: "effects",
|
|
5161
|
+
label: "Stroke",
|
|
5162
|
+
icon: "box",
|
|
5163
|
+
sectionLabels: ["Stroke"]
|
|
5164
|
+
},
|
|
5165
|
+
{
|
|
5166
|
+
key: "effects",
|
|
5167
|
+
category: "effects",
|
|
5168
|
+
label: "Effects",
|
|
5169
|
+
icon: CATEGORY_ICONS.effects,
|
|
5170
|
+
sectionLabels: ["Corners", "Effects"],
|
|
5171
|
+
showContext: true
|
|
5172
|
+
},
|
|
5173
|
+
...["content", "accessibility"].map((category) => ({
|
|
5174
|
+
key: category,
|
|
5175
|
+
category,
|
|
5176
|
+
label: CATEGORY_LABELS[category],
|
|
5177
|
+
icon: CATEGORY_ICONS[category],
|
|
5178
|
+
showContext: true
|
|
5179
|
+
}))
|
|
4422
5180
|
];
|
|
4423
5181
|
const indexedControls = selectedControls.map((control, index) => ({ control, index })).filter(
|
|
4424
5182
|
({ control }) => tokenOnly ? matchingTokens(
|
|
@@ -4445,7 +5203,8 @@ function installFoundryInspector(options = {}) {
|
|
|
4445
5203
|
entries.forEach(({ control }) => renderedProperties.add(control.property));
|
|
4446
5204
|
if (!entries.length) return "";
|
|
4447
5205
|
const sectionKey = `${group.key}:${section.label}`;
|
|
4448
|
-
|
|
5206
|
+
const sectionActions = section.label === "Position and size" ? '<span class="section-actions"><button class="section-action" data-layout-action="lock" aria-label="Lock aspect ratio" title="Lock aspect ratio"><i data-foundry-icon="lock"></i></button></span>' : section.label === "Padding" ? `<span class="section-actions"><button class="section-action ${paddingLinked ? "active" : ""}" data-padding-link aria-label="${paddingLinked ? "Unlink padding values" : "Link padding values"}"><i data-foundry-icon="${paddingLinked ? "link-2" : "unlink-2"}"></i></button></span>` : section.label === "Typeface" ? '<span class="section-actions type-presets"><button data-type-preset="compact">S</button><button data-type-preset="body">M</button><button data-type-preset="display">L</button></span>' : section.label === "Fill" ? '<span class="section-actions"><button class="section-action" data-color-action="gradient" aria-label="Add linear gradient" title="Add gradient"><i data-foundry-icon="sparkles"></i></button><button class="section-action" data-color-action="clear" aria-label="Clear fill" title="Clear fill"><i data-foundry-icon="x"></i></button></span>' : section.label === "Corners" ? `<span class="section-actions"><button class="section-action ${radiusLinked ? "active" : ""}" data-radius-link aria-label="${radiusLinked ? "Edit corner radii independently" : "Link corner radii"}"><i data-foundry-icon="${radiusLinked ? "link-2" : "unlink-2"}"></i></button></span>` : "";
|
|
5207
|
+
return `<section class="property-section ${collapsedSections.has(sectionKey) ? "collapsed" : ""}" data-section-key="${escapeHtml(sectionKey)}"><div class="section-head"><button class="section-toggle" aria-expanded="${String(!collapsedSections.has(sectionKey))}"><i data-foundry-icon="chevron-down"></i><strong>${escapeHtml(section.label)}</strong></button>${sectionActions}</div><div class="section-grid ${section.columns === 2 ? "two" : ""} ${section.stacked ? "stacked" : ""}">${entries.map(
|
|
4449
5208
|
({ control, index }) => renderPropertyControl(control, index, section.prefixes?.[control.property])
|
|
4450
5209
|
).join("")}</div></section>`;
|
|
4451
5210
|
}).join("");
|
|
@@ -4454,7 +5213,8 @@ function installFoundryInspector(options = {}) {
|
|
|
4454
5213
|
);
|
|
4455
5214
|
const remainingSection = remaining.length ? `<section class="property-section ${collapsedSections.has(`${group.key}:Other`) ? "collapsed" : ""}" data-section-key="${escapeHtml(`${group.key}:Other`)}"><div class="section-head"><button class="section-toggle" aria-expanded="${String(!collapsedSections.has(`${group.key}:Other`))}"><i data-foundry-icon="chevron-down"></i><strong>Other</strong></button></div><div class="section-grid">${remaining.map(({ control, index }) => renderPropertyControl(control, index)).join("")}</div></section>` : "";
|
|
4456
5215
|
const categoryKey = `category:${group.key}`;
|
|
4457
|
-
|
|
5216
|
+
const categoryAction = group.key === "layout" ? '<button class="category-action" data-layout-action="tidy"><i data-foundry-icon="wand-sparkles"></i><span>Tidy</span></button>' : "";
|
|
5217
|
+
return `<section class="inspector-category property-section ${collapsedSections.has(categoryKey) ? "collapsed" : ""}" data-category="${group.key}" data-section-key="${categoryKey}"><div class="inspector-heading section-head"><button class="section-toggle" aria-expanded="${String(!collapsedSections.has(categoryKey))}"><i data-foundry-icon="${group.icon}"></i><strong>${group.label}</strong></button>${categoryAction}<span class="property-count">${controls.length}</span></div><div class="category-body">${group.showContext ? renderContextPanel(group.category) : ""}${sections}${remainingSection}</div></section>`;
|
|
4458
5218
|
}).join("");
|
|
4459
5219
|
const currentBaseline = baselineForContext(
|
|
4460
5220
|
designMemory,
|
|
@@ -4476,6 +5236,7 @@ function installFoundryInspector(options = {}) {
|
|
|
4476
5236
|
button.setAttribute("aria-expanded", String(!collapsed));
|
|
4477
5237
|
if (collapsed) collapsedSections.add(key);
|
|
4478
5238
|
else collapsedSections.delete(key);
|
|
5239
|
+
sectionPreferenceTouched = true;
|
|
4479
5240
|
sessionStorage.setItem(
|
|
4480
5241
|
"__foundry_collapsed_sections",
|
|
4481
5242
|
JSON.stringify([...collapsedSections])
|
|
@@ -4488,6 +5249,12 @@ function installFoundryInspector(options = {}) {
|
|
|
4488
5249
|
renderControls();
|
|
4489
5250
|
})
|
|
4490
5251
|
);
|
|
5252
|
+
controlsRoot.querySelectorAll("[data-radius-link]").forEach(
|
|
5253
|
+
(button) => button.addEventListener("click", () => {
|
|
5254
|
+
radiusLinked = !radiusLinked;
|
|
5255
|
+
renderControls();
|
|
5256
|
+
})
|
|
5257
|
+
);
|
|
4491
5258
|
indexedControls.forEach(({ control, index }) => {
|
|
4492
5259
|
const matches = matchingTokens(
|
|
4493
5260
|
designGraph?.tokens ?? [],
|
|
@@ -4495,7 +5262,8 @@ function installFoundryInspector(options = {}) {
|
|
|
4495
5262
|
`${control.value}${control.unit ?? ""}`
|
|
4496
5263
|
);
|
|
4497
5264
|
const tokenEligible = control.category === "layout" && /gap|padding|margin/i.test(control.property) || control.category === "typography" && /font|lineHeight|letterSpacing/i.test(control.property) || control.category === "color" && /color|background|fill|stroke/i.test(control.property) || control.category === "effects" && /radius|shadow|blur/i.test(control.property);
|
|
4498
|
-
|
|
5265
|
+
const availableTokens = tokensForCategory(control.category);
|
|
5266
|
+
if (!tokenEligible || !availableTokens.length) return;
|
|
4499
5267
|
const field = controlsRoot.querySelector(`[data-control="${index}"]`);
|
|
4500
5268
|
const label = field?.closest("label");
|
|
4501
5269
|
if (!label) return;
|
|
@@ -4503,14 +5271,27 @@ function installFoundryInspector(options = {}) {
|
|
|
4503
5271
|
"beforeend",
|
|
4504
5272
|
matches.length ? `<span class="token-provenance">Uses ${escapeHtml(matches[0].name)}</span>` : '<span class="token-provenance literal">Literal value \xB7 consider a project token</span>'
|
|
4505
5273
|
);
|
|
4506
|
-
if (!matches.length) return;
|
|
4507
5274
|
label.insertAdjacentHTML(
|
|
4508
5275
|
"beforeend",
|
|
4509
|
-
`<span class="token-
|
|
4510
|
-
(token2) => `<button type="button" class="token-
|
|
4511
|
-
).join("")}</span>`
|
|
5276
|
+
`<span class="token-menu-wrap"><button type="button" class="token-menu-trigger" data-token-menu-trigger="${index}" aria-expanded="false" aria-label="Choose a project token"><i data-foundry-icon="bookmark"></i></button><span class="token-menu" data-token-menu="${index}" hidden>${availableTokens.map(
|
|
5277
|
+
(token2) => `<button type="button" class="token-option" data-token-control="${index}" data-token-value="${escapeHtml(token2.value)}" title="${escapeHtml(token2.value)}">${control.category === "color" ? `<span class="swatch" style="--token-color:${escapeHtml(token2.value)}"></span>` : ""}<span>${escapeHtml(token2.name)}</span><code>${escapeHtml(token2.value)}</code></button>`
|
|
5278
|
+
).join("")}</span></span>`
|
|
4512
5279
|
);
|
|
4513
5280
|
});
|
|
5281
|
+
renderIcons(controlsRoot);
|
|
5282
|
+
controlsRoot.querySelectorAll("[data-token-menu-trigger]").forEach((button) => {
|
|
5283
|
+
button.addEventListener("click", () => {
|
|
5284
|
+
const menu = controlsRoot.querySelector(
|
|
5285
|
+
`[data-token-menu="${button.dataset.tokenMenuTrigger}"]`
|
|
5286
|
+
);
|
|
5287
|
+
if (!menu) return;
|
|
5288
|
+
controlsRoot.querySelectorAll("[data-token-menu]").forEach((candidate2) => {
|
|
5289
|
+
if (candidate2 !== menu) candidate2.hidden = true;
|
|
5290
|
+
});
|
|
5291
|
+
menu.hidden = !menu.hidden;
|
|
5292
|
+
button.setAttribute("aria-expanded", String(!menu.hidden));
|
|
5293
|
+
});
|
|
5294
|
+
});
|
|
4514
5295
|
controlsRoot.querySelectorAll("[data-reset-control]").forEach((button) => {
|
|
4515
5296
|
button.addEventListener("click", () => {
|
|
4516
5297
|
const control = selectedControls[Number(button.dataset.resetControl)];
|
|
@@ -4628,10 +5409,38 @@ function installFoundryInspector(options = {}) {
|
|
|
4628
5409
|
fieldRecorders.get(linkedIndex)?.push(linkedBefore, value);
|
|
4629
5410
|
});
|
|
4630
5411
|
}
|
|
5412
|
+
const radiusProperties = [
|
|
5413
|
+
"borderTopLeftRadius",
|
|
5414
|
+
"borderTopRightRadius",
|
|
5415
|
+
"borderBottomLeftRadius",
|
|
5416
|
+
"borderBottomRightRadius"
|
|
5417
|
+
];
|
|
5418
|
+
if (radiusLinked && (control.property === "borderRadius" || radiusProperties.includes(control.property))) {
|
|
5419
|
+
indexedControls.forEach(({ control: linkedControl, index: linkedIndex }) => {
|
|
5420
|
+
if (linkedControl === control || !radiusProperties.includes(linkedControl.property))
|
|
5421
|
+
return;
|
|
5422
|
+
const linkedBefore = linkedControl.read();
|
|
5423
|
+
linkedControl.apply(value);
|
|
5424
|
+
const linkedField = controlsRoot.querySelector(
|
|
5425
|
+
`[data-control="${linkedIndex}"]`
|
|
5426
|
+
);
|
|
5427
|
+
if (linkedField) linkedField.value = String(value);
|
|
5428
|
+
fieldRecorders.get(linkedIndex)?.push(linkedBefore, value);
|
|
5429
|
+
});
|
|
5430
|
+
}
|
|
4631
5431
|
updateOutline();
|
|
4632
5432
|
recorder.push(before, value);
|
|
4633
5433
|
});
|
|
4634
5434
|
});
|
|
5435
|
+
controlsRoot.querySelectorAll("[data-color-control]").forEach((button) => {
|
|
5436
|
+
button.addEventListener("click", () => {
|
|
5437
|
+
const index = Number(button.dataset.colorControl);
|
|
5438
|
+
const control = selectedControls[index];
|
|
5439
|
+
if (!control) return;
|
|
5440
|
+
activeControlProperty = control.property;
|
|
5441
|
+
openColorEditor(button, index);
|
|
5442
|
+
});
|
|
5443
|
+
});
|
|
4635
5444
|
controlsRoot.querySelectorAll("[data-token-control]").forEach((button) => {
|
|
4636
5445
|
button.addEventListener("click", () => {
|
|
4637
5446
|
const index = Number(button.dataset.tokenControl);
|
|
@@ -4639,8 +5448,16 @@ function installFoundryInspector(options = {}) {
|
|
|
4639
5448
|
`[data-control="${index}"]`
|
|
4640
5449
|
);
|
|
4641
5450
|
const control = selectedControls[index];
|
|
4642
|
-
if (!
|
|
5451
|
+
if (!control) return;
|
|
4643
5452
|
const raw = button.dataset.tokenValue ?? "";
|
|
5453
|
+
if (control.kind === "color") {
|
|
5454
|
+
void applyControlValue(control, raw, "Use project color").then(() => {
|
|
5455
|
+
if (selected) selectedControls = controlsFor(selected);
|
|
5456
|
+
renderControls();
|
|
5457
|
+
});
|
|
5458
|
+
return;
|
|
5459
|
+
}
|
|
5460
|
+
if (!field) return;
|
|
4644
5461
|
field.value = control.kind === "number" ? String(numberFrom(raw)) : raw;
|
|
4645
5462
|
field.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
|
|
4646
5463
|
});
|
|
@@ -4651,6 +5468,8 @@ function installFoundryInspector(options = {}) {
|
|
|
4651
5468
|
}
|
|
4652
5469
|
function select(element, additive = false) {
|
|
4653
5470
|
if (element === host || host.contains(element)) return;
|
|
5471
|
+
colorPopover.hidden = true;
|
|
5472
|
+
activeColor = void 0;
|
|
4654
5473
|
if (additive && selectedElements.includes(element)) {
|
|
4655
5474
|
selectedElements = selectedElements.filter((item) => item !== element);
|
|
4656
5475
|
if (!selectedElements.length) {
|
|
@@ -4666,6 +5485,40 @@ function installFoundryInspector(options = {}) {
|
|
|
4666
5485
|
selectedElements = [element];
|
|
4667
5486
|
selected = element;
|
|
4668
5487
|
}
|
|
5488
|
+
if (!sectionPreferenceTouched) {
|
|
5489
|
+
const categoryKeys = [
|
|
5490
|
+
"position",
|
|
5491
|
+
"layout",
|
|
5492
|
+
"typography",
|
|
5493
|
+
"appearance",
|
|
5494
|
+
"fill",
|
|
5495
|
+
"stroke",
|
|
5496
|
+
"effects",
|
|
5497
|
+
"content",
|
|
5498
|
+
"accessibility",
|
|
5499
|
+
"motion"
|
|
5500
|
+
];
|
|
5501
|
+
categoryKeys.forEach((key) => collapsedSections.add(`category:${key}`));
|
|
5502
|
+
collapsedSections.delete("category:position");
|
|
5503
|
+
collapsedSections.delete("category:layout");
|
|
5504
|
+
collapsedSections.delete("category:appearance");
|
|
5505
|
+
collapsedSections.delete("category:fill");
|
|
5506
|
+
const isText = element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element.children.length === 0 && Boolean(element.textContent?.trim());
|
|
5507
|
+
const isMedia = element instanceof HTMLImageElement;
|
|
5508
|
+
if (isText) {
|
|
5509
|
+
collapsedSections.delete("category:typography");
|
|
5510
|
+
collapsedSections.delete("category:content");
|
|
5511
|
+
}
|
|
5512
|
+
if (isMedia) {
|
|
5513
|
+
collapsedSections.delete("category:content");
|
|
5514
|
+
collapsedSections.delete("category:accessibility");
|
|
5515
|
+
}
|
|
5516
|
+
if (Number.parseFloat(getComputedStyle(element).borderWidth) > 0)
|
|
5517
|
+
collapsedSections.delete("category:stroke");
|
|
5518
|
+
if (getComputedStyle(element).boxShadow !== "none" || Number.parseFloat(getComputedStyle(element).borderRadius) > 0)
|
|
5519
|
+
collapsedSections.delete("category:effects");
|
|
5520
|
+
if (element.getAnimations().length) collapsedSections.delete("category:motion");
|
|
5521
|
+
}
|
|
4669
5522
|
selectedControls = controlsFor(element);
|
|
4670
5523
|
const target = targetFor(element);
|
|
4671
5524
|
panel.classList.add("has-selection");
|
|
@@ -4702,6 +5555,8 @@ function installFoundryInspector(options = {}) {
|
|
|
4702
5555
|
});
|
|
4703
5556
|
}
|
|
4704
5557
|
function clearSelection() {
|
|
5558
|
+
colorPopover.hidden = true;
|
|
5559
|
+
activeColor = void 0;
|
|
4705
5560
|
selected = null;
|
|
4706
5561
|
selectedElements = [];
|
|
4707
5562
|
selectedControls = [];
|
|
@@ -5067,25 +5922,25 @@ function installFoundryInspector(options = {}) {
|
|
|
5067
5922
|
method: "POST",
|
|
5068
5923
|
body: JSON.stringify({ runId, results })
|
|
5069
5924
|
});
|
|
5070
|
-
if (workspaceState.
|
|
5925
|
+
if (workspaceState.reviewOpen) renderReviewPayload(payload);
|
|
5071
5926
|
showToast(
|
|
5072
5927
|
`${results.filter((result) => result.passed).length}/${results.length} changes verified`
|
|
5073
5928
|
);
|
|
5074
5929
|
}
|
|
5075
5930
|
function installPanelResizer() {
|
|
5076
5931
|
const resizer = shadow.querySelector(".panel-resizer");
|
|
5077
|
-
const savedWidth = Number(localStorage.getItem("__foundry_panel_width"));
|
|
5078
|
-
if (Number.isFinite(savedWidth) && savedWidth >= 300) panel.style.width = `${savedWidth}px`;
|
|
5079
5932
|
resizer.addEventListener("pointerdown", (event) => {
|
|
5080
|
-
if (event.button !== 0) return;
|
|
5933
|
+
if (event.button !== 0 || window.matchMedia("(max-width: 680px)").matches) return;
|
|
5081
5934
|
const startX = event.clientX;
|
|
5082
|
-
const startWidth =
|
|
5935
|
+
const startWidth = dockWidth;
|
|
5083
5936
|
resizer.setPointerCapture(event.pointerId);
|
|
5084
5937
|
event.preventDefault();
|
|
5085
5938
|
const move = (pointer) => {
|
|
5086
5939
|
const maximum = Math.min(520, window.innerWidth - 24);
|
|
5087
|
-
const next = Math.max(
|
|
5088
|
-
|
|
5940
|
+
const next = Math.max(340, Math.min(maximum, startWidth + startX - pointer.clientX));
|
|
5941
|
+
dockWidth = Math.round(next);
|
|
5942
|
+
host.style.setProperty("--fdc-dock-width", `${dockWidth}px`);
|
|
5943
|
+
positionWorkspaceSurfaces();
|
|
5089
5944
|
};
|
|
5090
5945
|
const finish = (pointer) => {
|
|
5091
5946
|
resizer.removeEventListener("pointermove", move);
|
|
@@ -5093,10 +5948,8 @@ function installFoundryInspector(options = {}) {
|
|
|
5093
5948
|
resizer.removeEventListener("pointercancel", finish);
|
|
5094
5949
|
if (resizer.hasPointerCapture(pointer.pointerId))
|
|
5095
5950
|
resizer.releasePointerCapture(pointer.pointerId);
|
|
5096
|
-
localStorage.setItem(
|
|
5097
|
-
|
|
5098
|
-
String(Math.round(panel.getBoundingClientRect().width))
|
|
5099
|
-
);
|
|
5951
|
+
localStorage.setItem(dockWidthKey, String(dockWidth));
|
|
5952
|
+
localStorage.removeItem(legacyDockWidthKey);
|
|
5100
5953
|
};
|
|
5101
5954
|
resizer.addEventListener("pointermove", move);
|
|
5102
5955
|
resizer.addEventListener("pointerup", finish);
|
|
@@ -5232,6 +6085,16 @@ function installFoundryInspector(options = {}) {
|
|
|
5232
6085
|
}
|
|
5233
6086
|
});
|
|
5234
6087
|
statusPopover.querySelector("[data-status-retry]").addEventListener("click", () => void hydrateSession());
|
|
6088
|
+
interfaceThemeTrigger.addEventListener("click", () => {
|
|
6089
|
+
if (interfaceThemeMenu.hidden) openInterfaceThemeMenu();
|
|
6090
|
+
else closeInterfaceThemeMenu();
|
|
6091
|
+
});
|
|
6092
|
+
interfaceThemeMenu.querySelectorAll("[data-interface-theme-choice]").forEach(
|
|
6093
|
+
(button) => button.addEventListener("click", () => {
|
|
6094
|
+
applyInterfaceTheme(button.dataset.interfaceThemeChoice);
|
|
6095
|
+
closeInterfaceThemeMenu(true);
|
|
6096
|
+
})
|
|
6097
|
+
);
|
|
5235
6098
|
changeDock.querySelector(".dock-undo")?.addEventListener("click", () => void replayHistory(-1));
|
|
5236
6099
|
changeDock.querySelector(".dock-review")?.addEventListener("click", () => void openReview());
|
|
5237
6100
|
onboarding.querySelector(".onboarding-start")?.addEventListener("click", () => {
|
|
@@ -5332,13 +6195,14 @@ function installFoundryInspector(options = {}) {
|
|
|
5332
6195
|
);
|
|
5333
6196
|
comparisonStage.querySelector("[data-close-comparison-stage]").addEventListener("click", closeSplitComparison);
|
|
5334
6197
|
const commandInput = commandPalette.querySelector("input");
|
|
6198
|
+
commandPalette.querySelector(".close-commands")?.addEventListener("click", () => closeCommands());
|
|
5335
6199
|
commandInput.addEventListener("input", () => renderCommands(commandInput.value));
|
|
5336
6200
|
commandInput.addEventListener("keydown", (event) => {
|
|
5337
6201
|
if (event.key === "Enter") {
|
|
5338
6202
|
const active = commandPalette.querySelector(".command-item.active");
|
|
5339
6203
|
if (active) runCommand(active.dataset.command);
|
|
5340
6204
|
}
|
|
5341
|
-
if (event.key === "Escape")
|
|
6205
|
+
if (event.key === "Escape") closeCommands();
|
|
5342
6206
|
});
|
|
5343
6207
|
shadow.querySelector(".close")?.addEventListener("click", destroyInspector);
|
|
5344
6208
|
shadow.querySelector(".open-workbench")?.addEventListener("click", openWorkbench);
|
|
@@ -5364,7 +6228,7 @@ function installFoundryInspector(options = {}) {
|
|
|
5364
6228
|
shadow.querySelector(".redo")?.addEventListener("click", () => void replayHistory(1));
|
|
5365
6229
|
shadow.querySelector(".align")?.addEventListener("click", () => void alignSelected());
|
|
5366
6230
|
shadow.querySelector(".distribute")?.addEventListener("click", () => void distributeSelected());
|
|
5367
|
-
shadow.querySelector(".review-back")?.addEventListener("click", closeReview);
|
|
6231
|
+
shadow.querySelector(".review-back")?.addEventListener("click", () => closeReview());
|
|
5368
6232
|
reviewCancel.addEventListener("click", () => {
|
|
5369
6233
|
if (reviewCancel.dataset.action === "cancel") void cancelRun();
|
|
5370
6234
|
else closeReview();
|
|
@@ -5374,6 +6238,25 @@ function installFoundryInspector(options = {}) {
|
|
|
5374
6238
|
if (applyButton.dataset.action === "retry") void retryRun();
|
|
5375
6239
|
});
|
|
5376
6240
|
function handleGlobalShortcuts(event) {
|
|
6241
|
+
if (workspaceState.reviewOpen && event.key === "Tab") {
|
|
6242
|
+
const focusable = [
|
|
6243
|
+
...reviewModal.querySelectorAll(
|
|
6244
|
+
'button:not(:disabled),input:not(:disabled),select:not(:disabled),textarea:not(:disabled),[tabindex]:not([tabindex="-1"])'
|
|
6245
|
+
)
|
|
6246
|
+
].filter((element) => element.offsetParent !== null);
|
|
6247
|
+
if (focusable.length) {
|
|
6248
|
+
const first = focusable[0];
|
|
6249
|
+
const last = focusable.at(-1);
|
|
6250
|
+
if (event.shiftKey && shadow.activeElement === first) {
|
|
6251
|
+
event.preventDefault();
|
|
6252
|
+
last.focus();
|
|
6253
|
+
} else if (!event.shiftKey && shadow.activeElement === last) {
|
|
6254
|
+
event.preventDefault();
|
|
6255
|
+
first.focus();
|
|
6256
|
+
}
|
|
6257
|
+
}
|
|
6258
|
+
return;
|
|
6259
|
+
}
|
|
5377
6260
|
const editable = event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement || event.target instanceof HTMLSelectElement || event.target instanceof HTMLElement && event.target.isContentEditable;
|
|
5378
6261
|
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
|
|
5379
6262
|
event.preventDefault();
|
|
@@ -5412,13 +6295,22 @@ function installFoundryInspector(options = {}) {
|
|
|
5412
6295
|
return;
|
|
5413
6296
|
}
|
|
5414
6297
|
if (event.key !== "Escape") return;
|
|
5415
|
-
if (
|
|
5416
|
-
|
|
6298
|
+
if (workspaceState.reviewOpen) {
|
|
6299
|
+
const nested = reviewModal.querySelector("details[open]");
|
|
6300
|
+
if (nested) nested.open = false;
|
|
6301
|
+
else closeReview();
|
|
6302
|
+
return;
|
|
6303
|
+
}
|
|
6304
|
+
if (!interfaceThemeMenu.hidden) closeInterfaceThemeMenu(true);
|
|
6305
|
+
else if (!colorPopover.hidden) {
|
|
6306
|
+
colorPopover.hidden = true;
|
|
6307
|
+
activeColor = void 0;
|
|
6308
|
+
} else if (!comparisonStage.hidden) closeSplitComparison();
|
|
6309
|
+
else if (!commandPalette.hidden) closeCommands();
|
|
5417
6310
|
else if (!libraryPanel.hidden) closeDesignMemory();
|
|
5418
6311
|
else if (!healthPanel.hidden) closeHealth();
|
|
5419
6312
|
else if (comparisonActive) closeComparison();
|
|
5420
6313
|
else if (!workbench.hidden) closeWorkbench();
|
|
5421
|
-
else if (workspaceState.tray === "expanded") closeReview();
|
|
5422
6314
|
else clearSelection();
|
|
5423
6315
|
}
|
|
5424
6316
|
document.addEventListener("keydown", handleGlobalShortcuts);
|
|
@@ -5452,6 +6344,8 @@ function installFoundryInspector(options = {}) {
|
|
|
5452
6344
|
cancelAnimationFrame(layerScrollFrame);
|
|
5453
6345
|
cancelAnimationFrame(mutationFrame);
|
|
5454
6346
|
layerMutationObserver.disconnect();
|
|
6347
|
+
systemDarkTheme.removeEventListener("change", handleSystemThemeChange);
|
|
6348
|
+
document.removeEventListener("pointerdown", handleInterfaceThemeDismiss, true);
|
|
5455
6349
|
clearInterval(reviewPoll);
|
|
5456
6350
|
document.removeEventListener("click", handlePointer, true);
|
|
5457
6351
|
document.removeEventListener("dblclick", handleTextEdit, true);
|