chore: guard shared UI boundaries

This commit is contained in:
Danila Poyarkov 2026-05-14 19:00:51 +03:00
parent 042271f519
commit 5acb8ffb6c
5 changed files with 29 additions and 3 deletions

View file

@ -238,7 +238,7 @@ Release commits are the exception: keep using `Release v0.x.y`.
## Code conventions
- Do not place code or tests ad hoc. Before adding or moving files, inspect the existing folder structure and nearby patterns, then put changes in the established domain-specific location. If no proper location exists, create one deliberately and update docs/conventions as needed.
- Architecture boundaries are enforced by Steiger (`bun run check:arch`). App code must use public workspace package exports, workspace packages must not import app `src/` code, package-local aliases (`#core`, `#vue`, `#cli`, `#mcp`) are only for their owning package, core must stay framework-agnostic, and canvas/editor overlay code must not import property-panel internals.
- Architecture boundaries are enforced by Steiger (`bun run check:arch`). App code must use public workspace package exports, workspace packages must not import app `src/` code, package-local aliases (`#core`, `#vue`, `#cli`, `#mcp`) are only for their owning package, core must stay framework-agnostic, shared UI (`src/components/ui/**`) must not import app services/stores, property-panel internals must stay inside the property panel, and canvas/editor overlay code must not import property-panel internals.
- Test placement is strict: E2E tests live under `tests/e2e/**` and use `*.spec.ts`; engine/unit tests live under `tests/engine/**` and use `*.test.ts`. Do not put store-only/internal-state assertions in E2E. If a test drives the UI like a user and verifies visible behavior, it can be E2E; if it creates nodes through internals and asserts graph state, it belongs in engine/unit coverage.
### File and folder naming

View file

@ -168,6 +168,27 @@ const noForeignPackageLocalAliases = createImportRule(
}
)
const noAppImportsInSharedUi = createImportRule(
'open-pencil/no-app-imports-in-shared-ui',
(sourceRel, _specifier, resolved) => {
if (!sourceRel.startsWith('src/components/ui/')) return null
if (resolved?.startsWith('src/app/')) {
return 'Shared UI components must not import app services or stores. Pass data/actions in or move app-specific wrappers outside src/components/ui.'
}
return null
}
)
const noPropertyPanelInternalsOutsidePanel = createImportRule(
'open-pencil/no-property-panel-internals-outside-panel',
(sourceRel, _specifier, resolved) => {
if (!resolved?.startsWith('src/components/properties/')) return null
if (sourceRel.startsWith('src/components/properties/')) return null
if (sourceRel === 'src/components/DesignPanel.vue') return null
return 'Property-panel internals must stay inside the property panel. Extract app-neutral UI before reusing elsewhere.'
}
)
const noUiImportsInCore = createImportRule(
'open-pencil/no-ui-imports-in-core',
(sourceRel, specifier) => {
@ -192,6 +213,8 @@ export const openPencilArchitecturePlugin = {
noAppImportsInWorkspacePackages,
noPackageInternalsInApp,
noForeignPackageLocalAliases,
noAppImportsInSharedUi,
noPropertyPanelInternalsOutsidePanel,
noUiImportsInCore
]
}

View file

@ -2,8 +2,9 @@ import { useEventListener } from '@vueuse/core'
import { ref } from 'vue'
import { isTauri } from '@/app/tauri/env'
import type { ToastVariant } from '@/components/ui/toast'
export type ToastVariant = 'default' | 'warning' | 'error'
export type { ToastVariant } from '@/components/ui/toast'
export interface Toast {
id: number

View file

@ -1,7 +1,7 @@
import { twMerge } from 'tailwind-merge'
import { tv } from 'tailwind-variants'
import type { ToastVariant } from '@/app/shell/ui'
export type ToastVariant = 'default' | 'warning' | 'error'
const toast = tv({
base: 'flex max-w-sm items-start gap-1.5 rounded-md px-2.5 py-1.5 text-xs shadow-md data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=closed]:slide-out-to-top-1 data-[state=open]:animate-in data-[state=open]:fade-in data-[state=open]:slide-in-from-top-1 data-[swipe=cancel]:translate-y-0 data-[swipe=cancel]:transition-transform data-[swipe=move]:translate-y-[var(--reka-toast-swipe-move-y)]',

View file

@ -23,6 +23,8 @@ export default defineConfig([
'open-pencil/no-app-imports-in-workspace-packages': 'error',
'open-pencil/no-package-internals-in-app': 'error',
'open-pencil/no-foreign-package-local-aliases': 'error',
'open-pencil/no-app-imports-in-shared-ui': 'error',
'open-pencil/no-property-panel-internals-outside-panel': 'error',
'open-pencil/no-ui-imports-in-core': 'error'
}
}