chore: guard shared UI boundaries
This commit is contained in:
parent
042271f519
commit
5acb8ffb6c
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)]',
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue