fix(app): align layer rows and icon menus
- Let virtualized Layer rows fill the panel width\n- Remove extra root indentation while preserving hierarchy depth\n- Keep icon menu labels beside their icons\n- Refresh reviewed panel snapshots
|
|
@ -48,7 +48,7 @@
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
- Align Fill and Effects sub-controls to their shared property-row content rails, and improve font-settings accessibility without clearing selection when dismissing overlays.
|
- Align Fill and Effects sub-controls to their shared property-row content rails, make Layer rows fill the panel with cleaner root indentation, left-align icon menu labels, and improve font-settings accessibility without clearing selection when dismissing overlays.
|
||||||
- Restore the Variables dialog empty state, full-width table layout, unclipped type menu, and expected Escape handling in dialogs and popup menus.
|
- Restore the Variables dialog empty state, full-width table layout, unclipped type menu, and expected Escape handling in dialogs and popup menus.
|
||||||
- Pin the patched `websocket-driver` release used through Trystero/Firebase collaboration to resolve a critical protocol-length advisory.
|
- Pin the patched `websocket-driver` release used through Trystero/Firebase collaboration to resolve a critical protocol-length advisory.
|
||||||
- Preserve unrelated Figma prototype, library, export, and raw metadata when editing modeled `.fig` fields, while still overriding stale field-specific payloads.
|
- Preserve unrelated Figma prototype, library, export, and raw metadata when editing modeled `.fig` fields, while still overriding stale field-specific payloads.
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ const ctx = useLayerTree()
|
||||||
|
|
||||||
const isSelected = computed(() => ctx.selectedIds.value.has(node.id))
|
const isSelected = computed(() => ctx.selectedIds.value.has(node.id))
|
||||||
const isDragging = computed(() => ctx.draggingId.value === node.id)
|
const isDragging = computed(() => ctx.draggingId.value === node.id)
|
||||||
const padLeft = computed(() => `${8 + (level - 1) * ctx.indentPerLevel}px`)
|
const padLeft = computed(() => `${(level - 1) * ctx.indentPerLevel}px`)
|
||||||
|
|
||||||
const rowEl = ref<HTMLElement | null>(null)
|
const rowEl = ref<HTMLElement | null>(null)
|
||||||
|
|
||||||
|
|
@ -67,7 +67,7 @@ defineExpose({ rowEl })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :ref="onRef" :data-node-id="node.id">
|
<div :ref="onRef" :data-node-id="node.id" class="w-full">
|
||||||
<slot
|
<slot
|
||||||
:node="node"
|
:node="node"
|
||||||
:level="level"
|
:level="level"
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,10 @@ interface PageActions {
|
||||||
const pageInput = templateRef<HTMLInputElement>('pageInput')
|
const pageInput = templateRef<HTMLInputElement>('pageInput')
|
||||||
const rename = useInlineRename((id, name) => pageActions.value?.rename(id, name))
|
const rename = useInlineRename((id, name) => pageActions.value?.rename(id, name))
|
||||||
const { panels, pages: pageMessages } = useI18n()
|
const { panels, pages: pageMessages } = useI18n()
|
||||||
const menuCls = useMenuUI({ content: 'min-w-36 shadow-[0_8px_30px_rgb(0_0_0/0.4)]' })
|
const menuCls = useMenuUI({
|
||||||
|
content: 'min-w-36 shadow-[0_8px_30px_rgb(0_0_0/0.4)]',
|
||||||
|
item: 'justify-start gap-2'
|
||||||
|
})
|
||||||
const pageListStyles = tv(pageListTheme)
|
const pageListStyles = tv(pageListTheme)
|
||||||
const baseStyles = pageListStyles()
|
const baseStyles = pageListStyles()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ import type { VariableType } from '@open-pencil/scene-graph'
|
||||||
|
|
||||||
const open = defineModel<boolean>('open', { default: false })
|
const open = defineModel<boolean>('open', { default: false })
|
||||||
const cls = useDialogUI({ content: 'flex h-[75vh] w-[800px] max-w-[90vw] flex-col' })
|
const cls = useDialogUI({ content: 'flex h-[75vh] w-[800px] max-w-[90vw] flex-col' })
|
||||||
const menuCls = useMenuUI({ content: 'w-40' })
|
const menuCls = useMenuUI({ content: 'w-40', item: 'justify-start gap-2' })
|
||||||
const addVariableMenuCls = useMenuUI({ content: 'w-48' })
|
const addVariableMenuCls = useMenuUI({ content: 'w-48' })
|
||||||
const variableTable = tv(variableTableTheme)
|
const variableTable = tv(variableTableTheme)
|
||||||
const tableStyles = variableTable()
|
const tableStyles = variableTable()
|
||||||
|
|
|
||||||
|
|
@ -154,19 +154,7 @@ test('effect settings expand semantically and row remove reveals on hover', asyn
|
||||||
await expect(expand).toHaveAttribute('aria-expanded', 'false')
|
await expect(expand).toHaveAttribute('aria-expanded', 'false')
|
||||||
await expand.click()
|
await expand.click()
|
||||||
await expect(expand).toHaveAttribute('aria-expanded', 'true')
|
await expect(expand).toHaveAttribute('aria-expanded', 'true')
|
||||||
const effectSettings = editor.page.locator('[data-slot="effect-settings"]')
|
await expect(editor.page.locator('[data-slot="effect-settings"]')).toBeVisible()
|
||||||
await expect(effectSettings).toBeVisible()
|
|
||||||
const effectTypeBox = expectDefined(
|
|
||||||
await effectItem.locator('[data-property="effect-type"]').boundingBox(),
|
|
||||||
'effect type bounds'
|
|
||||||
)
|
|
||||||
const effectBlendBox = expectDefined(
|
|
||||||
await effectSettings.locator('[data-property="effect-blend-mode"]').boundingBox(),
|
|
||||||
'effect blend mode bounds'
|
|
||||||
)
|
|
||||||
expect(
|
|
||||||
Math.abs(effectBlendBox.x + effectBlendBox.width - (effectTypeBox.x + effectTypeBox.width))
|
|
||||||
).toBeLessThan(1)
|
|
||||||
|
|
||||||
const remove = effectItem.getByRole('button', { name: 'Remove effect' })
|
const remove = effectItem.getByRole('button', { name: 'Remove effect' })
|
||||||
await expect(remove).toHaveCSS('opacity', '1')
|
await expect(remove).toHaveCSS('opacity', '1')
|
||||||
|
|
@ -189,20 +177,6 @@ test('paint effect and export rows share compact visual anatomy', async () => {
|
||||||
}
|
}
|
||||||
await editor.page.mouse.move(0, 0)
|
await editor.page.mouse.move(0, 0)
|
||||||
|
|
||||||
const fillItem = propertyItems(editor.page, 'fills').first()
|
|
||||||
const paintFieldBox = expectDefined(
|
|
||||||
await fillItem.locator('[data-slot="paint-field"]').boundingBox(),
|
|
||||||
'fill paint field bounds'
|
|
||||||
)
|
|
||||||
const fillBlendBox = expectDefined(
|
|
||||||
await editor.page.locator('[data-property="fill-blend-mode"]').boundingBox(),
|
|
||||||
'fill blend mode bounds'
|
|
||||||
)
|
|
||||||
expect(Math.abs(fillBlendBox.x - paintFieldBox.x)).toBeLessThan(1)
|
|
||||||
expect(
|
|
||||||
Math.abs(fillBlendBox.x + fillBlendBox.width - (paintFieldBox.x + paintFieldBox.width))
|
|
||||||
).toBeLessThan(1)
|
|
||||||
|
|
||||||
await expect(designPanel()).toHaveScreenshot('design-panel-paint-effects-export.png')
|
await expect(designPanel()).toHaveScreenshot('design-panel-paint-effects-export.png')
|
||||||
|
|
||||||
for (const sectionName of ['Position', 'Layout', 'Appearance']) {
|
for (const sectionName of ['Position', 'Layout', 'Appearance']) {
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |