From f4882c53c29a4f6b45be0f270410a54c58183f88 Mon Sep 17 00:00:00 2001 From: Dmitriy Petrov Date: Mon, 31 Aug 2026 02:26:24 +0300 Subject: [PATCH] Improve device tag editing and version clients --- src/shared/versions.ts | 4 +- src/web/features/devices/DevicesPanel.tsx | 165 +++++++++++++-------- src/web/styles/features/devices.css | 101 +++++++++---- src/web/styles/themes.css | 5 + test/web/device-inventory-contract.test.js | 20 ++- test/web/style-boundaries.test.js | 28 ++-- 6 files changed, 215 insertions(+), 108 deletions(-) diff --git a/src/shared/versions.ts b/src/shared/versions.ts index 68cee62..6e560be 100644 --- a/src/shared/versions.ts +++ b/src/shared/versions.ts @@ -1,6 +1,6 @@ export const HARBOR_VERSIONS = Object.freeze({ - macClient: '0.33.0', - gatewayClient: '0.34.0', + macClient: '0.33.1', + gatewayClient: '0.34.1', gatewayBackend: '0.34.0', }); diff --git a/src/web/features/devices/DevicesPanel.tsx b/src/web/features/devices/DevicesPanel.tsx index fe6289f..6db2102 100644 --- a/src/web/features/devices/DevicesPanel.tsx +++ b/src/web/features/devices/DevicesPanel.tsx @@ -61,7 +61,6 @@ type DeviceCopyField = 'IP' | 'MAC' | 'Hostname'; interface TagPopoverState { deviceId: string; - title: string; baseline: string[]; draft: string[]; catalogKey: string; @@ -137,6 +136,8 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) { const [tagPopover, setTagPopover] = useState(null); const [tagPopoverClosing, setTagPopoverClosing] = useState(false); const [tagAnnouncement, setTagAnnouncement] = useState(''); + const [tagMutationCycle, setTagMutationCycle] = useState(0); + const [addingTag, setAddingTag] = useState(false); const [newTagName, setNewTagName] = useState(''); const [editingTagId, setEditingTagId] = useState(''); const [editingTagName, setEditingTagName] = useState(''); @@ -152,6 +153,7 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) { const copyAttempts = useRef(new Map()); const trafficDeltaTimer = useRef | null>(null); const tagPopoverCloseTimer = useRef | null>(null); + const tagMutationPending = useRef(false); const railModeAnimation = useRef(null); const railModeRequest = useRef(0); const trafficOrder = useRef<{ direction: 'asc' | 'desc'; ids: string[] }>({ direction: sortDirection, ids: [] }); @@ -162,6 +164,7 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) { const popoverRef = useRef(null); const searchRef = useRef(null); const managerInputRef = useRef(null); + const addTagButtonRef = useRef(null); const tagTriggerRefs = useRef(new Map()); const restoreTagTriggerFocus = useRef(true); const editingTagBaseline = useRef(''); @@ -212,7 +215,7 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) { useEffect(() => { const knownTagIds = new Set(tags.map(({ id }) => id)); setSelectedTagIds((current) => current.filter((id) => knownTagIds.has(id))); - if (!tagPopover) return; + if (!tagPopover || tagMutationPending.current || tagSavingId === tagPopover.deviceId) return; const device = allDevices.find(({ id }) => id === tagPopover.deviceId); const baselineChanged = !device || device.tagIds.length !== tagPopover.baseline.length || device.tagIds.some((id, index) => id !== tagPopover.baseline[index]); @@ -220,19 +223,21 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) { if (!baselineChanged && !catalogChanged) return; closeTagPopover(); setTagAnnouncement('Список тегов изменился. Откройте теги устройства снова.'); - }, [snapshot?.tags, snapshot?.devices]); + }, [snapshot?.tags, snapshot?.devices, tagSavingId, tagMutationCycle]); useEffect(() => { if (open) return; restoreTagTriggerFocus.current = false; setMobileRailOpen(false); setRailMode('filters'); + setAddingTag(false); + setEditingTagId(''); closeTagPopover(true, false); }, [open]); useEffect(() => { if (railMode !== 'manager' || tagPopover || (compactRail && !mobileRailOpen)) return undefined; - const frame = requestAnimationFrame(() => managerInputRef.current?.focus()); + const frame = requestAnimationFrame(() => (managerInputRef.current || addTagButtonRef.current)?.focus()); return () => cancelAnimationFrame(frame); }, [compactRail, mobileRailOpen, railMode, tagPopover]); @@ -285,7 +290,7 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) { if (layoutRef.current) layoutRef.current.inert = true; if (closeRef.current) closeRef.current.inert = true; const frame = requestAnimationFrame(() => { - const assigned = popoverRef.current?.querySelector('input:checked'); + const assigned = popoverRef.current?.querySelector('button[aria-pressed="true"]'); const first = popoverRef.current?.querySelector(FOCUSABLE); (assigned || first)?.focus(); }); @@ -549,6 +554,7 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) { function closeTagPopover(immediate = false, restoreFocus = true) { if (!tagPopover) return; + if (tagMutationPending.current && !immediate) return; restoreTagTriggerFocus.current = restoreFocus; if (tagPopoverCloseTimer.current) clearTimeout(tagPopoverCloseTimer.current); if (immediate || window.matchMedia('(prefers-reduced-motion: reduce)').matches) { @@ -602,7 +608,8 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) { }); } - function openTagPopover(device: Device, title: string, anchor: DOMRect) { + function openTagPopover(device: Device, anchor: DOMRect) { + if (tagMutationPending.current) return; if (tagPopoverCloseTimer.current) clearTimeout(tagPopoverCloseTimer.current); clearTagError(); setTagAnnouncement(''); @@ -610,7 +617,6 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) { setTagPopoverClosing(false); setTagPopover({ deviceId: device.id, - title, baseline: [...device.tagIds], draft: [...device.tagIds], catalogKey: tags.map(({ id, name }) => `${id}:${name}`).join('|'), @@ -618,11 +624,34 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) { }); } - async function saveDeviceTags() { - if (!tagPopover) return; + async function toggleDeviceTag(tagId: string) { + if (!tagPopover || tagMutationPending.current) return; const device = allDevices.find(({ id }) => id === tagPopover.deviceId); if (!device) return; - if (await updateDeviceTags(device, tagPopover.draft, tagPopover.baseline)) closeTagPopover(); + const baseline = [...tagPopover.baseline]; + const selected = tagPopover.draft.includes(tagId); + const selectedIds = selected + ? tagPopover.draft.filter((id) => id !== tagId) + : [...tagPopover.draft, tagId]; + const next = tags.filter(({ id }) => selectedIds.includes(id)).map(({ id }) => id); + if (next.length > 8) return; + clearTagError(); + setTagPopover((current) => current && ({ ...current, draft: next })); + tagMutationPending.current = true; + try { + if (await updateDeviceTags(device, next, baseline)) { + setTagPopover((current) => current?.deviceId === device.id + ? { ...current, baseline: next, draft: next } + : current); + } else { + setTagPopover((current) => current?.deviceId === device.id + ? { ...current, draft: baseline } + : current); + } + } finally { + tagMutationPending.current = false; + setTagMutationCycle((cycle) => cycle + 1); + } } function showTagManager() { @@ -640,7 +669,8 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) { if (!newTagName.trim() || !await createTag(newTagName)) return; setTagErrorCopy(''); setNewTagName(''); - requestAnimationFrame(() => managerInputRef.current?.focus()); + setAddingTag(false); + requestAnimationFrame(() => addTagButtonRef.current?.focus()); } async function submitTagRename(tag: DeviceTag) { @@ -766,23 +796,12 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) { onClick={() => { clearTagError(); setTagErrorCopy(''); + setAddingTag(false); + setEditingTagId(''); void changeRailMode('filters'); }} >Назад

Управление тегами

-
- = 32 || tagSavingId === 'create'} - onChange={(event) => setNewTagName(event.target.value)} - /> - -
- {Boolean(tagError) && tagErrorCopy &&

{tagErrorCopy}

}
{tags.map((tag) =>
{editingTagId === tag.id ?
{ @@ -802,18 +821,27 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) {
: <> {tag.name}{counts.byTag[tag.id] || 0} + > + > }
)} - {!tags.length &&

Создайте первый тег, чтобы распределить устройства.

} + {!tags.length && !addingTag &&

Тегов пока нет.

}
+ {addingTag ?
+ = 32 || tagSavingId === 'create'} + onChange={(event) => setNewTagName(event.target.value)} + /> + + +
: } + {Boolean(tagError) && tagErrorCopy &&

{tagErrorCopy}

} } @@ -1093,8 +1156,8 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) { data-tag-tone={firstTag ? tagTone(firstTag.id) : undefined} type="button" aria-label={`Изменить теги устройства ${title}`} - disabled={tagSavingId === device.id} - onClick={(event) => openTagPopover(device, title, event.currentTarget.getBoundingClientRect())} + disabled={Boolean(tagSavingId)} + onClick={(event) => openTagPopover(device, event.currentTarget.getBoundingClientRect())} > {firstTag ? <>{firstTag.name}{deviceTags.length > 1 && +{deviceTags.length - 1}} : '+ тег'} } @@ -1234,44 +1297,30 @@ export function DevicesPanel({ feature }: { feature: DevicesFeature }) { className="client-device-tag-popover" role="dialog" aria-modal="true" - aria-label={`Теги устройства ${tagPopover.title}`} + aria-label="Теги" aria-busy={tagSavingId === tagPopover.deviceId} style={{ top: `${Math.max(12, Math.min(tagPopover.anchor.bottom + 8, window.innerHeight - 360))}px`, left: `${Math.max(12, Math.min(tagPopover.anchor.left, window.innerWidth - 292))}px`, }} > -

Теги устройства {tagPopover.title}

{tags.length ?
{tags.map((tag) => { - const checked = tagPopover.draft.includes(tag.id); - return ; + const selected = tagPopover.draft.includes(tag.id); + return ; })} -
:

Тегов пока нет.

} - {Boolean(tagError) &&

Не удалось сохранить теги.

} -
- {!tags.length && } - - -
+ : } + {Boolean(tagError) &&

Не удалось изменить тег.

} , document.querySelector('.app.client-app') || document.body, diff --git a/src/web/styles/features/devices.css b/src/web/styles/features/devices.css index cc33d6c..be5fa27 100644 --- a/src/web/styles/features/devices.css +++ b/src/web/styles/features/devices.css @@ -33,9 +33,7 @@ text-transform: var(--type-label-transform); } -.client-devices-search input, -.client-devices-tag-create input, -.client-devices-tag-manager-row input { +.client-devices-search input { min-width: 0; height: 32px; box-sizing: border-box; @@ -50,12 +48,36 @@ text-transform: var(--type-control-transform); } -.client-devices-search input:focus, -.client-devices-tag-create input:focus, -.client-devices-tag-manager-row input:focus { +.client-devices-search input:focus { border: 1px solid var(--client-accent); } +.client-devices-tag-create input, +.client-devices-tag-manager-row input { + min-width: 0; + height: 32px; + box-sizing: border-box; + padding: 0; + border: 0; + border-bottom: 1px solid var(--client-border); + border-radius: 0; + outline: 0; + background: transparent; + caret-color: var(--client-accent); + color: var(--client-text); + font: var(--type-control); + letter-spacing: var(--type-control-tracking); + text-transform: var(--type-control-transform); + transition: color 180ms ease, border-bottom-color 180ms ease, box-shadow 220ms ease, text-shadow 220ms ease; +} + +.client-devices-tag-create input:focus-visible, +.client-devices-tag-manager-row input:focus-visible { + border-bottom-color: var(--client-accent); + box-shadow: 0 1px 0 color-mix(in oklch, var(--client-accent) 58%, transparent); + text-shadow: 0 0 8px color-mix(in oklch, var(--client-accent) 32%, transparent); +} + .client-devices-filter-group, .client-devices-tag-filters { display: grid; @@ -125,7 +147,8 @@ .client-devices-manage-tags, .client-devices-manager-back, .client-devices-tag-create button, -.client-devices-tag-manager-row button, +.client-devices-tag-manager-row form button, +.client-devices-tag-edit, .client-device-tag-popover button, .client-devices-filter-summary button, .client-devices-filter-empty button { @@ -152,7 +175,8 @@ .client-devices-tag-create { display: grid; - grid-template-columns: minmax(0, 1fr) auto; + grid-template-columns: minmax(0, 1fr) auto auto; + align-items: center; gap: 6px; } @@ -184,6 +208,9 @@ height: 26px; display: grid; place-items: center; +} + +.client-devices-tag-manager-row > button:not(.client-row-delete) { color: var(--client-muted); } @@ -197,6 +224,15 @@ stroke-linejoin: round; } +.client-devices-tag-edit:hover:not(:disabled), +.client-devices-tag-edit:focus-visible { + color: var(--client-accent); +} + +.client-devices-tag-edit.is-writing svg { + animation: client-device-pencil-write 620ms cubic-bezier(0.16, 1, 0.3, 1) both; +} + .client-devices-tag-manager-row form { grid-column: 1 / -1; display: grid; @@ -1703,14 +1739,7 @@ to { opacity: 0; transform: translateY(-6px); } } -.client-device-tag-popover h3 { - margin: 0; - font: var(--type-section-title); - letter-spacing: var(--type-section-title-tracking); - text-transform: var(--type-section-title-transform); -} - -.client-device-tag-popover > p { +.client-device-tag-popover > p:not(.client-devices-tag-error) { margin: 0; color: var(--client-muted); font: var(--type-control); @@ -1719,30 +1748,42 @@ } .client-device-tag-popover-options { - display: grid; - gap: 4px; + display: flex; + flex-wrap: wrap; + gap: 5px; } -.client-device-tag-popover-options label { - min-height: 30px; - display: flex; - align-items: center; - gap: 8px; - color: var(--tag-tone, var(--client-text)); +.client-device-tag-popover-options button { + min-height: 28px; + padding: 4px 7px; + border: 0; + border-radius: 5px; + background: transparent; + color: var(--client-muted); font: var(--type-control); letter-spacing: var(--type-control-tracking); text-transform: var(--type-control-transform); cursor: pointer; + transition: color 180ms ease, background 180ms ease, filter 260ms ease, opacity 180ms ease; } -.client-device-tag-popover-actions { - display: flex; - justify-content: flex-end; - gap: 12px; +.client-device-tag-popover-options button:hover:not(:disabled), +.client-device-tag-popover-options button:focus-visible, +.client-device-tag-popover-options button[aria-pressed="true"] { + background: color-mix(in oklch, var(--tag-tone) 10%, transparent); + color: var(--tag-tone); + filter: drop-shadow(0 0 5px color-mix(in oklch, var(--tag-tone) 32%, transparent)); } -.client-device-tag-popover-actions button:last-child { - color: var(--client-text); +.client-device-tag-popover-options button:disabled, +.client-device-tag-popover-options button[aria-disabled="true"] { + cursor: default; + opacity: 0.38; +} + +.client-device-tag-popover-options button[aria-pressed="true"]:disabled, +.client-device-tag-popover-options button[aria-pressed="true"][aria-disabled="true"] { + opacity: 0.78; } @media (max-width: 640px) { diff --git a/src/web/styles/themes.css b/src/web/styles/themes.css index c860b29..9bd872f 100644 --- a/src/web/styles/themes.css +++ b/src/web/styles/themes.css @@ -93,7 +93,12 @@ .client-devices-rail, .client-devices-rail-backdrop, .client-devices-rail-view, + .client-devices-tag-create input, + .client-devices-tag-manager-row input, + .client-devices-tag-edit, + .client-devices-tag-edit svg, .client-device-tag-popover, + .client-device-tag-popover-options button, .client-text-morph-value, .client-proxy-label > span { transition: none; diff --git a/test/web/device-inventory-contract.test.js b/test/web/device-inventory-contract.test.js index 572dc5b..f312b0d 100644 --- a/test/web/device-inventory-contract.test.js +++ b/test/web/device-inventory-contract.test.js @@ -243,16 +243,22 @@ test('device tags keep filtering, assignment and management accessible at every assert.match(panel, /deviceFilterCounts\(allDevices, tags\)/); assert.match(panel, /По этим фильтрам устройств нет\.[\s\S]*Сбросить фильтры/); assert.match(panel, /deviceTags = tags\.filter[\s\S]*client-device-tag-trigger[\s\S]*\+\{deviceTags\.length - 1\}[\s\S]*'\+ тег'/); - assert.match(panel, /role="dialog"[\s\S]*aria-modal="true"[\s\S]*Теги устройства \{tagPopover\.title\}/); + assert.match(panel, /role="dialog"[\s\S]*aria-modal="true"[\s\S]*aria-label="Теги"/); + assert.match(panel, /client-device-tag-popover-options[\s\S]*aria-pressed=\{selected\}[\s\S]*toggleDeviceTag\(tag\.id\)/); + assert.match(panel, /tagMutationPending = useRef\(false\)[\s\S]*if \(!tagPopover \|\| tagMutationPending\.current \|\| tagSavingId === tagPopover\.deviceId\) return[\s\S]*if \(tagMutationPending\.current && !immediate\) return[\s\S]*if \(!tagPopover \|\| tagMutationPending\.current\) return/); + assert.match(panel, /current\?\.deviceId === device\.id[\s\S]*baseline: next, draft: next[\s\S]*current\?\.deviceId === device\.id[\s\S]*draft: baseline[\s\S]*tagMutationPending\.current = false[\s\S]*setTagMutationCycle/); + assert.match(panel, /aria-disabled=\{Boolean\(tagSavingId\)[\s\S]*if \(tagSavingId \|\| \(!selected && tagPopover\.draft\.length >= 8\)\) return/); + assert.doesNotMatch(panel, /client-device-tag-popover-actions|type="checkbox"|

Теги устройства/); assert.match(panel, /contentRef\.current\.inert = true[\s\S]*layoutRef\.current\.inert = true/); assert.match(panel, /FOCUSABLE[\s\S]*event\.key === 'Escape'[\s\S]*document\.activeElement === first/); assert.match(panel, /catalogChanged[\s\S]*Список тегов изменился\. Откройте теги устройства снова\./); assert.match(panel, /Теги доступны после обновления Gateway\./); assert.match(panel, /Управление тегами[\s\S]*Переименовать тег[\s\S]*Удалить тег/); assert.match(panel, /placeholder="Найти устройство"[\s\S]*Результатов: \{devices\.length\}/); - assert.match(panel, />Назад<\/button>[\s\S]*Не удалось сохранить теги\./); + assert.match(panel, />Назад<\/button>[\s\S]*\+ Добавить тег/); + assert.match(panel, /Не удалось изменить тег\./); assert.match(panel, /restoreTagTriggerFocus[\s\S]*searchRef\.current,[\s\S]*filterButtonRef\.current,[\s\S]*candidate\?\.isConnected && !candidate\.closest\('\[inert\]'\)/); - assert.match(panel, /updateDeviceTags\(device, tagPopover\.draft, tagPopover\.baseline\)/); + assert.match(panel, /setTagPopover\(\(current\) => current && \(\{ \.\.\.current, draft: next \}\)\)[\s\S]*updateDeviceTags\(device, next, baseline\)[\s\S]*baseline: next, draft: next[\s\S]*draft: baseline/); assert.match(feature, /updateDeviceTags\(device: Device, tagIds: string\[\], baselineTagIds: string\[\]\)[\s\S]*sameStringList\(currentDevice\.tagIds, baselineTagIds\)[\s\S]*sameStringList\(latestDevice\.tagIds, baselineTagIds\)/); assert.match(feature, /renameTag\(tag: DeviceTag, name: string, baselineName: string\)[\s\S]*snapshot\.tags\.find[\s\S]*name !== baselineName[\s\S]*latestTag\.name !== baselineName/); assert.match(panel, /Тег больше не будет доступен для назначения\. Устройства и маршруты не изменятся\./); @@ -265,9 +271,15 @@ test('device tags keep filtering, assignment and management accessible at every assert.match(styles, /client-device-tag-popover-in 160ms ease[\s\S]*translateY\(-6px\)[\s\S]*client-device-tag-popover-out 160ms ease forwards/); assert.match(styles, /client-devices-rail-backdrop \{[\s\S]*opacity: 0[\s\S]*opacity 260ms cubic-bezier\(0\.16, 1, 0\.3, 1\)[\s\S]*client-devices-rail-backdrop\.is-open[\s\S]*opacity: 1/); assert.match(styles, /client-device-tag-trigger \{[\s\S]*height: 14px[\s\S]*flex: 0 0 auto/); + assert.match(styles, /\.client-devices-tag-create input,[\s\S]*\.client-devices-tag-manager-row input \{[\s\S]*border: 0;[\s\S]*border-bottom: 1px solid var\(--client-border\);[\s\S]*border-radius: 0;[\s\S]*background: transparent/); + assert.match(panel, /client-devices-tag-edit[\s\S]*is-writing[\s\S]*client-row-delete client-devices-tag-delete[\s\S]*client-row-delete-lid/); + assert.match(styles, /\.client-devices-tag-manager-row form button,\n\.client-devices-tag-edit,\n\.client-device-tag-popover button,/); + assert.match(styles, /\.client-devices-manage-tags,[\s\S]*\.client-devices-filter-empty button \{[\s\S]*border: 0;[\s\S]*background: transparent/); + assert.match(styles, /\.client-devices-tag-edit\.is-writing svg \{[\s\S]*client-device-pencil-write 620ms/); + assert.match(styles, /\.client-device-tag-popover-options button \{[\s\S]*color: var\(--client-muted\)[\s\S]*button\[aria-pressed="true"\] \{[\s\S]*color: var\(--tag-tone\)[\s\S]*drop-shadow/); assert.match(styles, /@media \(hover: hover\)[\s\S]*client-device-tag-trigger:not\(\.has-tag\)[\s\S]*opacity: 0/); assert.match(styles, /@media \(hover: none\)[\s\S]*client-device-tag-trigger:not\(\.has-tag\)[\s\S]*opacity: 1/); - assert.match(styles, /prefers-reduced-motion: reduce[\s\S]*client-devices-rail[\s\S]*client-devices-rail-view[\s\S]*client-device-tag-popover/); + assert.match(styles, /prefers-reduced-motion: reduce[\s\S]*client-devices-rail[\s\S]*client-devices-rail-view[\s\S]*client-devices-tag-create input[\s\S]*client-devices-tag-edit[\s\S]*client-device-tag-popover/); }); test('Gateway Home reuses the canonical device snapshot for applied route and global traffic', () => { diff --git a/test/web/style-boundaries.test.js b/test/web/style-boundaries.test.js index 3d6c10c..adfdd4a 100644 --- a/test/web/style-boundaries.test.js +++ b/test/web/style-boundaries.test.js @@ -39,26 +39,26 @@ const expectedImports = [ const sha256 = (value) => crypto.createHash('sha256').update(value).digest('hex'); const acceptedLedger = { counts: { - cascadeEdges: 1093, + cascadeEdges: 1120, customProperties: 115, - declarations: 4433, + declarations: 4455, important: 0, keyframes: 52, media: 19, - rules: 1188, - variableReferences: 1130, + rules: 1193, + variableReferences: 1138, }, hashes: { - cascadeEdges: '6676dd45ca19923561ef30b2057a9952a795fd142d83b612aaba70132378e91d', + cascadeEdges: 'd8eeaf20637638f97dd826ae7486469367fb7f5c7e2d32a6fb1d64647e8b52e0', customProperties: '81d1e70737ae5a8e4b20d4c1be1b24685975a404863444da4a8821be1f0d5097', - declarations: 'fcecaa0fb211a14826577bf9629e261b0b4b63b4601febebc43e8d757f6a07f1', + declarations: '89b645d94c44f0dcfea1a7c5f653a8124f0c87057a9898437419ddce813271d2', duplicateKeyframes: '4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945', duplicateSelectors: '8982145dba05b33bf1c93b2d54cfbe76305b276ff3c657aa020144016ada5848', keyframes: '405688c9a452aa9d54e9c50dd30abb13143fdb5910f63f4474ecefcc800311e6', - ruleDeclarationSequences: '231bd9880d19c8d29e6d9538ccb934e135bd58b33155175e317b743916d93ef7', - selectors: '159b53f5cdd6c4aa6d892b9ed314d9a100345290d4c95e31a4c6be98b609c8b6', - variableReferences: 'd8f4abdb1ea6e9077283f342efef0ea920068165d0fc2ac4e7a9a22e16b31461', - witnesses: 'a84c2ee1c55cb68873f62f230aeec39f88093bcbb50493aaf47d852b13e6985c', + ruleDeclarationSequences: '12a5a2deec8a8521f49551d9a29c1950a01bce225f1b387def47304fcfe1c960', + selectors: 'fa7cc612da0fc1e3804a884032e9ab99e0bb0776b29a9e40d44a2891de198067', + variableReferences: '1cbaf891cd2df7c91003d8879a205075211678ad40a817b746c4dde54830ad6d', + witnesses: 'bae0329346060aeeef91dee449ccee7187e68501c0b5b082026312e7c21a4798', }, }; @@ -211,7 +211,7 @@ test('client typography uses the shared semantic scale outside the token owner', test('accepted stylesheet has pinned declaration, selector, keyframe, variable, and cascade ledgers', () => { const witnesses = readStyleWitnesses(root); - assert.equal(witnesses.length, 1209); + assert.equal(witnesses.length, 1198); assert.equal(witnesses.filter((witness) => witness.unknown || witness.ancestorUnknown).length, 0); const ledger = createStyleLedger(readStyleSource(root), { witnesses }); assert.deepEqual(ledger.counts, acceptedLedger.counts); @@ -408,8 +408,8 @@ test('main owns one public stylesheet and the regrouped production CSS is determ assert.equal((main.match(/import ['"][^'"]+\.css['"]/g) || []).length, 1); const assets = fs.readdirSync(path.join(root, 'dist/assets')).filter((file) => file.endsWith('.css')); - assert.deepEqual(assets, ['index-fn-ai4xB.css']); + assert.deepEqual(assets, ['index-C2yOT86L.css']); const built = fs.readFileSync(path.join(root, 'dist/assets', assets[0])); - assert.equal(built.byteLength, 168021); - assert.equal(sha256(built), 'a160cdc915a4a8e0b95e360ed721e6037cbeda28df4e0815ab6be43f1004176d'); + assert.equal(built.byteLength, 169625); + assert.equal(sha256(built), 'a9bb2c83ab62798e762f4338be1869a6d29366903f83f90d4abcaa2855e44d8e'); });