openpencil/src/components/LayersPanel.vue
Danila Poyarkov 42225c604a
feat: refresh branding with generated platform icons (#707)
* feat: refresh branding with generated platform icons

Keep the approved mark and optical micro master as the source of truth. Generate web, documentation, and native assets at their owning build boundaries instead of storing raster variants or linking web icons to desktop output.

* fix: refine small brand marks and loading artwork

* feat: adopt blue editing-handle brand mark

* feat: refine teal branding for light and dark themes

* feat: apply ivory tiles across brand surfaces

Use edge-to-edge web tiles with a larger mark and optical micro favicons. Preserve native spacing and platform-owned maskable/touch cropping. Address review feedback on story props, native dimensions, and brand test type checking.

* test: allow cold npm startup in packaging fixture

CI hit Bun's five-second default while running npm pack --dry-run. Give this integration test a scoped 30-second budget without changing production timeouts or other tests.
2026-09-16 11:54:07 +03:00

88 lines
2.7 KiB
Vue

<script setup lang="ts">
import { SplitterGroup, SplitterPanel, SplitterResizeHandle } from 'reka-ui'
import { tv } from 'tailwind-variants'
import { computed, ref } from 'vue'
import { useI18n } from '@open-pencil/vue'
import AppMenu from '@/components/shell/AppMenu.vue'
import SegmentedControl from '@/components/ui/select/SegmentedControl.vue'
import splitterTheme from '@/theme/splitter'
import AssetsPanel from './assets-panel/AssetsPanel.vue'
import LayerTree from './LayerTree/LayerTree.vue'
import PagesPanel from './PagesPanel.vue'
const { menu, panels } = useI18n()
const activePanel = ref<'file' | 'assets'>('file')
const panelModel = computed({
get: () => activePanel.value,
set: (value: string) => {
if (value === 'file' || value === 'assets') activePanel.value = value
}
})
const panelOptions = computed(() => [
{ value: 'file', label: menu.value.file },
{ value: 'assets', label: panels.value.assets }
])
const panelTabsUI = { root: 'w-full' }
const splitterStyles = tv(splitterTheme)({ direction: 'vertical' })
</script>
<template>
<aside
data-test-id="layers-panel"
class="flex min-w-0 flex-1 flex-col overflow-hidden border-r border-border bg-panel"
style="contain: paint layout style"
>
<AppMenu />
<div class="shrink-0 border-b border-border px-2 py-1.5">
<SegmentedControl
v-model="panelModel"
:options="panelOptions"
:label="panels.layers"
:ui="panelTabsUI"
>
<template #option="{ option }">
<span
:data-test-id="
option.value === 'file' ? 'left-panel-layers-tab' : 'left-panel-assets-tab'
"
class="truncate"
>
{{ option.label }}
</span>
</template>
</SegmentedControl>
</div>
<AssetsPanel v-if="activePanel === 'assets'" />
<SplitterGroup
v-else
direction="vertical"
auto-save-id="layers-layout"
class="flex-1 overflow-hidden"
>
<SplitterPanel
:default-size="30"
:min-size="10"
:max-size="60"
class="flex flex-col overflow-hidden"
>
<PagesPanel />
</SplitterPanel>
<SplitterResizeHandle :class="splitterStyles.handle()">
<div :class="splitterStyles.divider()" />
</SplitterResizeHandle>
<SplitterPanel :default-size="70" :min-size="20" class="flex flex-col overflow-hidden">
<header
data-test-id="layers-header"
class="shrink-0 px-3 py-2 text-[11px] font-semibold text-surface"
>
{{ panels.layers }}
</header>
<LayerTree data-test-id="layers-tree" />
</SplitterPanel>
</SplitterGroup>
</aside>
</template>