refactor(ui): organize app component domains
- Move root-level picker, input, and menu components into lowercase domain folders - Update component namespace linting for kebab-case app domains - Document FSD-lite component structure in AGENTS.md
This commit is contained in:
parent
d195bb67d2
commit
c1207f1be3
19
AGENTS.md
19
AGENTS.md
|
|
@ -143,14 +143,15 @@ Keep this section light; implementation details move often.
|
|||
|
||||
### File and folder naming
|
||||
|
||||
OpenPencil follows a Reka UI-inspired component namespace structure:
|
||||
OpenPencil uses domain namespaces rather than full Feature-Sliced Design ceremony:
|
||||
|
||||
- Vue component namespace folders use PascalCase: `ColorPicker/`, `Toolbar/`, `ProviderSettings/`.
|
||||
- Vue component files use PascalCase: `ColorPickerRoot.vue`, `ToolbarItem.vue`.
|
||||
- Component-scoped composables use camelCase: `useToolbarState.ts`, `usePageList.ts`.
|
||||
- Non-component domain folders use lowercase or kebab-case: `scene-graph/`, `figma-api/`, `node-edit/`.
|
||||
- Non-component TypeScript files use lowercase or kebab-case unless they are conventional entrypoints such as `index.ts`, `types.ts`, `context.ts`, or `use.ts`.
|
||||
- Multi-file root components live inside their component namespace folder, not beside it.
|
||||
- App services/state/integration live under `src/app/**`; route/layout views live under `src/views/**`; app UI lives under `src/components/**`.
|
||||
- `src/components/ui/**` is the shared app design-system layer. `packages/vue/src/primitives/**` is the headless SDK primitive layer. App wrappers around SDK primitives should stay in app component domains and only move to `ui/**` when genuinely generic.
|
||||
- Root-level `src/components/*.vue` is reserved for broad editor panels/surfaces assembled by views or shell layout. Do not add new root-level base controls; create a domain namespace or use `src/components/ui/**` for reusable primitives.
|
||||
- App component domain folders should be lowercase or kebab-case (`chat/`, `properties/`, `fill-picker/`, `color-picker-panel/`, `canvas/`, `inputs/`). Avoid adding new `PascalCase/Component.vue` app folders; migrate existing ones gradually when touched.
|
||||
- Vue component files stay PascalCase: `ColorPickerRoot.vue`, `ToolbarItem.vue`. Component-scoped composables use camelCase: `useToolbarState.ts`, `usePageList.ts`.
|
||||
- Non-component domain folders use lowercase or kebab-case: `scene-graph/`, `figma-api/`, `node-edit/`. Non-component TypeScript files use lowercase or kebab-case unless they are conventional entrypoints such as `index.ts`, `types.ts`, `context.ts`, or `use.ts`.
|
||||
- Multi-file root components live inside their component namespace folder, not beside it. When a reusable picker/input/control grows beyond one file, create a namespace instead of leaving related files at `src/components/` root.
|
||||
- Use subfolders for multi-file domains instead of sibling files with repeated prefixes. Prefer `selection/container.ts`, `selection/hit-test.ts` over `selection-container.ts`, `selection-hit-test.ts`. When adding a second file for a domain (e.g. `eval-wrap.ts` next to `eval.ts`), create the folder immediately (`eval/index.ts` + `eval/wrap.ts`) instead of prefixing. Oxlint catches sibling prefix files when a sibling folder exists; Steiger catches 3+ sibling files with the same prefix. The convention applies even before either rule triggers.
|
||||
|
||||
### Repo tools and scripts
|
||||
|
|
@ -258,7 +259,7 @@ Self-review checklist:
|
|||
- `src/components/ui/**` is the app design-system layer: reusable visual primitives, wrappers around Reka UI primitives, low-level styled controls, and UI class helpers. These files must not import app services/stores or feature panels.
|
||||
- `src/components/Shell/**` is for app shell chrome and global app services rendered as components (menu bar, toast viewport, update/status chrome). Shell components may use app shell/editor stores.
|
||||
- `src/components/properties/**`, `src/components/chat/**`, `src/components/LayerTree/**`, `src/components/Toolbar/**`, and similar folders are feature/domain component namespaces. Keep feature-specific controls there unless they are genuinely reusable UI primitives.
|
||||
- Root-level `src/components/*.vue` is for broad editor panels/surfaces that are assembled by views or shell layout. Do not add new root-level base controls; create a domain folder or move reusable primitives to `src/components/ui/**`.
|
||||
- Treat existing root-level picker/input/control components as migration candidates when touched; do not expand that pattern.
|
||||
- Test hooks should be `data-test-id` attributes owned by the rendered markup or generated internally from semantic component state. Do not add `testId`, `visibilityTestId`, `triggerTestId`, or other test-id props to component APIs.
|
||||
|
||||
- Use reka-ui for UI components (Splitter, ContextMenu, DropdownMenu, etc.)
|
||||
|
|
@ -267,7 +268,7 @@ Self-review checklist:
|
|||
- App wrappers around SDK primitives should compose a single `ui` object from shared UI helpers (`useSelectUI`, `usePopoverUI`, etc.) rather than bypassing the design system with raw Tailwind strings spread across multiple props.
|
||||
- Editor commands share `packages/vue/src/editor/commands/registry.ts` as the canonical source for shortcut display tokens, keyboard bindings, and context-menu test IDs. Store portable shortcuts such as `MOD+D`, `MOD+SHIFT+H`, and `MOD+ALT+K`; format them with `formatShortcut()` at render time so macOS shows `⌘`/`⌥` and Windows/Linux show `Ctrl`/`Alt`.
|
||||
- Labels and translations must not contain shortcut text. Keep labels semantic (`Add auto layout`, `Show/Hide`) and render shortcuts from command metadata. Steiger enforces this for `packages/vue/src/i18n/messages.ts` and locale JSON files.
|
||||
- Canvas context-menu structure lives in `packages/vue/src/editor/menu-model/canvas.ts`. Do not hand-build command grouping in `src/components/CanvasMenu.vue`; the component should render menu entries and provide app-specific actions only when unavoidable.
|
||||
- Canvas context-menu structure lives in `packages/vue/src/editor/menu-model/canvas.ts`. Do not hand-build command grouping in `src/components/canvas/CanvasMenu.vue`; the component should render menu entries and provide app-specific actions only when unavoidable.
|
||||
- Browser and Tauri menus share `src/app/shell/menu/schema.ts` as the canonical menu model. Do not add menu items directly in `src/components/Shell/AppMenu.vue` or `desktop/src/menu.rs`.
|
||||
- Regenerate the native menu with `bun run generate:tauri-menu` after editing the shared menu schema; `desktop/generated/menu.json` is consumed by the Tauri menu builder. Tauri also runs this generator from `desktop/tauri.conf.json` via `beforeDevCommand` and `beforeBuildCommand`.
|
||||
- Every shared menu item with an `id` must be handled by `src/app/shell/menu/use.ts`, an editor command, or explicitly marked browser/native-only in the schema.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { existsSync } from 'node:fs'
|
||||
|
||||
import { parse as parseVueSfc } from 'vue/compiler-sfc'
|
||||
|
||||
function normalizedFilename(context) {
|
||||
|
|
@ -311,7 +312,10 @@ const noNativeTitleAttributesInVue = {
|
|||
let hasTitleAttribute = false
|
||||
walkVueTemplateAst(template, (templateNode) => {
|
||||
if (hasTitleAttribute) return
|
||||
if (isStaticVueAttribute(templateNode, 'title') || isVueBindDirective(templateNode, 'title')) {
|
||||
if (
|
||||
isStaticVueAttribute(templateNode, 'title') ||
|
||||
isVueBindDirective(templateNode, 'title')
|
||||
) {
|
||||
hasTitleAttribute = true
|
||||
}
|
||||
})
|
||||
|
|
@ -451,7 +455,8 @@ const TEST_ID_FORMAT = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/
|
|||
const noRawTestIdStringProps = {
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Disallow test-id component props — use data-test-id attrs or internal semantic ids'
|
||||
description:
|
||||
'Disallow test-id component props — use data-test-id attrs or internal semantic ids'
|
||||
}
|
||||
},
|
||||
create(context) {
|
||||
|
|
@ -624,9 +629,7 @@ const noRawTestIdSelectorsInTests = {
|
|||
|
||||
function isGeneratedTestIdLiteral(value) {
|
||||
if (typeof value !== 'string') return false
|
||||
const toolbarValue = value.startsWith('mobile-toolbar-')
|
||||
? value.slice('mobile-'.length)
|
||||
: value
|
||||
const toolbarValue = value.startsWith('mobile-toolbar-') ? value.slice('mobile-'.length) : value
|
||||
return (
|
||||
toolbarValue.startsWith('toolbar-tool-') ||
|
||||
toolbarValue.startsWith('toolbar-flyout-') ||
|
||||
|
|
@ -714,7 +717,8 @@ const noBrowserSideEffectsInVue = {
|
|||
) {
|
||||
context.report({
|
||||
node,
|
||||
message: 'Use VueUse useEventListener() instead of direct browser event listeners in Vue components.'
|
||||
message:
|
||||
'Use VueUse useEventListener() instead of direct browser event listeners in Vue components.'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
|
@ -743,7 +747,8 @@ const noBrowserSideEffectsInVue = {
|
|||
const noDocumentQuerySelectorInVue = {
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Disallow document.querySelector in Vue components — use template refs or composables'
|
||||
description:
|
||||
'Disallow document.querySelector in Vue components — use template refs or composables'
|
||||
}
|
||||
},
|
||||
create(context) {
|
||||
|
|
@ -756,7 +761,10 @@ const noDocumentQuerySelectorInVue = {
|
|||
if (callee?.type !== 'MemberExpression') return
|
||||
if (callee.object?.type !== 'Identifier' || callee.object.name !== 'document') return
|
||||
if (callee.property?.type !== 'Identifier') return
|
||||
if (callee.property.name !== 'querySelector' && callee.property.name !== 'querySelectorAll') {
|
||||
if (
|
||||
callee.property.name !== 'querySelector' &&
|
||||
callee.property.name !== 'querySelectorAll'
|
||||
) {
|
||||
return
|
||||
}
|
||||
context.report({
|
||||
|
|
@ -772,7 +780,8 @@ const noDocumentQuerySelectorInVue = {
|
|||
const noDirectSelectionToolStateMutation = {
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Disallow direct editor selection/tool state assignment outside core editor internals'
|
||||
description:
|
||||
'Disallow direct editor selection/tool state assignment outside core editor internals'
|
||||
}
|
||||
},
|
||||
create(context) {
|
||||
|
|
@ -843,7 +852,8 @@ function colorObjectLiteral(node, color) {
|
|||
const noHardcodedColorConstants = {
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Use named color constants instead of inline Color object literals for shared colors'
|
||||
description:
|
||||
'Use named color constants instead of inline Color object literals for shared colors'
|
||||
}
|
||||
},
|
||||
create(context) {
|
||||
|
|
@ -853,10 +863,17 @@ const noHardcodedColorConstants = {
|
|||
return {
|
||||
ObjectExpression(node) {
|
||||
if (colorObjectLiteral(node, { r: 0, g: 0, b: 0, a: 1 })) {
|
||||
context.report({ node, message: 'Use BLACK from constants instead of an inline black Color literal.' })
|
||||
context.report({
|
||||
node,
|
||||
message: 'Use BLACK from constants instead of an inline black Color literal.'
|
||||
})
|
||||
}
|
||||
if (colorObjectLiteral(node, { r: 0, g: 0, b: 0, a: 0 })) {
|
||||
context.report({ node, message: 'Use TRANSPARENT from constants instead of an inline transparent Color literal.' })
|
||||
context.report({
|
||||
node,
|
||||
message:
|
||||
'Use TRANSPARENT from constants instead of an inline transparent Color literal.'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1551,10 +1568,10 @@ const vueComponentFilePascalCase = {
|
|||
}
|
||||
}
|
||||
|
||||
const componentNamespacePascalCase = {
|
||||
const componentNamespaceCasing = {
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Require component namespace folders to use PascalCase names'
|
||||
description: 'Require component namespace folders to use the project casing convention'
|
||||
}
|
||||
},
|
||||
create(context) {
|
||||
|
|
@ -1577,12 +1594,11 @@ const componentNamespacePascalCase = {
|
|||
const parts = componentMatch[1].split('/')
|
||||
const first = parts[0]
|
||||
const second = parts[1]
|
||||
const allowedGroups = new Set(['chat', 'properties', 'ui'])
|
||||
|
||||
if (parts.length > 1 && !allowedGroups.has(first) && !isPascalCaseName(first)) {
|
||||
if (parts.length > 1 && !isPascalCaseName(first) && !isKebabOrLowercaseName(first)) {
|
||||
context.report({
|
||||
node,
|
||||
message: `Component namespace folder '${first}' must use PascalCase.`
|
||||
message: `Component namespace folder '${first}' must use PascalCase or kebab-case.`
|
||||
})
|
||||
return
|
||||
}
|
||||
|
|
@ -1591,11 +1607,12 @@ const componentNamespacePascalCase = {
|
|||
parts.length > 2 &&
|
||||
(first === 'chat' || first === 'properties') &&
|
||||
second !== undefined &&
|
||||
!isPascalCaseName(second)
|
||||
!isPascalCaseName(second) &&
|
||||
!isKebabOrLowercaseName(second)
|
||||
) {
|
||||
context.report({
|
||||
node,
|
||||
message: `Nested component namespace folder '${first}/${second}' must use PascalCase.`
|
||||
message: `Nested component namespace folder '${first}/${second}' must use PascalCase or kebab-case.`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1789,7 +1806,8 @@ const noDirectOpenPencilBrowserStore = {
|
|||
if (!isOpenPencilMember(node.object)) return
|
||||
context.report({
|
||||
node,
|
||||
message: 'Use window.openPencil.getStore() instead of accessing window.openPencil.store directly.'
|
||||
message:
|
||||
'Use window.openPencil.getStore() instead of accessing window.openPencil.store directly.'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1887,7 +1905,8 @@ function hasAstChild(node, predicate, seen = new WeakSet()) {
|
|||
function containsRecordStringUnknownType(node) {
|
||||
if (isRecordStringUnknownType(node)) return true
|
||||
if (node?.type === 'TSArrayType') return isRecordStringUnknownType(node.elementType)
|
||||
if (node?.type === 'TSUnionType') return node.types?.some(containsRecordStringUnknownType) ?? false
|
||||
if (node?.type === 'TSUnionType')
|
||||
return node.types?.some(containsRecordStringUnknownType) ?? false
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -1942,7 +1961,8 @@ const noBroadUnknownTypeAssertions = {
|
|||
function typeNameText(node) {
|
||||
if (!node) return 'unknown'
|
||||
if (node.type === 'Identifier') return node.name
|
||||
if (node.type === 'TSQualifiedName') return `${typeNameText(node.left)}.${typeNameText(node.right)}`
|
||||
if (node.type === 'TSQualifiedName')
|
||||
return `${typeNameText(node.left)}.${typeNameText(node.right)}`
|
||||
return node.type
|
||||
}
|
||||
|
||||
|
|
@ -2034,7 +2054,8 @@ const noDuplicateTypeShapes = {
|
|||
}
|
||||
context.report({
|
||||
node,
|
||||
message: 'Duplicate object type shape. Reuse the existing named type instead of redeclaring the same members.'
|
||||
message:
|
||||
'Duplicate object type shape. Reuse the existing named type instead of redeclaring the same members.'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -2054,7 +2075,8 @@ const noLocalJsonObjectAliases = {
|
|||
if (!isRecordStringUnknownType(node.typeAnnotation)) return
|
||||
context.report({
|
||||
node,
|
||||
message: 'Import JsonObject from @open-pencil/scene-graph/primitives instead of declaring a local alias.'
|
||||
message:
|
||||
'Import JsonObject from @open-pencil/scene-graph/primitives instead of declaring a local alias.'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -2062,7 +2084,6 @@ const noLocalJsonObjectAliases = {
|
|||
}
|
||||
|
||||
const noFlatKiwiModules = createProgramFilenameRule({
|
||||
|
||||
description: 'Disallow flat top-level Kiwi modules — group code under Kiwi subdomains',
|
||||
check(file) {
|
||||
const marker = '/packages/core/src/kiwi/'
|
||||
|
|
@ -2136,7 +2157,7 @@ const plugin = {
|
|||
'prefer-vueuse-timeouts': preferVueUseTimeouts,
|
||||
'max-composition-root-lines': maxCompositionRootLines,
|
||||
'vue-component-file-pascal-case': vueComponentFilePascalCase,
|
||||
'component-namespace-pascal-case': componentNamespacePascalCase,
|
||||
'component-namespace-casing': componentNamespaceCasing,
|
||||
'non-component-source-directories-kebab-case': nonComponentSourceDirectoriesKebabCase,
|
||||
'no-component-root-sibling-folder': noComponentRootSiblingFolder,
|
||||
'no-useless-pass-through-wrappers': noUselessPassThroughWrappers,
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@
|
|||
"open-pencil/prefer-vueuse-intervals": "error",
|
||||
"open-pencil/prefer-vueuse-timeouts": "error",
|
||||
"open-pencil/vue-component-file-pascal-case": "error",
|
||||
"open-pencil/component-namespace-pascal-case": "error",
|
||||
"open-pencil/component-namespace-casing": "error",
|
||||
"open-pencil/non-component-source-directories-kebab-case": "error",
|
||||
"open-pencil/no-component-root-sibling-folder": "error",
|
||||
"open-pencil/no-flat-kiwi-modules": "error",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { ColorPickerRoot } from '@open-pencil/vue'
|
||||
|
||||
import ColorPickerPanel from '@/components/ColorPickerPanel/ColorPickerPanel.vue'
|
||||
import ColorPickerPanel from '@/components/color-picker-panel/ColorPickerPanel.vue'
|
||||
import { usePopoverUI } from '@/components/ui/popover'
|
||||
|
||||
import type { Color } from '@open-pencil/scene-graph/primitives'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { computed, ref } from 'vue'
|
|||
|
||||
import { useI18n, useSelectionState, useEditorCommands } from '@open-pencil/vue'
|
||||
|
||||
import VariablesDialog from './VariablesDialog.vue'
|
||||
import VariablesDialog from './variables/VariablesDialog.vue'
|
||||
import BooleanOperationsControl from './properties/BooleanOperationsControl.vue'
|
||||
import AppearanceSection from './properties/AppearanceSection.vue'
|
||||
import EffectsSection from './properties/EffectsSection.vue'
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import IconLucidePanelLeft from '~icons/lucide/panel-left'
|
|||
import IconLucidePanelRight from '~icons/lucide/panel-right'
|
||||
import IconLucidePanelTop from '~icons/lucide/panel-top'
|
||||
import CanvasMenu from './CanvasMenu.vue'
|
||||
import ScrubInput from './ScrubInput.vue'
|
||||
import ScrubInput from './inputs/ScrubInput.vue'
|
||||
|
||||
const store = useEditorStore()
|
||||
const collab = useCollabInjected()
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
import { LayerTreeRoot, LayerTreeItem, useInlineRename } from '@open-pencil/vue'
|
||||
import type { LayerDragInstruction, LayerNode } from '@open-pencil/vue'
|
||||
import { useEditorStore } from '@/app/editor/active-store'
|
||||
import CanvasMenu from '../CanvasMenu.vue'
|
||||
import CanvasMenu from '../canvas/CanvasMenu.vue'
|
||||
import LayerTreeNodeRow from './LayerTreeNodeRow.vue'
|
||||
import LayerTreeRenameRow from './LayerTreeRenameRow.vue'
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { useAIChat } from '@/app/ai/chat/use'
|
|||
import ChatPanel from './ChatPanel.vue'
|
||||
import CodePanel from './CodePanel.vue'
|
||||
import DesignPanel from './DesignPanel.vue'
|
||||
import ZoomDropdown from './ZoomDropdown.vue'
|
||||
import ZoomDropdown from './editor/ZoomDropdown.vue'
|
||||
|
||||
const { activeTab } = useAIChat()
|
||||
const { panels } = useI18n()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { ColorAreaArea, ColorAreaRoot, ColorAreaThumb } from 'reka-ui'
|
||||
|
||||
import { useColorPickerPanelContext } from '@/components/ColorPickerPanel/context'
|
||||
import { useColorPickerPanelContext } from '@/components/color-picker-panel/context'
|
||||
|
||||
const ctx = useColorPickerPanelContext()
|
||||
</script>
|
||||
|
|
@ -2,10 +2,10 @@
|
|||
import type { Color } from '@open-pencil/scene-graph/primitives'
|
||||
import type { OkHCLControls } from '@open-pencil/vue'
|
||||
|
||||
import ColorAreaControl from '@/components/ColorPickerPanel/ColorAreaControl.vue'
|
||||
import FormatControls from '@/components/ColorPickerPanel/FormatControls.vue'
|
||||
import HueAlphaSliders from '@/components/ColorPickerPanel/HueAlphaSliders.vue'
|
||||
import { provideColorPickerPanel } from '@/components/ColorPickerPanel/context'
|
||||
import ColorAreaControl from '@/components/color-picker-panel/ColorAreaControl.vue'
|
||||
import FormatControls from '@/components/color-picker-panel/FormatControls.vue'
|
||||
import HueAlphaSliders from '@/components/color-picker-panel/HueAlphaSliders.vue'
|
||||
import { provideColorPickerPanel } from '@/components/color-picker-panel/context'
|
||||
|
||||
const { color, okhcl = null } = defineProps<{
|
||||
color: Color
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
<script setup lang="ts">
|
||||
import AppSelect from '@/components/ui/AppSelect.vue'
|
||||
import HsbFields from '@/components/ColorPickerPanel/HsbFields.vue'
|
||||
import HslFields from '@/components/ColorPickerPanel/HslFields.vue'
|
||||
import OkhclFields from '@/components/ColorPickerPanel/OkhclFields.vue'
|
||||
import RgbFields from '@/components/ColorPickerPanel/RgbFields.vue'
|
||||
import { useColorPickerPanelContext } from '@/components/ColorPickerPanel/context'
|
||||
import HsbFields from '@/components/color-picker-panel/HsbFields.vue'
|
||||
import HslFields from '@/components/color-picker-panel/HslFields.vue'
|
||||
import OkhclFields from '@/components/color-picker-panel/OkhclFields.vue'
|
||||
import RgbFields from '@/components/color-picker-panel/RgbFields.vue'
|
||||
import { useColorPickerPanelContext } from '@/components/color-picker-panel/context'
|
||||
|
||||
const ctx = useColorPickerPanelContext()
|
||||
</script>
|
||||
|
|
@ -2,8 +2,8 @@
|
|||
import { inputNumberValue } from '@open-pencil/vue'
|
||||
import { colorToCSS } from '@open-pencil/core/color'
|
||||
|
||||
import PickerSlider from '@/components/PickerSlider.vue'
|
||||
import { useColorPickerPanelContext } from '@/components/ColorPickerPanel/context'
|
||||
import PickerSlider from '@/components/color-picker-panel/PickerSlider.vue'
|
||||
import { useColorPickerPanelContext } from '@/components/color-picker-panel/context'
|
||||
|
||||
const ctx = useColorPickerPanelContext()
|
||||
</script>
|
||||
|
|
@ -2,8 +2,8 @@
|
|||
import { inputNumberValue } from '@open-pencil/vue'
|
||||
import { colorToCSS } from '@open-pencil/core/color'
|
||||
|
||||
import PickerSlider from '@/components/PickerSlider.vue'
|
||||
import { useColorPickerPanelContext } from '@/components/ColorPickerPanel/context'
|
||||
import PickerSlider from '@/components/color-picker-panel/PickerSlider.vue'
|
||||
import { useColorPickerPanelContext } from '@/components/color-picker-panel/context'
|
||||
|
||||
const ctx = useColorPickerPanelContext()
|
||||
</script>
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
<script setup lang="ts">
|
||||
import { colorToCSS } from '@open-pencil/core/color'
|
||||
|
||||
import PickerSlider from '@/components/PickerSlider.vue'
|
||||
import { useColorPickerPanelContext } from '@/components/ColorPickerPanel/context'
|
||||
import PickerSlider from '@/components/color-picker-panel/PickerSlider.vue'
|
||||
import { useColorPickerPanelContext } from '@/components/color-picker-panel/context'
|
||||
|
||||
const ctx = useColorPickerPanelContext()
|
||||
</script>
|
||||
|
|
@ -2,8 +2,8 @@
|
|||
import { colorToCSS } from '@open-pencil/core/color'
|
||||
import { fromPercent, toPercent } from '@open-pencil/vue'
|
||||
|
||||
import PickerSlider from '@/components/PickerSlider.vue'
|
||||
import { useColorPickerPanelContext } from '@/components/ColorPickerPanel/context'
|
||||
import PickerSlider from '@/components/color-picker-panel/PickerSlider.vue'
|
||||
import { useColorPickerPanelContext } from '@/components/color-picker-panel/context'
|
||||
|
||||
const ctx = useColorPickerPanelContext()
|
||||
</script>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { inputNumberValue } from '@open-pencil/vue'
|
||||
import { usePickerSliderUI } from './ui/picker-slider'
|
||||
import { usePickerSliderUI } from '@/components/ui/picker-slider'
|
||||
|
||||
type PickerSliderDisplay = {
|
||||
value?: number
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { inputNumberValue } from '@open-pencil/vue'
|
||||
import { useColorPickerPanelContext } from '@/components/ColorPickerPanel/context'
|
||||
import { useColorPickerPanelContext } from '@/components/color-picker-panel/context'
|
||||
|
||||
const ctx = useColorPickerPanelContext()
|
||||
</script>
|
||||
|
|
@ -4,10 +4,10 @@ import { twMerge } from 'tailwind-merge'
|
|||
import { applySolidFillColor, FillPickerRoot, useI18n } from '@open-pencil/vue'
|
||||
|
||||
import GradientEditor from './GradientEditor.vue'
|
||||
import ColorPickerPanel from '@/components/ColorPickerPanel/ColorPickerPanel.vue'
|
||||
import ColorPickerPanel from '@/components/color-picker-panel/ColorPickerPanel.vue'
|
||||
import ImageFillPicker from './ImageFillPicker.vue'
|
||||
import Tip from './ui/Tip.vue'
|
||||
import { usePopoverUI } from './ui/popover'
|
||||
import Tip from '@/components/ui/Tip.vue'
|
||||
import { usePopoverUI } from '@/components/ui/popover'
|
||||
|
||||
import type { Fill } from '@open-pencil/scene-graph'
|
||||
import type { OkHCLControls } from '@open-pencil/vue'
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
<script setup lang="ts">
|
||||
import AppSelect from './ui/AppSelect.vue'
|
||||
import Tip from './ui/Tip.vue'
|
||||
import ColorPickerPanel from '@/components/ColorPickerPanel/ColorPickerPanel.vue'
|
||||
import ScrubInput from './ScrubInput.vue'
|
||||
import AppSelect from '@/components/ui/AppSelect.vue'
|
||||
import Tip from '@/components/ui/Tip.vue'
|
||||
import ColorPickerPanel from '@/components/color-picker-panel/ColorPickerPanel.vue'
|
||||
import ScrubInput from '@/components/inputs/ScrubInput.vue'
|
||||
import { colorToCSS } from '@open-pencil/core/color'
|
||||
import {
|
||||
GradientEditorRoot,
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
import { computed, shallowRef, watch } from 'vue'
|
||||
import { useFileDialog, useObjectUrl } from '@vueuse/core'
|
||||
|
||||
import AppSelect from './ui/AppSelect.vue'
|
||||
import AppSelect from '@/components/ui/AppSelect.vue'
|
||||
|
||||
import { useEditorStore } from '@/app/editor/active-store'
|
||||
|
||||
|
|
@ -3,7 +3,7 @@ import { computed, ref } from 'vue'
|
|||
|
||||
import { useAppearance, useI18n } from '@open-pencil/vue'
|
||||
|
||||
import ScrubInput from '@/components/ScrubInput.vue'
|
||||
import ScrubInput from '@/components/inputs/ScrubInput.vue'
|
||||
import VariableScrubInput from '@/components/properties/VariableScrubInput.vue'
|
||||
import IconButton from '@/components/ui/IconButton.vue'
|
||||
import PanelSection from '@/components/ui/PanelSection.vue'
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, useAttrs } from 'vue'
|
||||
|
||||
import ScrubInput from '@/components/ScrubInput.vue'
|
||||
import ScrubInput from '@/components/inputs/ScrubInput.vue'
|
||||
import BoundVariableButton from '@/components/properties/BoundVariableButton.vue'
|
||||
import VariablePickerPopover from '@/components/properties/VariablePickerPopover.vue'
|
||||
import IconButton from '@/components/ui/IconButton.vue'
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import AppSelect from '@/components/ui/AppSelect.vue'
|
||||
import ColorInput from '@/components/ColorPicker/ColorInput.vue'
|
||||
import ScrubInput from '@/components/ScrubInput.vue'
|
||||
import ScrubInput from '@/components/inputs/ScrubInput.vue'
|
||||
import IconButton from '@/components/ui/IconButton.vue'
|
||||
import PanelSection from '@/components/ui/PanelSection.vue'
|
||||
import Tip from '@/components/ui/Tip.vue'
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import { PropertyListRoot, useFillControls, useOkHCL, useI18n, inputValue } from '@open-pencil/vue'
|
||||
import { colorToHexRaw, parseColor } from '@open-pencil/core/color'
|
||||
|
||||
import FillPicker from '@/components/FillPicker.vue'
|
||||
import FillPicker from '@/components/fill-picker/FillPicker.vue'
|
||||
import IconButton from '@/components/ui/IconButton.vue'
|
||||
import PanelSection from '@/components/ui/PanelSection.vue'
|
||||
import ColorStyleRow from '@/components/properties/ColorStyleRow.vue'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import AppSelect from '@/components/ui/AppSelect.vue'
|
||||
import ScrubInput from '@/components/ScrubInput.vue'
|
||||
import ScrubInput from '@/components/inputs/ScrubInput.vue'
|
||||
import IconButton from '@/components/ui/IconButton.vue'
|
||||
import { useI18n, useLayoutControlsContext } from '@open-pencil/vue'
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import {
|
|||
SelectViewport
|
||||
} from 'reka-ui'
|
||||
|
||||
import ScrubInput from '@/components/ScrubInput.vue'
|
||||
import ScrubInput from '@/components/inputs/ScrubInput.vue'
|
||||
import VariableScrubInput from '@/components/properties/VariableScrubInput.vue'
|
||||
import BoundVariableButton from '@/components/properties/BoundVariableButton.vue'
|
||||
import VariablePickerPopover from '@/components/properties/VariablePickerPopover.vue'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import ScrubInput from '@/components/ScrubInput.vue'
|
||||
import ScrubInput from '@/components/inputs/ScrubInput.vue'
|
||||
import IconButton from '@/components/ui/IconButton.vue'
|
||||
import PanelRow from '@/components/ui/PanelRow.vue'
|
||||
import PanelSection from '@/components/ui/PanelSection.vue'
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import ColorStyleRow from '@/components/properties/ColorStyleRow.vue'
|
|||
import { boundVariableColor } from '@/components/properties/color-style-row'
|
||||
import AppSelect from '@/components/ui/AppSelect.vue'
|
||||
import ColorInput from '@/components/ColorPicker/ColorInput.vue'
|
||||
import ScrubInput from '@/components/ScrubInput.vue'
|
||||
import ScrubInput from '@/components/inputs/ScrubInput.vue'
|
||||
import IconButton from '@/components/ui/IconButton.vue'
|
||||
import PanelSection from '@/components/ui/PanelSection.vue'
|
||||
import Tip from '@/components/ui/Tip.vue'
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { TypographyControlsRoot, useI18n } from '@open-pencil/vue'
|
||||
|
||||
import FontPicker from '@/components/FontPicker.vue'
|
||||
import FontPicker from '@/components/font-picker/FontPicker.vue'
|
||||
import FontSettingsPopover from '@/components/FontSettings/FontSettingsPopover.vue'
|
||||
import VariableScrubInput from '@/components/properties/VariableScrubInput.vue'
|
||||
import AppSelect from '@/components/ui/AppSelect.vue'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import ScrubInput from '@/components/ScrubInput.vue'
|
||||
import ScrubInput from '@/components/inputs/ScrubInput.vue'
|
||||
import BoundVariableButton from '@/components/properties/BoundVariableButton.vue'
|
||||
import VariablePickerPopover from '@/components/properties/VariablePickerPopover.vue'
|
||||
import { useI18n, useNumberVariableBinding } from '@open-pencil/vue'
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import IconToggleLeft from '~icons/lucide/toggle-left'
|
|||
import IconType from '~icons/lucide/type'
|
||||
import IconX from '~icons/lucide/x'
|
||||
import ColorInput from '@/components/ColorPicker/ColorInput.vue'
|
||||
import Tip from './ui/Tip.vue'
|
||||
import Tip from '@/components/ui/Tip.vue'
|
||||
import { useDialogUI } from '@/components/ui/dialog'
|
||||
import { useMenuUI } from '@/components/ui/menu'
|
||||
|
||||
Loading…
Reference in a new issue