diff --git a/.changeset/fresh-ui-intent.md b/.changeset/fresh-ui-intent.md new file mode 100644 index 0000000..b729ca5 --- /dev/null +++ b/.changeset/fresh-ui-intent.md @@ -0,0 +1,7 @@ +--- +"@inkcre/ui-web": minor +--- + +Give the web package first-class Oxc and native TypeScript development +workflows, and replace the non-discoverable `agent-skills/` folders with the +installed, validated `@inkcre/ui-web#ui-web` TanStack Intent skill. diff --git a/.gitignore b/.gitignore index cafd430..eee3857 100644 --- a/.gitignore +++ b/.gitignore @@ -6,9 +6,10 @@ yarn.lock # IDE files .vscode/* -!.vscode/*.example.jsonc -!.vscode/*.code-snippets -!.vscode/extensions.json +!.vscode/*.example.jsonc +!.vscode/*.code-snippets +!.vscode/extensions.json +!.vscode/settings.json # temp files tmp @@ -29,4 +30,4 @@ coverage/ # images *.png -*.jpg \ No newline at end of file +*.jpg diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 404a8c6..b982cd9 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,11 +1,11 @@ { "recommendations": [ "Vue.volar", - "esbenp.prettier-vscode", + "oxc.oxc-vscode", "mrmlnc.vscode-scss", "lokalise.i18n-ally", "antfu.iconify", "antfu.unocss", "vitest.explorer" ] -} \ No newline at end of file +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..97135c3 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "editor.defaultFormatter": "oxc.oxc-vscode", + "editor.formatOnSave": true, + "editor.formatOnSaveMode": "file", + "editor.codeActionsOnSave": { + "source.fixAll.oxc": "explicit" + }, + "js/ts.tsdk.path": "node_modules/typescript/lib", + "js/ts.tsdk.promptToUseWorkspaceVersion": true, + "oxc.fixKind": "safe_fix", + "oxc.requireConfig": true +} diff --git a/package.json b/package.json index 202534a..c5d4c20 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "build": "pnpm --filter @inkcre/ui-web build", "build-tokens": "tsx scripts/build-tokens.ts", "build-package-metadata": "tsx scripts/build-package-metadata.ts", - "build-skills": "tsx scripts/build-agent-skills.ts", + "build-skills": "pnpm --filter @inkcre/ui-web skill:generate", "generate": "pnpm build-tokens && pnpm build-package-metadata && pnpm build-skills", "check:generated": "tsx scripts/check-generated.ts", "check:stories": "tsx scripts/check-story-coverage.ts", @@ -21,10 +21,11 @@ "test": "pnpm --filter @inkcre/ui-web test", "story": "pnpm --filter @inkcre/ui-web story:build", "story:dev": "pnpm --filter @inkcre/ui-web story:dev", - "format": "oxfmt .oxfmtrc.json package.json pnpm-workspace.yaml tsconfig.json scripts packages/web/package.json packages/web/component-manifest.json packages/web/component-manifest.schema.json packages/web/tsconfig.json packages/web/tsconfig.check.json packages/web/tsconfig.exports.json packages/web/vite.config.js packages/web/vite.subpaths.config.js packages/web/vitest.config.ts packages/web/histoire.config.ts packages/web/stories .github/workflows/*.yml README.md", - "format:check": "pnpm format --check", - "lint": "oxlint --quiet scripts packages/web/src packages/web/stories packages/web/histoire.config.ts packages/web/vite.config.js packages/web/vite.subpaths.config.js packages/web/vitest.config.ts", - "check": "pnpm format:check && pnpm lint && pnpm check:generated && pnpm check:stories && pnpm check:token-workflow && pnpm type-check && pnpm test && pnpm build && pnpm check:package && pnpm story", + "check:skills": "pnpm --filter @inkcre/ui-web skill:generate:check && pnpm --filter @inkcre/ui-web skill:validate", + "format": "oxfmt .oxfmtrc.json package.json pnpm-workspace.yaml tsconfig.json scripts .github/workflows/*.yml README.md && pnpm --filter @inkcre/ui-web format", + "format:check": "oxfmt --check .oxfmtrc.json package.json pnpm-workspace.yaml tsconfig.json scripts .github/workflows/*.yml README.md && pnpm --filter @inkcre/ui-web format:check", + "lint": "oxlint --deny-warnings scripts && pnpm --filter @inkcre/ui-web lint", + "check": "pnpm format:check && pnpm lint && pnpm check:generated && pnpm check:skills && pnpm check:stories && pnpm check:token-workflow && pnpm type-check && pnpm test && pnpm build && pnpm check:package && pnpm story", "changeset": "changeset", "changeset:add": "changeset add", "changeset:version": "changeset version && pnpm build-package-metadata", diff --git a/packages/web/.oxfmtrc.json b/packages/web/.oxfmtrc.json new file mode 100644 index 0000000..e81e537 --- /dev/null +++ b/packages/web/.oxfmtrc.json @@ -0,0 +1,10 @@ +{ + "$schema": "../../node_modules/oxfmt/configuration_schema.json", + "ignorePatterns": [ + "src/components.d.ts", + "src/public-components.ts", + "src/version.ts", + "styles/tokens/**", + "styles/uno/preset-ink.ts" + ] +} diff --git a/packages/web/.oxlintrc.json b/packages/web/.oxlintrc.json new file mode 100644 index 0000000..43ef7c2 --- /dev/null +++ b/packages/web/.oxlintrc.json @@ -0,0 +1,17 @@ +{ + "$schema": "../../node_modules/oxlint/configuration_schema.json", + "plugins": ["eslint", "oxc", "typescript", "unicorn", "vue", "vitest"], + "options": { + "denyWarnings": true, + "reportUnusedDisableDirectives": "error" + }, + "overrides": [ + { + "files": ["src/components/inkPicker/inkPicker.vue"], + "rules": { + "vue/valid-define-emits": "off", + "vue/valid-define-props": "off" + } + } + ] +} diff --git a/packages/web/MIGRATION.md b/packages/web/MIGRATION.md index f25a820..2b41783 100644 --- a/packages/web/MIGRATION.md +++ b/packages/web/MIGRATION.md @@ -24,6 +24,27 @@ peer for `InkJsonEditor`; consumers do not import it directly, but their package manager must resolve it in the installation graph. Execution 06 must verify this through the committed consumer lockfile and frozen installation. +## Installed Agent Skill + +The non-discoverable `agent-skills/` documentation path is replaced by one +TanStack Intent skill at `skills/ui-web`. No compatibility mirror is shipped: +known consumers did not use the old path, and preserving it would double the +generated stale-output surface without creating an Intent contract. + +Consumers that want agent guidance explicitly allowlist the trusted package: + +```json +{ + "intent": { + "skills": ["@inkcre/ui-web"] + } +} +``` + +Then load the installed skill with +`intent load @inkcre/ui-web#ui-web`. This changes packaged documentation only; +it does not add a runtime JavaScript export. + ## Compatibility and rollback - `@inkcre/web-design@1.2.2` is the immutable rollback artifact. It will not be diff --git a/packages/web/README.md b/packages/web/README.md index 38cf87c..bdc1a3d 100644 --- a/packages/web/README.md +++ b/packages/web/README.md @@ -14,12 +14,12 @@ pnpm add @inkcre/ui-web ```typescript // main.ts -import { createApp } from 'vue' -import InKCreUIWeb from '@inkcre/ui-web' // optional -import "@inkcre/ui-web/styles" // must +import { createApp } from "vue"; +import InKCreUIWeb from "@inkcre/ui-web"; // optional +import "@inkcre/ui-web/styles"; // required once -const app = createApp(App) -app.use(InKCreUIWeb) +const app = createApp(App); +app.use(InKCreUIWeb); ``` ```scss @@ -59,11 +59,15 @@ export default defineConfig({ ### Use routing -InkHeader and some other components can integrate with your router. See `agent-skills/router/SKILL.md` for setup instructions. +InkHeader and other components can consume a router adapter. Load +`@inkcre/ui-web#ui-web` through TanStack Intent and read +`references/integration.md` for the package boundary. ### Use internationalization (i18n) -The design system supports internationalization. See `agent-skills/i18n/SKILL.md` for setup instructions with vue-i18n. +The design system supports internationalization through the same adapter +boundary. The installed UI skill documents the provider contract and locale +subpath. ## Features @@ -77,6 +81,24 @@ The design system supports internationalization. See `agent-skills/i18n/SKILL.md ## Agent Skills -The package includes [Agent Skills](https://agentskills.io) - standardized packages of domain expertise that AI agents can discover and load dynamically. Located in `agent-skills/`, component skills are automatically generated from source code during the build process. +The package ships one progressively disclosed Agent Skill under +`skills/ui-web`. Consumers explicitly trust the package through +`package.json#intent.skills`, then discover and load it with the pinned +[TanStack Intent](https://tanstack.com/intent) CLI: -Skills work with Claude Code, GitHub Copilot, and other AI agents supporting the agentskills.io standard. +```json +{ + "intent": { + "skills": ["@inkcre/ui-web"] + } +} +``` + +```bash +intent list +intent load @inkcre/ui-web#ui-web +``` + +The generated component facts come from the public component manifest and +source/story contracts. Selection and composition guidance is reviewed in +`skill.seed.json`. diff --git a/packages/web/agent-skills/best-practices/SKILL.md b/packages/web/agent-skills/best-practices/SKILL.md deleted file mode 100644 index cd32de4..0000000 --- a/packages/web/agent-skills/best-practices/SKILL.md +++ /dev/null @@ -1,76 +0,0 @@ ---- -name: best-practices -description: Best practices for developing with @inkcre/ui-web including coding guidelines and accessibility. ---- - -# Best Practices - -Use this skill for guidance on developing with @inkcre/ui-web. - -## Component Development - -1. **Single Responsibility**: Each component does one thing well -2. **High Cohesion, Low Coupling**: Keep related code together, minimize dependencies -3. **Clear API**: Props and events should be intuitive -4. **Avoid Prop Drilling**: Use provide/inject for deeply nested data -5. **Testable**: Write components that are easy to test - -## Naming Conventions - -- **Components**: `camelCase` (e.g., `inkButton`) -- **CSS Classes**: `kebab-case` (e.g., `ink-button`) -- **Props/Variables**: `camelCase` (e.g., `isLoading`) -- **Events**: `kebab-case` (e.g., `update:modelValue`) - -## Error Handling - -- Use graceful degradation -- Provide user-friendly error messages -- Log errors appropriately -- Validate inputs and provide defaults - -## Accessibility - -- Use semantic HTML elements -- Provide ARIA labels where needed -- Ensure keyboard navigation works -- Test with screen readers -- Maintain proper color contrast - -## Performance - -- Use `v-if` vs `v-show` appropriately -- Avoid unnecessary watchers -- Use `computed` for derived state -- Lazy load components when possible -- Optimize large lists with virtual scrolling - -## Code for Human Brains - -Write code that's easy to understand: - -- **Keep it simple**: Prefer straightforward solutions -- **Limit cognitive load**: Keep functions simple (≤4 concepts to hold in memory) -- **Use meaningful names**: Variables should be self-documenting -- **Prefer early returns**: Avoid deeply nested conditions -- **Comment the "why"**: Explain motivation, not just what - -### Example - -❌ Hard to understand: -```typescript -if (val > someConstant && (condition2 || condition3) && (condition4 && !condition5)) { - // What are we checking? -} -``` - -✅ Easy to understand: -```typescript -const isValid = val > someConstant; -const isAllowed = condition2 || condition3; -const isSecure = condition4 && !condition5; - -if (isValid && isAllowed && isSecure) { - // Clear what each condition means -} -``` diff --git a/packages/web/agent-skills/components/SKILL.md b/packages/web/agent-skills/components/SKILL.md deleted file mode 100644 index 32c3bad..0000000 --- a/packages/web/agent-skills/components/SKILL.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -name: components -description: Use @inkcre/ui-web Vue 3 components. Includes all 21 components with props, events, and usage examples. ---- - -# @inkcre/ui-web Components - -Use this skill when working with the @inkcre/ui-web Vue 3 component library. - -## Overview - -@inkcre/ui-web provides 21 UI components for Vue 3 applications: -- [InkAutoForm](references/inkAutoForm.md) -- [InkButton](references/inkButton.md) -- [InkDatetimePickerView](references/inkDatetimePickerView.md) -- [InkDialog](references/inkDialog.md) -- [InkDoubleCheck](references/inkDoubleCheck.md) -- [InkDropdown](references/inkDropdown.md) -- [InkField](references/inkField.md) -- [InkForm](references/inkForm.md) -- [InkHeader](references/inkHeader.md) -- [InkImage](references/inkImage.md) -- [InkInput](references/inkInput.md) -- [InkJsonEditor](references/inkJsonEditor.md) -- [InkLoading](references/inkLoading.md) -- [InkPagination](references/inkPagination.md) -- [InkPicker](references/inkPicker.md) -- [InkPlaceholder](references/inkPlaceholder.md) -- [InkPopup](references/inkPopup.md) -- [InkScrim](references/inkScrim.md) -- [InkSwitch](references/inkSwitch.md) -- [InkTextarea](references/inkTextarea.md) -- [InkTooltip](references/inkTooltip.md) - -## Installation - -```bash -npm install @inkcre/ui-web -# or -pnpm add @inkcre/ui-web -``` - -## Setup - -```typescript -// main.ts -import { createApp } from 'vue' -import InKCreUIWeb from '@inkcre/ui-web' -import "@inkcre/ui-web/styles" - -const app = createApp(App) -app.use(InKCreUIWeb) -``` - -## Component References - -Each component has detailed documentation in the `references/` directory: - -- [`InkAutoForm`](references/inkAutoForm.md) - Component with props, events, and examples -- [`InkButton`](references/inkButton.md) - Component with props, events, and examples -- [`InkDatetimePickerView`](references/inkDatetimePickerView.md) - Component with props, events, and examples -- [`InkDialog`](references/inkDialog.md) - Component with props, events, and examples -- [`InkDoubleCheck`](references/inkDoubleCheck.md) - Component with props, events, and examples -- [`InkDropdown`](references/inkDropdown.md) - Component with props, events, and examples -- [`InkField`](references/inkField.md) - Component with props, events, and examples -- [`InkForm`](references/inkForm.md) - Component with props, events, and examples -- [`InkHeader`](references/inkHeader.md) - Component with props, events, and examples -- [`InkImage`](references/inkImage.md) - Component with props, events, and examples -- [`InkInput`](references/inkInput.md) - Component with props, events, and examples -- [`InkJsonEditor`](references/inkJsonEditor.md) - Component with props, events, and examples -- [`InkLoading`](references/inkLoading.md) - Component with props, events, and examples -- [`InkPagination`](references/inkPagination.md) - Component with props, events, and examples -- [`InkPicker`](references/inkPicker.md) - Component with props, events, and examples -- [`InkPlaceholder`](references/inkPlaceholder.md) - Component with props, events, and examples -- [`InkPopup`](references/inkPopup.md) - Component with props, events, and examples -- [`InkScrim`](references/inkScrim.md) - Component with props, events, and examples -- [`InkSwitch`](references/inkSwitch.md) - Component with props, events, and examples -- [`InkTextarea`](references/inkTextarea.md) - Component with props, events, and examples -- [`InkTooltip`](references/inkTooltip.md) - Component with props, events, and examples diff --git a/packages/web/agent-skills/components/references/inkAutoForm.md b/packages/web/agent-skills/components/references/inkAutoForm.md deleted file mode 100644 index 571f2d6..0000000 --- a/packages/web/agent-skills/components/references/inkAutoForm.md +++ /dev/null @@ -1,144 +0,0 @@ -# InkAutoForm - -# InkAutoForm - -The InkAutoForm component automatically generates forms from flat JSON Schema definitions. It maps primitive types (string, number, boolean) to appropriate form controls and provides validation using `vscode-json-languageservice`. - -## Rationale - -InkAutoForm exists to simplify form creation by automatically generating UI controls from JSON Schema definitions, reducing boilerplate code for data input forms. - -Use InkAutoForm when you have a flat JSON schema for user input and want to avoid manually wiring form controls. Do not use for complex nested schemas, arrays, or conditional logic. - -## Design Semantics - -### Concepts - -- `Schema`: A JSON Schema object defining the structure and constraints of the form fields. -- `Form Data`: An object holding the current values of the form fields, updated in real-time. -- `Layout`: The arrangement of form fields, either inline, column, or row. - -### Visual / UX Meaning - -In the default state, fields display labels and inputs with placeholders. When validation fails, fields show error messages below the input, changing the border color to indicate errors. Required fields are marked with an asterisk. The layout affects spacing and flow: column stacks vertically, row aligns horizontally, inline compacts fields. - -## Canonical Examples - -- Basic form with string, number, and boolean fields: - - ```vue - - - - ``` - -## Behavioral Contract - -- Form data is validated in real-time against the schema; errors are displayed immediately below invalid fields. -- Required fields must be filled; missing values trigger validation errors. -- Default values from the schema are applied on initialization. -- State transitions (e.g., from valid to invalid) update the UI without throwing exceptions. -- No uncaught exceptions are thrown during validation or rendering. - -## Extension & Composition - -InkAutoForm can be composed within larger forms or used standalone. It supports v-model for two-way binding and can be integrated with form submission logic. Not recommended for high-frequency reflow containers due to dynamic field rendering. - -## Non-Goals - -- Handling nested objects or arrays in schemas. -- Supporting complex conditional schemas with `if`/`then`/`else`. -- Data persistence or business logic integration. -- Custom validation beyond JSON Schema constraints. - -## Implementation Notes - -Relies on `vscode-json-languageservice` for schema validation. Internal state managed via Vue reactivity. Assumes DOM environment for rendering; SSR support may require additional handling. - -## Props - -```typescript -export const inkAutoFormProps = { - /** JSON Schema definition for the form (flat properties only) */ - schema: { - type: Object as PropType, - required: true, - }, - /** Form data object */ - formData: makeObjectProp>({}), - /** Layout for form fields */ - layout: makeStringProp("col"), -} as const; -``` - -## Events - -```typescript -export const inkAutoFormEmits = { - "update:formData": (value: Record) => true, -} as const; -``` - -## Types - -```typescript -type FieldLayout = "inline" | "col" | "row"; - -export interface JSONSchema { - type: "object"; - properties: Record; - required?: string[]; - [key: string]: any; -} -interface JSONSchemaProperty { - type: "string" | "number" | "integer" | "boolean"; - title?: string; - description?: string; - default?: any; - enum?: any[]; - format?: "date" | "time" | "datetime" | "date-time"; - maxLength?: number; - minimum?: number; - maximum?: number; - pattern?: string; - [key: string]: any; -} -interface FieldComponentMapping { - component: string; - props: Record; -} -``` - -## Import - -```typescript -import { InkAutoForm } from '@inkcre/ui-web'; -``` diff --git a/packages/web/agent-skills/components/references/inkButton.md b/packages/web/agent-skills/components/references/inkButton.md deleted file mode 100644 index 1a684a8..0000000 --- a/packages/web/agent-skills/components/references/inkButton.md +++ /dev/null @@ -1,154 +0,0 @@ -# InkButton - -# InkButton - -Just button, you can have text, icon here and provides different semantics in different sizes. - -## Rationale - -InkButton exists to provide a consistent visual and interactive affordance for primary actions, secondary actions, and destructive actions across the product. - -Use it for any single-click action that requires a compact button UI; avoid using it when a non-single-click workflow is needed (for example, file uploads with progress, or long-running background tasks that need a separate action component). - -## Design Semantics - -### Concepts - -- `theme`: visual and purposeful variant of the button (`subtle`, `primary`, `danger`). -- `type`: shape variant of the button (`default`, `square`). -- `size`: density of the control (`md`, `sm`). -- `icon`: iconfont class name (e.g., `i-mdi-cog`). -- `iconPlacement`: where to place the icon (`prefix`, `suffix`). -- `isLoading`: whether the button is in a loading state. -- `slot`: custom content (e.g., icon + label, label only). - -### Visual / UX Meaning - -- `subtle` (default): low emphasis, use for non-critical or contextual actions. -- `primary`: main action in a section or modal; higher contrast to attract attention. -- `danger`: destructive action that explains a potentially destructive outcome (e.g., delete). -- `sm`: smaller footprint for compact toolbars; `md`: default size used in forms and actions. -- `square`: used for icon-only buttons. - -## Canonical Examples - -- Subtle (default): Used as the normal/secondary action. - - ```vue - - ``` - -- Primary: The primary action in a modal or a form footer. - - ```vue - - ``` - -- Danger: Used for destructive actions where the user must confirm an operation. - - ```vue - - ``` - -- Small size: Use in dense toolbars or where space is constrained. - - ```vue - - ``` - -- Icon: Use built-in icon prop. - - ```vue - - ``` - -- Loading: Show loading state. - - ```vue - - ``` - -### Custom slot content (icon + label) - -The `slot` is supported for custom content; this is how you add icons or non-text children. - -```vue - -
- Save - -``` - -## Behavioral Contract - -- In all variants, clicking the button emits a `click` event. -- Click events are NOT emitted when `isLoading` is true. -- The default values are: `text` = "Button Text", `theme` = `subtle`, `type` = `default`, `size` = `md`. -- The component uses a native ` - - - - - - - - - - - - - - - - - - - - - - ---- - -## Behavioral Contract - -> 这些是**使用者可以依赖的行为保证**。 - -- 鼠标悬停时: - - ✅ 显示提示框 - - ✅ 提示框定位在指定方向 -- 鼠标移开时: - - ✅ 隐藏提示框 -- 提示框: - - ❌ 不会阻挡鼠标事件 - - ❌ 不会影响页面布局 - ---- - -## Public API - -### Props - -- `content`: 提示文本内容 / Tooltip text content -- `position`: 提示框位置 (`top` | `bottom` | `left` | `right`) / Tooltip position - -> 完整类型、默认值与可控项请参考下方自动生成的 API 表格。 - ---- - -## Slots - -### default - -触发提示的元素,通常是图标、按钮或文本。 -Trigger element, usually an icon, button, or text. - ---- - -## Extension & Composition - -- 可包裹任何需要提示的元素 -- 建议提示文本不超过一行 -- 不要嵌套使用多个 tooltip - ---- - -## Non-Goals - -> 以下内容**不属于该组件的职责范围**。 - -- 不支持富文本内容(使用 Popover 组件) -- 不处理移动端点击显示(移动端应使用其他方式) -- 不提供箭头指示器(保持简洁) - ---- - -## Implementation Notes - -> ⚠️ 面向维护者,而非组件使用者。 - -- 使用 absolute 定位实现位置控制 -- hover 状态通过 ref 管理 -- CSS transition 实现显示/隐藏动画 -- 依赖父元素的 relative 定位 - -## Props - -```typescript -export const inkTooltipProps = { - content: makeStringProp(""), - position: makeStringProp("top"), -} as const; -``` - -## Events - -```typescript -export const inkTooltipEmits = {} as const; -``` - -## Types - -```typescript -type TooltipPosition = "top" | "bottom" | "left" | "right"; - -// --- Props --- -export const inkTooltipProps = { - content: makeStringProp(""), - position: makeStringProp("top"), -} -``` - -## Import - -```typescript -import { InkTooltip } from '@inkcre/ui-web'; -``` diff --git a/packages/web/agent-skills/i18n/SKILL.md b/packages/web/agent-skills/i18n/SKILL.md deleted file mode 100644 index fcf4310..0000000 --- a/packages/web/agent-skills/i18n/SKILL.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -name: i18n -description: Integrate @inkcre/ui-web with vue-i18n for internationalization. ---- - -# Internationalization (i18n) - -Use this skill when setting up internationalization for @inkcre/ui-web components. - -## Overview - -The design system supports i18n through a provider pattern compatible with vue-i18n. - -## Interface - -```typescript -import type { Ref, InjectionKey } from "vue"; - -export interface InkI18n { - t: (key: string) => string; - locale: Ref; -} - -export const INK_I18N_KEY: InjectionKey = Symbol("INK_I18N"); -``` - -## Setup - -1. Install vue-i18n: - -```bash -pnpm add vue-i18n -``` - -2. Extend provided locales: - -```typescript -// locales/en.ts -import { en } from "@inkcre/ui-web/locales"; - -export default { - ...en, - // Your custom translations -} -``` - -3. Configure vue-i18n: - -```typescript -// locales/index.ts -import { createI18n } from "vue-i18n"; -import en from "./en"; -import zhCN from "./zhCN"; - -const i18n = createI18n({ - legacy: false, - locale: "en", - fallbackLocale: "en", - messages: { en, zhCN }, -}); - -export default i18n; -``` - -4. Provide to design system: - -```typescript -// App.vue - -``` - -## Usage - -Components will automatically use translations if i18n is provided. They fall back to English if not configured. - -```typescript -import { useOptionalI18n } from "@inkcre/ui-web"; - -const i18n = useOptionalI18n(); -if (i18n) { - console.log(i18n.t('common.save')); -} -``` diff --git a/packages/web/agent-skills/router/SKILL.md b/packages/web/agent-skills/router/SKILL.md deleted file mode 100644 index 7120951..0000000 --- a/packages/web/agent-skills/router/SKILL.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -name: router -description: Integrate @inkcre/ui-web with Vue Router using the adapter pattern. ---- - -# Router Integration - -Use this skill when integrating @inkcre/ui-web components with Vue Router. - -## Overview - -Some components (like InkHeader) need router capabilities. The design system uses a provider pattern that works with any router implementation. - -## Interface - -```typescript -import type { ComputedRef, InjectionKey } from "vue"; - -export interface InkRouter { - /** Current path (reactive) */ - currentPath: ComputedRef; - /** Current page name */ - currentName: ComputedRef; -} - -export const INK_ROUTER_KEY: InjectionKey = Symbol("INK_ROUTER"); -``` - -## Setup - -1. Create a router adapter: - -```typescript -// router-adapter.ts -import { computed } from "vue"; -import type { Router, RouteLocationNormalizedLoaded } from "vue-router"; -import type { InkRouter } from "@inkcre/ui-web"; - -export function createInkRouterAdapter( - router: Router, - route: RouteLocationNormalizedLoaded -): InkRouter { - return { - currentPath: computed(() => route.path), - currentName: computed(() => route.name), - }; -} -``` - -2. Provide in your app: - -```typescript -// App.vue - -``` - -## Usage in Components - -```typescript -import { useOptionalRouter } from "@inkcre/ui-web"; - -const router = useOptionalRouter(); -if (router) { - console.log(router.currentPath.value); -} -``` diff --git a/packages/web/agent-skills/styling/SKILL.md b/packages/web/agent-skills/styling/SKILL.md deleted file mode 100644 index 71e17b5..0000000 --- a/packages/web/agent-skills/styling/SKILL.md +++ /dev/null @@ -1,90 +0,0 @@ ---- -name: styling -description: Use @inkcre/ui-web styling system with design tokens, SCSS mixins, and theming. ---- - -# Styling System - -Use this skill when working with the @inkcre/ui-web styling system. - -## Overview - -The design system provides: -- Design tokens (colors, spacing, typography, etc.) -- SCSS mixins and functions -- Theme support (light/dark modes) -- CSS custom properties - -## Import SCSS Utilities - -```scss -@use "@inkcre/ui-web/styles/mixins" as *; -@use "@inkcre/ui-web/styles/functions" as fn; -@use "@inkcre/ui-web/tokens/ref" as ref; -@use "@inkcre/ui-web/tokens/sys" as sys; -@use "@inkcre/ui-web/tokens/comp" as comp; -``` - -## Design Tokens - -### Token Layers - -1. **Reference Tokens** (`ref`): Primitive values -2. **System Tokens** (`sys`): Semantic tokens -3. **Component Tokens** (`comp`): Component-specific tokens - -### Token Categories - -- **Colors**: `ref.$color`, `sys.$color-light`, `sys.$color-dark` -- **Spacing**: `ref.$space` (xs, sm, md, lg, xl, etc.) -- **Typography**: `ref.$typo` (font sizes, weights, line heights) -- **Border Radius**: `ref.$radius` -- **Elevation**: `ref.$elevation` (shadows) -- **Breakpoints**: `ref.$breakpoint` (sm, md, lg, xl) -- **Opacity**: `ref.$opacity` - -## Usage Examples - -```scss -.my-component { - // Use system tokens - color: fn.map-deep-get(sys.$color-light, "text", "base"); - padding: fn.map-deep-get(ref.$space, "md"); - border-radius: fn.map-deep-get(ref.$radius, "sm"); - - // Use component tokens - background: fn.map-deep-get(comp.$light, "button", "bg-primary"); -} -``` - -## Theme Support - -```scss -// Light mode -[data-theme="light"] { - --color-text-base: #{fn.map-deep-get(sys.$color-light, "text", "base")}; -} - -// Dark mode -[data-theme="dark"] { - --color-text-base: #{fn.map-deep-get(sys.$color-dark, "text", "base")}; -} -``` - -## UnoCSS Configuration - -```typescript -// uno.config.ts -export default defineConfig({ - safelist: [ - 'i-mdi-menu', - 'i-mdi-loading', - 'i-mdi-refresh', - 'i-mdi-chevron-right', - 'i-mdi-chevron-down', - 'i-mdi-alert-circle-outline', - 'i-mdi-inbox-outline', - 'animate-spin' - ] -}) -``` diff --git a/packages/web/env.d.ts b/packages/web/env.d.ts index e310275..cc38a01 100644 --- a/packages/web/env.d.ts +++ b/packages/web/env.d.ts @@ -1,6 +1,10 @@ /// +/// declare module "*.svg" { const source: string; export default source; } + +declare module "*.scss"; +declare module "virtual:uno.css"; diff --git a/packages/web/histoire.config.ts b/packages/web/histoire.config.ts index a4db0ac..eb66533 100644 --- a/packages/web/histoire.config.ts +++ b/packages/web/histoire.config.ts @@ -182,6 +182,7 @@ export const codeToHtml = () => Promise.resolve(''); export default defineConfig({ plugins: [HstVue()], + viteIgnorePlugins: ["vite:dts"], setupFile: "./stories/histoire.setup.ts", storyMatch: ["stories/**/*.story.vue"], storyIgnored: ["**/node_modules/**", "**/dist/**", "**/.histoire/**"], @@ -224,7 +225,7 @@ export default defineConfig({ // Note: Using process.env.NODE_ENV here is correct as Histoire sets it based on command markdown: process.env.NODE_ENV === "production" - ? (env) => { + ? (_env) => { // Build mode: Use markdown-it without Shiki const md = new MarkdownIt({ html: true, diff --git a/packages/web/package.json b/packages/web/package.json index 6424015..a116927 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -2,6 +2,13 @@ "name": "@inkcre/ui-web", "version": "1.2.2", "description": "Design System for InKCre Web Projects", + "keywords": [ + "design-system", + "inkcre", + "tanstack-intent", + "ui", + "vue" + ], "repository": { "type": "git", "url": "https://github.com/InKCre/ui.git", @@ -11,7 +18,7 @@ "files": [ "dist/", "styles/", - "agent-skills/", + "skills", "MIGRATION.md" ], "type": "module", @@ -74,9 +81,16 @@ "build:subpath-types": "vue-tsc --declaration --emitDeclarationOnly --noEmit false --declarationMap false --composite false -p tsconfig.exports.json", "build:finalize": "tsx ../../scripts/finalize-package.ts", "build:tokens": "tsx ../../scripts/build-tokens.ts", - "build:skills": "tsx ../../scripts/build-agent-skills.ts", + "build:skills": "pnpm run skill:generate:check", "clean": "rimraf dist", "preview": "vite preview", + "format": "oxfmt --config .oxfmtrc.json src stories styles .oxfmtrc.json .oxlintrc.json package.json skill.seed.json component-manifest.json component-manifest.schema.json tsconfig.json tsconfig.build.json tsconfig.check.json tsconfig.exports.json env.d.ts histoire.config.ts uno.config.ts vite.config.js vite.subpaths.config.js vitest.config.ts", + "format:check": "oxfmt --check --config .oxfmtrc.json src stories styles .oxfmtrc.json .oxlintrc.json package.json skill.seed.json component-manifest.json component-manifest.schema.json tsconfig.json tsconfig.build.json tsconfig.check.json tsconfig.exports.json env.d.ts histoire.config.ts uno.config.ts vite.config.js vite.subpaths.config.js vitest.config.ts", + "lint": "oxlint --config .oxlintrc.json src stories histoire.config.ts uno.config.ts vite.config.js vite.subpaths.config.js vitest.config.ts", + "lint:fix": "pnpm run lint --fix", + "skill:generate": "tsx ../../scripts/build-agent-skills.ts", + "skill:generate:check": "tsx ../../scripts/build-agent-skills.ts --check", + "skill:validate": "intent validate", "story:dev": "histoire dev", "story:build": "histoire build", "story:preview": "histoire preview", @@ -86,15 +100,24 @@ "test:ui": "vitest --ui", "test:coverage": "vitest --coverage" }, + "dependencies": { + "@vue/shared": "3.5.25" + }, "devDependencies": { + "@codemirror/autocomplete": "^6.20.0", + "@codemirror/commands": "^6.10.0", + "@codemirror/lang-json": "^6.0.2", + "@codemirror/lint": "^6.9.2", + "@codemirror/state": "^6.5.2", + "@codemirror/view": "^6.38.8", "@histoire/plugin-vue": "1.0.0-alpha.5", "@iconify-json/mdi": "^1.2.3", + "@tanstack/intent": "0.3.6", "@unocss/preset-icons": "^66.5.10", "@unocss/preset-wind3": "^66.5.10", "@vitejs/plugin-vue": "^6.0.3", "@vitest/coverage-v8": "^4.0.15", "@vitest/ui": "^4.0.15", - "@vue/shared": "3.5.25", "@vue/test-utils": "^2.4.6", "@vueuse/core": "^14.1.0", "cpr": "^3.0.1", @@ -108,9 +131,12 @@ "vite": "^7.2.7", "vite-plugin-dts": "^4.5.4", "vitest": "^4.0.15", + "vscode-json-languageservice": "^5.6.4", + "vscode-languageserver-textdocument": "^1.0.12", "vue": "^3.4.0", "vue-i18n": "^11.2.7", - "vue-tsc": "^3.1.8" + "vue-router": "^4.0.0", + "vue-tsc": "3.3.8" }, "peerDependencies": { "@codemirror/autocomplete": "^6.20.0", diff --git a/packages/web/skill.seed.json b/packages/web/skill.seed.json new file mode 100644 index 0000000..811f73e --- /dev/null +++ b/packages/web/skill.seed.json @@ -0,0 +1,586 @@ +{ + "skill": { + "name": "ui-web", + "description": "Use @inkcre/ui-web Vue components for forms, feedback, overlays, media, navigation, JSON editing, styling, router adapters, and i18n integration.", + "title": "InKCre UI Web", + "quickSelection": { + "Application navigation and branding": "InkHeader", + "Single action": "InkButton", + "Structured form": "InkForm with InkField and the matching form control", + "Schema-driven primitive form": "InkAutoForm", + "JSON configuration editing": "InkJsonEditor", + "Destructive confirmation": "InkDoubleCheck for a compact guard; InkDialog for a focused modal decision", + "Custom or positioned overlay": "InkPopup; add InkScrim only when the composition does not already own one", + "Loading state": "InkLoading", + "Empty or error state": "InkPlaceholder", + "Expandable media": "InkImage", + "Paged collection": "InkPagination" + }, + "rules": [ + "Choose components by product intent and interaction semantics, not only visual resemblance.", + "Prefer package components over local substitutes when the public component contract fits.", + "Use public package exports and documented subpaths; never import src/components files.", + "Keep form labels, controls, and layout context explicit through InkForm and InkField.", + "Distinguish loading, empty, error, and confirmation states instead of using one generic feedback surface.", + "Read only the component references needed for the current task." + ], + "avoid": [ + "Do not invent props or events from conventions used by another UI library.", + "Do not treat low-level InkPopup or InkScrim as a replacement for InkDialog when the standard dialog workflow fits.", + "Do not expose repository paths, generated implementation files, or internal prop helpers to consumers.", + "Do not edit this generated skill tree directly; update skill.seed.json or public component facts and regenerate it." + ] + }, + "components": { + "InkAutoForm": { + "intent": ["schema-driven form", "flat JSON Schema editor", "automatic primitive controls"], + "preferWhen": [ + "A flat JSON Schema should produce consistent controls with little application boilerplate.", + "String, number, and boolean properties can map to the package's existing field controls." + ], + "avoidWhen": [ + "The workflow needs deeply nested schemas, conditional branches, arrays, or bespoke field composition.", + "The user should edit raw JSON text; use InkJsonEditor instead." + ], + "composeWith": ["InkForm", "InkInput", "InkSwitch"], + "apiCaveats": [ + "Treat the supported schema subset as a deliberate flat-form contract.", + "Supply custom component mappings only when the generated default control is insufficient." + ], + "commonMistakes": [ + "Do not assume complete JSON Schema coverage.", + "Do not use it merely to avoid designing a workflow-specific form." + ], + "publicTypes": ["JSONSchema", "JSONSchemaProperty"] + }, + "InkButton": { + "intent": ["button", "single-click action", "icon or text command"], + "preferWhen": [ + "The user initiates one immediate command.", + "An action needs package-consistent theme, size, icon, or loading treatment." + ], + "avoidWhen": [ + "The control represents navigation and should preserve link semantics.", + "The workflow requires a multi-step confirmation surface rather than one click." + ], + "composeWith": ["InkDialog", "InkDoubleCheck", "InkHeader", "InkTooltip"], + "apiCaveats": [ + "Choose theme from the component's actual public values; do not borrow variant names from another library.", + "Keep icon-only commands accessible with surrounding context or a tooltip." + ], + "commonMistakes": [ + "Do not replace a destructive confirmation flow with a danger-colored button alone." + ] + }, + "InkDatetimePickerView": { + "intent": ["date and time columns", "weekday picker view", "low-level datetime selection"], + "preferWhen": [ + "A picker composition needs scrollable date, time, datetime, or weekday columns.", + "InkPicker owns the field and popup while this component owns the selection view." + ], + "avoidWhen": [ + "A native text/date input is sufficient.", + "A standalone form field is required without a surrounding InkPicker composition." + ], + "composeWith": ["InkPicker", "InkPopup"], + "apiCaveats": [ + "Use Date values for date/time modes and preserve the selected mode contract.", + "Treat this as a view primitive, not the complete labelled form control." + ], + "commonMistakes": [ + "Do not duplicate popup, formatting, and field responsibilities already owned by InkPicker." + ] + }, + "InkDialog": { + "intent": ["dialog", "confirmation modal", "focused overlay workflow"], + "preferWhen": [ + "A focused decision needs a title, content, cancel/confirm actions, and a backdrop.", + "A short modal workflow benefits from the package's complete dialog structure." + ], + "avoidWhen": [ + "Only a low-level positioned surface is needed; use InkPopup.", + "A compact second-click guard is enough; use InkDoubleCheck." + ], + "composeWith": ["InkButton", "InkForm", "InkLoading", "InkPopup"], + "apiCaveats": [ + "Use the actual model event contract and slots documented in the generated API facts.", + "Keep destructive consequences visible in the dialog content before confirmation." + ], + "commonMistakes": [ + "Do not nest a second independently controlled scrim beneath the dialog.", + "Do not invent open/update:open semantics without checking the emitted events." + ] + }, + "InkDoubleCheck": { + "intent": ["two-step confirmation", "accidental-click guard", "compact destructive action"], + "preferWhen": [ + "A nearby action needs one additional deliberate click without opening a modal.", + "The consequence is already understood and only accidental activation must be prevented." + ], + "avoidWhen": [ + "The user needs explanatory content, input, or multiple choices; use InkDialog.", + "The action is harmless and the extra confirmation would add friction." + ], + "composeWith": ["InkButton", "InkTooltip"], + "apiCaveats": [ + "Make the second state visually and textually clear.", + "Reset confirmation state when the surrounding workflow changes." + ], + "commonMistakes": [ + "Do not use a two-click guard for every action.", + "Do not hide the destructive meaning behind an unlabeled icon." + ] + }, + "InkDropdown": { + "intent": ["single selection", "dropdown options", "lazy-loaded choice"], + "preferWhen": [ + "The user selects one value from a known or asynchronously loaded option list.", + "Optional stepping controls help move through an ordered option set." + ], + "avoidWhen": [ + "The user enters free text; use InkInput.", + "The interaction needs multi-selection or arbitrary tagging." + ], + "composeWith": ["InkField", "InkForm"], + "apiCaveats": [ + "Use DropdownOption for public option values.", + "Choose either static options or the refresher lifecycle deliberately.", + "Editable state controls whether selection interactions are available." + ], + "commonMistakes": [ + "Do not assume an empty model triggers lazy loading.", + "Do not mix static options and refresher behavior without defining precedence." + ], + "publicTypes": ["DropdownOption"] + }, + "InkField": { + "intent": ["form field wrapper", "label and value layout", "field-level structure"], + "preferWhen": [ + "A control needs a label, required state, and consistent inline, row, or column layout.", + "A custom control must align with other InkForm fields." + ], + "avoidWhen": [ + "Content is not a form value.", + "A control already renders its own field wrapper through InkForm context and label props." + ], + "composeWith": ["InkDropdown", "InkInput", "InkJsonEditor", "InkPicker", "InkTextarea"], + "apiCaveats": [ + "Keep field layout consistent with the surrounding InkForm unless an intentional local override is needed." + ], + "commonMistakes": [ + "Do not create nested duplicate labels around controls that already consume form context." + ] + }, + "InkForm": { + "intent": ["form layout context", "grouped fields", "consistent field composition"], + "preferWhen": [ + "Multiple package controls should share one field layout.", + "Child controls should receive layout context through the package's provider contract." + ], + "avoidWhen": [ + "The content is read-only or is not a form.", + "The application expects the component to provide a complete validation framework." + ], + "composeWith": [ + "InkAutoForm", + "InkDropdown", + "InkField", + "InkInput", + "InkJsonEditor", + "InkPicker", + "InkSwitch", + "InkTextarea" + ], + "apiCaveats": [ + "InkForm primarily owns layout context; application validation and submission remain explicit.", + "Use the emitted native submit event rather than relying on browser navigation." + ], + "commonMistakes": [ + "Do not infer full validation orchestration from the form wrapper.", + "Do not bypass the shared layout with unrelated local field markup." + ] + }, + "InkHeader": { + "intent": ["application header", "branding", "top navigation"], + "preferWhen": [ + "An application shell needs logo, title, current-page context, or menu actions.", + "The header should consume the optional router adapter." + ], + "avoidWhen": [ + "A content section only needs a heading.", + "A footer or generic horizontal layout is required." + ], + "composeWith": ["InkButton", "InkTooltip"], + "apiCaveats": [ + "Router integration is adapter-based and optional; the component does not import the consuming router." + ], + "commonMistakes": ["Do not use the application header as a generic card or section title."] + }, + "InkImage": { + "intent": ["image display", "thumbnail", "click-to-expand media"], + "preferWhen": [ + "An image needs package-consistent loading, error, and expandable viewing behavior.", + "A compact thumbnail should open a larger view." + ], + "avoidWhen": [ + "A decorative image needs only native img behavior.", + "The media is not an image or needs a dedicated gallery workflow." + ], + "composeWith": ["InkLoading", "InkPlaceholder", "InkPopup"], + "apiCaveats": [ + "Always provide meaningful alt text for informative images.", + "Handle the public error payload instead of relying on internal image state." + ], + "commonMistakes": [ + "Do not enable expansion when the source cannot provide a useful larger view." + ] + }, + "InkInput": { + "intent": ["single-line text input", "inline text editing", "form text field"], + "preferWhen": [ + "The user enters or edits one line of text.", + "A details view needs the component's inline editing mode." + ], + "avoidWhen": [ + "The value spans multiple lines; use InkTextarea.", + "The value is selected from options; use InkDropdown or InkPicker." + ], + "composeWith": ["InkField", "InkForm"], + "apiCaveats": [ + "Use the component's value event contract rather than assuming modelValue naming.", + "When used inside InkForm, let form context own the default field layout." + ], + "commonMistakes": [ + "Do not use the inline visual mode as a substitute for explicit edit permissions." + ] + }, + "InkJsonEditor": { + "intent": [ + "JSON text editor", + "schema-aware JSON configuration", + "structured text validation" + ], + "preferWhen": [ + "Users need to edit raw JSON with completion and validation support.", + "A schema should guide a CodeMirror-based JSON editing experience." + ], + "avoidWhen": [ + "A flat schema can be represented as ordinary form controls; consider InkAutoForm.", + "The content is general prose or arbitrary code." + ], + "composeWith": ["InkField", "InkForm", "InkPlaceholder"], + "apiCaveats": [ + "The model is JSON text, not an automatically parsed application object.", + "The CodeMirror and VS Code language-service packages remain part of the declared peer contract." + ], + "commonMistakes": [ + "Do not silently coerce invalid JSON into a different value.", + "Do not use it for ordinary multiline text." + ] + }, + "InkLoading": { + "intent": ["loading indicator", "async progress feedback", "pending state"], + "preferWhen": [ + "An operation is currently pending and the user needs immediate visual feedback.", + "A screen, region, or inline action has no determinate progress value." + ], + "avoidWhen": [ + "Content is empty or failed; use InkPlaceholder.", + "A static skeleton layout is required." + ], + "composeWith": ["InkButton", "InkDialog", "InkImage"], + "apiCaveats": [ + "Choose size and density for the surrounding context instead of scaling with local CSS." + ], + "commonMistakes": [ + "Do not leave a loading indicator active after the operation reaches an empty or error state." + ] + }, + "InkPagination": { + "intent": ["pagination", "previous and next navigation", "paged collection"], + "preferWhen": [ + "A dataset has an explicit current page and total page count.", + "Users need deterministic movement through bounded pages." + ], + "avoidWhen": [ + "All content fits comfortably on one page.", + "The product deliberately uses infinite scrolling without page identity." + ], + "composeWith": ["InkLoading", "InkPlaceholder"], + "apiCaveats": [ + "Keep the controlled page value aligned with the data request lifecycle.", + "Use the text mode only when numbered page affordances are intentionally unnecessary." + ], + "commonMistakes": ["Do not render inaccessible or clickable pages outside the valid range."] + }, + "InkPicker": { + "intent": ["formatted value picker", "date or time field", "custom popup selection"], + "preferWhen": [ + "A value needs field layout, display formatting, a popup, and a custom selection surface.", + "Date, time, datetime, or weekday selection should use InkDatetimePickerView." + ], + "avoidWhen": [ + "A simple option list fits InkDropdown.", + "A single-line text field fits InkInput." + ], + "composeWith": ["InkDatetimePickerView", "InkField", "InkForm", "InkPopup"], + "apiCaveats": [ + "The generic value type must stay consistent across formatter, model value, and emitted update.", + "Custom picker content uses the default slot and owns its own value interaction." + ], + "commonMistakes": [ + "Do not assume every picker value is a Date.", + "Do not duplicate the popup state outside the component without a controlled-state reason." + ] + }, + "InkPlaceholder": { + "intent": ["empty state", "error state", "unavailable content guidance"], + "preferWhen": [ + "A region has no data, no results, or a recoverable error.", + "The user needs a concise explanation and optional next action." + ], + "avoidWhen": [ + "The operation is still pending; use InkLoading.", + "The message is a transient hover hint; use InkTooltip." + ], + "composeWith": ["InkButton", "InkLoading"], + "apiCaveats": ["Make the state and recovery action specific to the surrounding task."], + "commonMistakes": [ + "Do not use the same generic copy for empty, filtered-empty, permission, and error states." + ] + }, + "InkPopup": { + "intent": ["positioned popup", "low-level overlay surface", "custom controlled overlay"], + "preferWhen": [ + "A custom overlay needs center, edge, or coordinate positioning.", + "The consumer must own most of the interior composition." + ], + "avoidWhen": [ + "A standard confirmation dialog fits InkDialog.", + "Only supplementary hover information is needed; use InkTooltip." + ], + "composeWith": ["InkButton", "InkPicker", "InkScrim"], + "apiCaveats": [ + "Treat open state as controlled through the component's public model contract.", + "Decide explicitly whether scrim click should close the popup." + ], + "commonMistakes": [ + "Do not stack multiple scrims for one overlay.", + "Do not omit focus and keyboard behavior when building a dialog-like custom popup." + ] + }, + "InkScrim": { + "intent": ["backdrop", "background interaction blocker", "overlay foundation"], + "preferWhen": [ + "A custom overlay composition needs a full-screen backdrop and dismiss interaction.", + "No higher-level component already owns the scrim." + ], + "avoidWhen": [ + "InkDialog or InkPopup already supplies the required backdrop behavior.", + "Background interaction should remain available." + ], + "composeWith": ["InkPopup"], + "apiCaveats": [ + "Keep open state and click-to-close behavior synchronized with the owning overlay." + ], + "commonMistakes": [ + "Do not render an orphan scrim without an overlay lifecycle.", + "Do not rely on visual dimming alone for modal accessibility." + ] + }, + "InkSwitch": { + "intent": ["boolean toggle", "immediate setting", "async on/off control"], + "preferWhen": [ + "A boolean setting takes effect immediately.", + "The state transition may be asynchronous and needs pending feedback." + ], + "avoidWhen": [ + "The choice is one of several values.", + "The decision is deferred until form submission and checkbox semantics are more accurate." + ], + "composeWith": ["InkField", "InkForm", "InkLoading"], + "apiCaveats": [ + "Keep the controlled boolean or promised state consistent with the async update lifecycle.", + "Provide a visible label whenever context is not otherwise explicit." + ], + "commonMistakes": [ + "Do not optimistically show a successful state when the async change can still fail." + ] + }, + "InkTextarea": { + "intent": ["multiline text input", "description field", "long-form form value"], + "preferWhen": [ + "The user enters comments, descriptions, or other multiline text.", + "The field should share InkForm layout context." + ], + "avoidWhen": [ + "The value is one line; use InkInput.", + "The content is JSON and needs structured editing; use InkJsonEditor." + ], + "composeWith": ["InkField", "InkForm"], + "apiCaveats": [ + "The public value contract uses value/update:value rather than modelValue.", + "Use rows to communicate expected input scale without forcing fixed-height local CSS." + ], + "commonMistakes": [ + "Do not assume v-model without checking the argument used by the value contract." + ] + }, + "InkTooltip": { + "intent": ["contextual hint", "hover explanation", "supplementary label"], + "preferWhen": [ + "An icon, abbreviation, or truncated value needs optional supporting context.", + "The information is helpful but not required to complete the task." + ], + "avoidWhen": [ + "The information is critical and must remain visible.", + "The interaction is touch-only with no dependable hover or focus path." + ], + "composeWith": ["InkButton", "InkHeader"], + "apiCaveats": ["Keep tooltip content concise and preserve keyboard access to the trigger."], + "commonMistakes": [ + "Do not hide validation errors, destructive consequences, or required instructions in a tooltip." + ] + } + }, + "recipes": [ + { + "name": "Structured Form", + "intent": "Build a form whose fields share labels and layout while each control keeps its own value semantics.", + "components": [ + "InkForm", + "InkField", + "InkInput", + "InkTextarea", + "InkDropdown", + "InkPicker", + "InkSwitch", + "InkButton" + ], + "steps": [ + "Use InkForm to establish the shared field layout.", + "Choose one control per value intent: input, textarea, dropdown, picker, or switch.", + "Use InkField for custom controls or explicit field structure not already supplied by form context.", + "Keep validation, submission, and async error handling in application code.", + "Use InkButton for submit or secondary commands." + ], + "caveats": [ + "Do not normalize every control to the same model prop name; use each component's actual API.", + "Do not wrap controls in duplicate InkField labels when they already consume InkForm context." + ] + }, + { + "name": "Schema Configuration", + "intent": "Choose between an automatically generated primitive form and raw schema-aware JSON editing.", + "components": ["InkAutoForm", "InkJsonEditor", "InkForm", "InkPlaceholder"], + "steps": [ + "Use InkAutoForm when a flat schema maps cleanly to ordinary primitive controls.", + "Use InkJsonEditor when users must see or edit the raw JSON document.", + "Keep parsing and persistence errors separate from schema validation feedback.", + "Use InkPlaceholder for unrecoverable schema-loading or permission states." + ], + "caveats": [ + "Do not claim complete JSON Schema support from InkAutoForm.", + "Do not silently replace invalid user JSON with a generated object." + ] + }, + { + "name": "Destructive Confirmation", + "intent": "Prevent accidental irreversible actions with the least disruptive accurate confirmation.", + "components": ["InkDoubleCheck", "InkDialog", "InkButton", "InkLoading"], + "steps": [ + "Use InkDoubleCheck when the consequence is already understood and a second deliberate click is enough.", + "Use InkDialog when the user needs explanation, choices, or a focused decision.", + "Show pending state while the destructive request is running.", + "Close or reset confirmation state only after the application lifecycle is explicit." + ], + "caveats": [ + "A danger theme is not a confirmation mechanism.", + "Do not combine a double-check and dialog unless risk evidence justifies both layers." + ] + }, + { + "name": "Custom Overlay", + "intent": "Build a positioned or custom overlay while keeping backdrop and state ownership unambiguous.", + "components": ["InkPopup", "InkScrim", "InkButton"], + "steps": [ + "Use InkPopup as the controlled surface and choose its position deliberately.", + "Use the popup's existing scrim behavior when it meets the interaction contract.", + "Add InkScrim only when the custom composition has no backdrop owner.", + "Provide explicit close controls and application-appropriate keyboard behavior." + ], + "caveats": [ + "Prefer InkDialog when the composition is semantically a standard dialog.", + "Never render two independently controlled backdrops for one overlay." + ] + }, + { + "name": "Collection Feedback", + "intent": "Represent pending, empty, error, and paged collection states without conflating them.", + "components": ["InkLoading", "InkPlaceholder", "InkPagination", "InkButton"], + "steps": [ + "Show InkLoading only while a request is pending.", + "After completion, choose a specific empty, filtered-empty, permission, or error InkPlaceholder.", + "Render InkPagination only when a valid bounded page model exists.", + "Offer a retry or recovery InkButton only when the action is available." + ], + "caveats": [ + "Do not leave stale pagination controls visible for an invalid result set.", + "Do not use loading feedback as a permanent empty state." + ] + }, + { + "name": "Expandable Media", + "intent": "Show compact imagery with optional larger viewing and explicit fallback states.", + "components": ["InkImage", "InkLoading", "InkPlaceholder"], + "steps": [ + "Provide the image source and meaningful alt text.", + "Enable expansion only when a useful larger asset or view exists.", + "Use image loading and error events to align surrounding feedback.", + "Use InkPlaceholder only when the surrounding media region needs recovery guidance." + ] + } + ], + "integration": { + "rules": [ + "Load @inkcre/ui-web/styles once in the application style entry unless the Sass entry is intentionally compiled instead.", + "Use the package root for components and public types, /utils for utilities, /locales for locale bundles, and /uno for presetInk.", + "Keep declared peer dependencies resolvable in the consumer installation graph.", + "Do not alias raw package source or dist internals in the registry-backed production contract." + ], + "router": [ + "Adapt the consuming router to reactive currentPath and currentName values.", + "Provide the adapter through INK_ROUTER_KEY.", + "Treat useOptionalRouter returning null as a supported no-router state." + ], + "i18n": [ + "Adapt the consuming i18n system to a t function and reactive locale.", + "Provide the adapter through INK_I18N_KEY.", + "Treat useOptionalI18n returning null as the package fallback state." + ] + }, + "styling": { + "rules": [ + "Prefer system and component tokens over hard-coded visual values.", + "Use exported Sass functions and mixins rather than importing package files by filesystem path.", + "Keep ref, sys, and comp token output generated from tokens/inkcre.tokens.json.", + "Use CSS custom properties and the package theme contract instead of duplicating token maps in application code.", + "Import presetInk from @inkcre/ui-web/uno when the consuming UnoCSS setup needs the package preset." + ], + "tokenLayers": [ + "Reference tokens (ref) are primitive values such as color, space, typography, radius, elevation, breakpoint, and opacity.", + "System tokens (sys) map primitives to semantic light and dark theme roles.", + "Component tokens (comp) own component-specific semantic values." + ] + }, + "commonMistakes": [ + "Importing raw src/components or dist files instead of the package export map.", + "Assuming all controls use modelValue/update:modelValue; several components intentionally expose another value contract.", + "Using a visually similar component without matching its interaction semantics.", + "Treating InkForm as a complete validation framework rather than a layout/context owner.", + "Using InkPopup where InkDialog already provides the required confirmation structure.", + "Showing InkLoading for empty or failed content after the request has completed.", + "Hard-coding design values that already exist in the ref, sys, or comp token layers.", + "Loading every component reference when progressive disclosure only requires a few." + ] +} diff --git a/packages/web/skills/ui-web/SKILL.md b/packages/web/skills/ui-web/SKILL.md new file mode 100644 index 0000000..f8fb5be --- /dev/null +++ b/packages/web/skills/ui-web/SKILL.md @@ -0,0 +1,79 @@ +--- +name: ui-web +description: Use @inkcre/ui-web Vue components for forms, feedback, overlays, media, navigation, JSON editing, styling, router adapters, and i18n integration. +--- + + + +# InKCre UI Web + +## Use When + +Use this skill when building or reviewing Vue application UI with +`@inkcre/ui-web`, or when deciding which InKCre component composition +matches a product task. + +## Workflow + +1. Identify the product intent and interaction state. +2. Read `references/component-map.md` to shortlist components. +3. For multi-component work, read `references/composition-recipes.md`. +4. Load only the selected files under `references/components/`. +5. Check integration, styling, and common-mistake references only when relevant. +6. Implement through public package imports; do not depend on repository source paths. + +## Quick Selection + +- Application navigation and branding: InkHeader +- Single action: InkButton +- Structured form: InkForm with InkField and the matching form control +- Schema-driven primitive form: InkAutoForm +- JSON configuration editing: InkJsonEditor +- Destructive confirmation: InkDoubleCheck for a compact guard; InkDialog for a focused modal decision +- Custom or positioned overlay: InkPopup; add InkScrim only when the composition does not already own one +- Loading state: InkLoading +- Empty or error state: InkPlaceholder +- Expandable media: InkImage +- Paged collection: InkPagination + +## Import And Registration + +Use named imports for locally registered components: + +```ts +import { InkButton, InkForm, InkInput } from "@inkcre/ui-web"; +``` + +Use the plugin when the application intentionally registers every public component: + +```ts +import InKCreUiWeb from "@inkcre/ui-web"; +import "@inkcre/ui-web/styles"; + +app.use(InKCreUiWeb); +``` + +## Rules + +- Choose components by product intent and interaction semantics, not only visual resemblance. +- Prefer package components over local substitutes when the public component contract fits. +- Use public package exports and documented subpaths; never import src/components files. +- Keep form labels, controls, and layout context explicit through InkForm and InkField. +- Distinguish loading, empty, error, and confirmation states instead of using one generic feedback surface. +- Read only the component references needed for the current task. + +## References + +- `references/component-map.md`: intent-to-component routing. +- `references/composition-recipes.md`: reviewed multi-component workflows. +- `references/integration.md`: installation, router, and i18n boundaries. +- `references/styling-and-themes.md`: CSS, Sass, tokens, and themes. +- `references/common-mistakes.md`: package-specific failure modes. +- `references/components/Ink*.md`: generated public API facts plus reviewed guidance. + +## Avoid + +- Do not invent props or events from conventions used by another UI library. +- Do not treat low-level InkPopup or InkScrim as a replacement for InkDialog when the standard dialog workflow fits. +- Do not expose repository paths, generated implementation files, or internal prop helpers to consumers. +- Do not edit this generated skill tree directly; update skill.seed.json or public component facts and regenerate it. diff --git a/packages/web/skills/ui-web/references/common-mistakes.md b/packages/web/skills/ui-web/references/common-mistakes.md new file mode 100644 index 0000000..0a9e88b --- /dev/null +++ b/packages/web/skills/ui-web/references/common-mistakes.md @@ -0,0 +1,105 @@ + + +# Common Mistakes + +- Importing raw src/components or dist files instead of the package export map. +- Assuming all controls use modelValue/update:modelValue; several components intentionally expose another value contract. +- Using a visually similar component without matching its interaction semantics. +- Treating InkForm as a complete validation framework rather than a layout/context owner. +- Using InkPopup where InkDialog already provides the required confirmation structure. +- Showing InkLoading for empty or failed content after the request has completed. +- Hard-coding design values that already exist in the ref, sys, or comp token layers. +- Loading every component reference when progressive disclosure only requires a few. + +## InkAutoForm + +- Do not assume complete JSON Schema coverage. +- Do not use it merely to avoid designing a workflow-specific form. + +## InkButton + +- Do not replace a destructive confirmation flow with a danger-colored button alone. + +## InkDatetimePickerView + +- Do not duplicate popup, formatting, and field responsibilities already owned by InkPicker. + +## InkDialog + +- Do not nest a second independently controlled scrim beneath the dialog. +- Do not invent open/update:open semantics without checking the emitted events. + +## InkDoubleCheck + +- Do not use a two-click guard for every action. +- Do not hide the destructive meaning behind an unlabeled icon. + +## InkDropdown + +- Do not assume an empty model triggers lazy loading. +- Do not mix static options and refresher behavior without defining precedence. + +## InkField + +- Do not create nested duplicate labels around controls that already consume form context. + +## InkForm + +- Do not infer full validation orchestration from the form wrapper. +- Do not bypass the shared layout with unrelated local field markup. + +## InkHeader + +- Do not use the application header as a generic card or section title. + +## InkImage + +- Do not enable expansion when the source cannot provide a useful larger view. + +## InkInput + +- Do not use the inline visual mode as a substitute for explicit edit permissions. + +## InkJsonEditor + +- Do not silently coerce invalid JSON into a different value. +- Do not use it for ordinary multiline text. + +## InkLoading + +- Do not leave a loading indicator active after the operation reaches an empty or error state. + +## InkPagination + +- Do not render inaccessible or clickable pages outside the valid range. + +## InkPicker + +- Do not assume every picker value is a Date. +- Do not duplicate the popup state outside the component without a controlled-state reason. + +## InkPlaceholder + +- Do not use the same generic copy for empty, filtered-empty, permission, and error states. + +## InkPopup + +- Do not stack multiple scrims for one overlay. +- Do not omit focus and keyboard behavior when building a dialog-like custom popup. + +## InkScrim + +- Do not render an orphan scrim without an overlay lifecycle. +- Do not rely on visual dimming alone for modal accessibility. + +## InkSwitch + +- Do not optimistically show a successful state when the async change can still fail. + +## InkTextarea + +- Do not assume v-model without checking the argument used by the value contract. + +## InkTooltip + +- Do not hide validation errors, destructive consequences, or required instructions in a tooltip. diff --git a/packages/web/skills/ui-web/references/component-map.md b/packages/web/skills/ui-web/references/component-map.md new file mode 100644 index 0000000..4ed1a0a --- /dev/null +++ b/packages/web/skills/ui-web/references/component-map.md @@ -0,0 +1,45 @@ + + +# Component Map + +Choose by user intent before choosing by visual resemblance. Open only the +component references relevant to the task. + +## controls + +- [InkButton](components/InkButton.md): button; single-click action; icon or text command +- [InkHeader](components/InkHeader.md): application header; branding; top navigation +- [InkPagination](components/InkPagination.md): pagination; previous and next navigation; paged collection +- [InkSwitch](components/InkSwitch.md): boolean toggle; immediate setting; async on/off control + +## feedback + +- [InkDialog](components/InkDialog.md): dialog; confirmation modal; focused overlay workflow +- [InkDoubleCheck](components/InkDoubleCheck.md): two-step confirmation; accidental-click guard; compact destructive action +- [InkLoading](components/InkLoading.md): loading indicator; async progress feedback; pending state +- [InkPlaceholder](components/InkPlaceholder.md): empty state; error state; unavailable content guidance +- [InkPopup](components/InkPopup.md): positioned popup; low-level overlay surface; custom controlled overlay +- [InkTooltip](components/InkTooltip.md): contextual hint; hover explanation; supplementary label + +## forms + +- [InkAutoForm](components/InkAutoForm.md): schema-driven form; flat JSON Schema editor; automatic primitive controls +- [InkDropdown](components/InkDropdown.md): single selection; dropdown options; lazy-loaded choice +- [InkField](components/InkField.md): form field wrapper; label and value layout; field-level structure +- [InkForm](components/InkForm.md): form layout context; grouped fields; consistent field composition +- [InkInput](components/InkInput.md): single-line text input; inline text editing; form text field +- [InkPicker](components/InkPicker.md): formatted value picker; date or time field; custom popup selection +- [InkTextarea](components/InkTextarea.md): multiline text input; description field; long-form form value + +## media + +- [InkImage](components/InkImage.md): image display; thumbnail; click-to-expand media + +## overlays + +- [InkScrim](components/InkScrim.md): backdrop; background interaction blocker; overlay foundation + +## specialized + +- [InkDatetimePickerView](components/InkDatetimePickerView.md): date and time columns; weekday picker view; low-level datetime selection +- [InkJsonEditor](components/InkJsonEditor.md): JSON text editor; schema-aware JSON configuration; structured text validation diff --git a/packages/web/skills/ui-web/references/components/InkAutoForm.md b/packages/web/skills/ui-web/references/components/InkAutoForm.md new file mode 100644 index 0000000..76f8d3f --- /dev/null +++ b/packages/web/skills/ui-web/references/components/InkAutoForm.md @@ -0,0 +1,42 @@ + + +# InkAutoForm + +## Intent + +- schema-driven form +- flat JSON Schema editor +- automatic primitive controls + +## Prefer When + +- A flat JSON Schema should produce consistent controls with little application boilerplate. +- String, number, and boolean properties can map to the package's existing field controls. + +## Avoid When + +- The workflow needs deeply nested schemas, conditional branches, arrays, or bespoke field composition. +- The user should edit raw JSON text; use InkJsonEditor instead. + +## Compose With + +`InkForm`, `InkInput`, `InkSwitch` + +## Public API Facts + +- Import: `import { InkAutoForm } from "@inkcre/ui-web";` +- Props: `formData`, `layout`, `schema` +- Events: `update:formData` +- Slots: None +- Public types: `JSONSchema`, `JSONSchemaProperty` +- Story variants: `Text Fields with Validation`, `Textarea (Long Text)`, `Boolean Switches`, `Dropdown (Enum Values)`, `Date and Time Pickers`, `Default Values from Schema`, `Validation Errors`, `Invalid Schema Handling`, `Complex Form (Mixed Field Types)`, `Inline Layout`, `Row Layout` + +## API Caveats + +- Treat the supported schema subset as a deliberate flat-form contract. +- Supply custom component mappings only when the generated default control is insufficient. + +## Common Mistakes + +- Do not assume complete JSON Schema coverage. +- Do not use it merely to avoid designing a workflow-specific form. diff --git a/packages/web/skills/ui-web/references/components/InkButton.md b/packages/web/skills/ui-web/references/components/InkButton.md new file mode 100644 index 0000000..a1f3dd7 --- /dev/null +++ b/packages/web/skills/ui-web/references/components/InkButton.md @@ -0,0 +1,41 @@ + + +# InkButton + +## Intent + +- button +- single-click action +- icon or text command + +## Prefer When + +- The user initiates one immediate command. +- An action needs package-consistent theme, size, icon, or loading treatment. + +## Avoid When + +- The control represents navigation and should preserve link semantics. +- The workflow requires a multi-step confirmation surface rather than one click. + +## Compose With + +`InkDialog`, `InkDoubleCheck`, `InkHeader`, `InkTooltip` + +## Public API Facts + +- Import: `import { InkButton } from "@inkcre/ui-web";` +- Props: `icon`, `iconPlacement`, `isLoading`, `size`, `text`, `theme`, `type` +- Events: `click` +- Slots: `default`, `prefix-icon`, `suffix-icon` +- Public types: None +- Story variants: `Subtle`, `Primary`, `Danger`, `Medium (Default)`, `Small`, `Loading State - Subtle`, `Loading State - Primary`, `Loading State - Danger`, `Very Long Text`, `Short Text`, `Icon Prefix`, `Icon Suffix`, `Icon Only (Square)` + +## API Caveats + +- Choose theme from the component's actual public values; do not borrow variant names from another library. +- Keep icon-only commands accessible with surrounding context or a tooltip. + +## Common Mistakes + +- Do not replace a destructive confirmation flow with a danger-colored button alone. diff --git a/packages/web/skills/ui-web/references/components/InkDatetimePickerView.md b/packages/web/skills/ui-web/references/components/InkDatetimePickerView.md new file mode 100644 index 0000000..3b96806 --- /dev/null +++ b/packages/web/skills/ui-web/references/components/InkDatetimePickerView.md @@ -0,0 +1,41 @@ + + +# InkDatetimePickerView + +## Intent + +- date and time columns +- weekday picker view +- low-level datetime selection + +## Prefer When + +- A picker composition needs scrollable date, time, datetime, or weekday columns. +- InkPicker owns the field and popup while this component owns the selection view. + +## Avoid When + +- A native text/date input is sufficient. +- A standalone form field is required without a surrounding InkPicker composition. + +## Compose With + +`InkPicker`, `InkPopup` + +## Public API Facts + +- Import: `import { InkDatetimePickerView } from "@inkcre/ui-web";` +- Props: `hourFormat`, `maxDate`, `minDate`, `mode`, `modelValue` +- Events: `update:modelValue` +- Slots: None +- Public types: None +- Story variants: `Basic` + +## API Caveats + +- Use Date values for date/time modes and preserve the selected mode contract. +- Treat this as a view primitive, not the complete labelled form control. + +## Common Mistakes + +- Do not duplicate popup, formatting, and field responsibilities already owned by InkPicker. diff --git a/packages/web/skills/ui-web/references/components/InkDialog.md b/packages/web/skills/ui-web/references/components/InkDialog.md new file mode 100644 index 0000000..4d0ab4c --- /dev/null +++ b/packages/web/skills/ui-web/references/components/InkDialog.md @@ -0,0 +1,42 @@ + + +# InkDialog + +## Intent + +- dialog +- confirmation modal +- focused overlay workflow + +## Prefer When + +- A focused decision needs a title, content, cancel/confirm actions, and a backdrop. +- A short modal workflow benefits from the package's complete dialog structure. + +## Avoid When + +- Only a low-level positioned surface is needed; use InkPopup. +- A compact second-click guard is enough; use InkDoubleCheck. + +## Compose With + +`InkButton`, `InkForm`, `InkLoading`, `InkPopup` + +## Public API Facts + +- Import: `import { InkDialog } from "@inkcre/ui-web";` +- Props: `cancelText`, `closeOnScrim`, `confirmText`, `modelValue`, `position`, `showCancel`, `showConfirm`, `subtitle`, `title` +- Events: `cancel`, `confirm`, `update:modelValue` +- Slots: `default`, `footer`, `header` +- Public types: None +- Story variants: `Basic`, `with Custom Slots`, `Async`, `Without Cancel` + +## API Caveats + +- Use the actual model event contract and slots documented in the generated API facts. +- Keep destructive consequences visible in the dialog content before confirmation. + +## Common Mistakes + +- Do not nest a second independently controlled scrim beneath the dialog. +- Do not invent open/update:open semantics without checking the emitted events. diff --git a/packages/web/skills/ui-web/references/components/InkDoubleCheck.md b/packages/web/skills/ui-web/references/components/InkDoubleCheck.md new file mode 100644 index 0000000..ff8b607 --- /dev/null +++ b/packages/web/skills/ui-web/references/components/InkDoubleCheck.md @@ -0,0 +1,42 @@ + + +# InkDoubleCheck + +## Intent + +- two-step confirmation +- accidental-click guard +- compact destructive action + +## Prefer When + +- A nearby action needs one additional deliberate click without opening a modal. +- The consequence is already understood and only accidental activation must be prevented. + +## Avoid When + +- The user needs explanatory content, input, or multiple choices; use InkDialog. +- The action is harmless and the extra confirmation would add friction. + +## Compose With + +`InkButton`, `InkTooltip` + +## Public API Facts + +- Import: `import { InkDoubleCheck } from "@inkcre/ui-web";` +- Props: `cancelText`, `confirmText`, `message`, `title` +- Events: `confirm` +- Slots: `default` +- Public types: None +- Story variants: `Delete Confirmation` + +## API Caveats + +- Make the second state visually and textually clear. +- Reset confirmation state when the surrounding workflow changes. + +## Common Mistakes + +- Do not use a two-click guard for every action. +- Do not hide the destructive meaning behind an unlabeled icon. diff --git a/packages/web/skills/ui-web/references/components/InkDropdown.md b/packages/web/skills/ui-web/references/components/InkDropdown.md new file mode 100644 index 0000000..3e48908 --- /dev/null +++ b/packages/web/skills/ui-web/references/components/InkDropdown.md @@ -0,0 +1,43 @@ + + +# InkDropdown + +## Intent + +- single selection +- dropdown options +- lazy-loaded choice + +## Prefer When + +- The user selects one value from a known or asynchronously loaded option list. +- Optional stepping controls help move through an ordered option set. + +## Avoid When + +- The user enters free text; use InkInput. +- The interaction needs multi-selection or arbitrary tagging. + +## Compose With + +`InkField`, `InkForm` + +## Public API Facts + +- Import: `import { InkDropdown } from "@inkcre/ui-web";` +- Props: `displayAs`, `editable`, `enableStepping`, `label`, `layout`, `modelValue`, `options`, `placeholder`, `prop`, `refresher`, `required` +- Events: `change`, `update:modelValue`, `update:options` +- Slots: None +- Public types: `DropdownOption` +- Story variants: `Basic`, `With Refresh`, `Async Options`, `With Descriptions`, `Preselected Async Value`, `With Stepping`, `Stepping & Refresh`, `Keyboard Navigation` + +## API Caveats + +- Use DropdownOption for public option values. +- Choose either static options or the refresher lifecycle deliberately. +- Editable state controls whether selection interactions are available. + +## Common Mistakes + +- Do not assume an empty model triggers lazy loading. +- Do not mix static options and refresher behavior without defining precedence. diff --git a/packages/web/skills/ui-web/references/components/InkField.md b/packages/web/skills/ui-web/references/components/InkField.md new file mode 100644 index 0000000..b207be0 --- /dev/null +++ b/packages/web/skills/ui-web/references/components/InkField.md @@ -0,0 +1,40 @@ + + +# InkField + +## Intent + +- form field wrapper +- label and value layout +- field-level structure + +## Prefer When + +- A control needs a label, required state, and consistent inline, row, or column layout. +- A custom control must align with other InkForm fields. + +## Avoid When + +- Content is not a form value. +- A control already renders its own field wrapper through InkForm context and label props. + +## Compose With + +`InkDropdown`, `InkInput`, `InkJsonEditor`, `InkPicker`, `InkTextarea` + +## Public API Facts + +- Import: `import { InkField } from "@inkcre/ui-web";` +- Props: `editable`, `label`, `layout`, `required`, `value` +- Events: `value-click` +- Slots: `default`, `label-right` +- Public types: None +- Story variants: `Column`, `Inline`, `Row`, `Optional`, `Required`, `Very Long Label`, `Very Long Value`, `Empty Value` + +## API Caveats + +- Keep field layout consistent with the surrounding InkForm unless an intentional local override is needed. + +## Common Mistakes + +- Do not create nested duplicate labels around controls that already consume form context. diff --git a/packages/web/skills/ui-web/references/components/InkForm.md b/packages/web/skills/ui-web/references/components/InkForm.md new file mode 100644 index 0000000..df3c050 --- /dev/null +++ b/packages/web/skills/ui-web/references/components/InkForm.md @@ -0,0 +1,42 @@ + + +# InkForm + +## Intent + +- form layout context +- grouped fields +- consistent field composition + +## Prefer When + +- Multiple package controls should share one field layout. +- Child controls should receive layout context through the package's provider contract. + +## Avoid When + +- The content is read-only or is not a form. +- The application expects the component to provide a complete validation framework. + +## Compose With + +`InkAutoForm`, `InkDropdown`, `InkField`, `InkInput`, `InkJsonEditor`, `InkPicker`, `InkSwitch`, `InkTextarea` + +## Public API Facts + +- Import: `import { InkForm } from "@inkcre/ui-web";` +- Props: `layout` +- Events: `submit` +- Slots: `default` +- Public types: None +- Story variants: `Column Layout`, `Inline Layout` + +## API Caveats + +- InkForm primarily owns layout context; application validation and submission remain explicit. +- Use the emitted native submit event rather than relying on browser navigation. + +## Common Mistakes + +- Do not infer full validation orchestration from the form wrapper. +- Do not bypass the shared layout with unrelated local field markup. diff --git a/packages/web/skills/ui-web/references/components/InkHeader.md b/packages/web/skills/ui-web/references/components/InkHeader.md new file mode 100644 index 0000000..822b891 --- /dev/null +++ b/packages/web/skills/ui-web/references/components/InkHeader.md @@ -0,0 +1,40 @@ + + +# InkHeader + +## Intent + +- application header +- branding +- top navigation + +## Prefer When + +- An application shell needs logo, title, current-page context, or menu actions. +- The header should consume the optional router adapter. + +## Avoid When + +- A content section only needs a heading. +- A footer or generic horizontal layout is required. + +## Compose With + +`InkButton`, `InkTooltip` + +## Public API Facts + +- Import: `import { InkHeader } from "@inkcre/ui-web";` +- Props: `logoSrc`, `pageTitle`, `title` +- Events: `menu-click`, `title-click` +- Slots: `right-icon` +- Public types: None +- Story variants: `Basic`, `With Logo`, `Without Logo`, `Auto Page Title` + +## API Caveats + +- Router integration is adapter-based and optional; the component does not import the consuming router. + +## Common Mistakes + +- Do not use the application header as a generic card or section title. diff --git a/packages/web/skills/ui-web/references/components/InkImage.md b/packages/web/skills/ui-web/references/components/InkImage.md new file mode 100644 index 0000000..53fc5a5 --- /dev/null +++ b/packages/web/skills/ui-web/references/components/InkImage.md @@ -0,0 +1,41 @@ + + +# InkImage + +## Intent + +- image display +- thumbnail +- click-to-expand media + +## Prefer When + +- An image needs package-consistent loading, error, and expandable viewing behavior. +- A compact thumbnail should open a larger view. + +## Avoid When + +- A decorative image needs only native img behavior. +- The media is not an image or needs a dedicated gallery workflow. + +## Compose With + +`InkLoading`, `InkPlaceholder`, `InkPopup` + +## Public API Facts + +- Import: `import { InkImage } from "@inkcre/ui-web";` +- Props: `alt`, `expanded`, `lazy`, `src`, `title` +- Events: `close`, `error`, `expand`, `update:expanded` +- Slots: `expanded-footer`, `expanded-header`, `thumbnail` +- Public types: None +- Story variants: `Basic Usage`, `With Title`, `Custom Size (Style + Class)`, `Custom Thumbnail Slot`, `Eager Loading (lazy: false)`, `With Expanded Footer`, `With Custom Header` + +## API Caveats + +- Always provide meaningful alt text for informative images. +- Handle the public error payload instead of relying on internal image state. + +## Common Mistakes + +- Do not enable expansion when the source cannot provide a useful larger view. diff --git a/packages/web/skills/ui-web/references/components/InkInput.md b/packages/web/skills/ui-web/references/components/InkInput.md new file mode 100644 index 0000000..73d6178 --- /dev/null +++ b/packages/web/skills/ui-web/references/components/InkInput.md @@ -0,0 +1,41 @@ + + +# InkInput + +## Intent + +- single-line text input +- inline text editing +- form text field + +## Prefer When + +- The user enters or edits one line of text. +- A details view needs the component's inline editing mode. + +## Avoid When + +- The value spans multiple lines; use InkTextarea. +- The value is selected from options; use InkDropdown or InkPicker. + +## Compose With + +`InkField`, `InkForm` + +## Public API Facts + +- Import: `import { InkInput } from "@inkcre/ui-web";` +- Props: `editable`, `label`, `layout`, `modelValue`, `placeholder`, `prop`, `required`, `type` +- Events: `update:modelValue` +- Slots: `default` +- Public types: None +- Story variants: `Column Layout`, `Inline Layout`, `Optional`, `Required`, `Inline Editable` + +## API Caveats + +- Use the component's value event contract rather than assuming modelValue naming. +- When used inside InkForm, let form context own the default field layout. + +## Common Mistakes + +- Do not use the inline visual mode as a substitute for explicit edit permissions. diff --git a/packages/web/skills/ui-web/references/components/InkJsonEditor.md b/packages/web/skills/ui-web/references/components/InkJsonEditor.md new file mode 100644 index 0000000..774cb3f --- /dev/null +++ b/packages/web/skills/ui-web/references/components/InkJsonEditor.md @@ -0,0 +1,42 @@ + + +# InkJsonEditor + +## Intent + +- JSON text editor +- schema-aware JSON configuration +- structured text validation + +## Prefer When + +- Users need to edit raw JSON with completion and validation support. +- A schema should guide a CodeMirror-based JSON editing experience. + +## Avoid When + +- A flat schema can be represented as ordinary form controls; consider InkAutoForm. +- The content is general prose or arbitrary code. + +## Compose With + +`InkField`, `InkForm`, `InkPlaceholder` + +## Public API Facts + +- Import: `import { InkJsonEditor } from "@inkcre/ui-web";` +- Props: `editable`, `label`, `layout`, `modelValue`, `placeholder`, `prop`, `required`, `rows`, `schema`, `schemaUri` +- Events: `update:modelValue` +- Slots: None +- Public types: None +- Story variants: `Basic`, `JSON Schema`, `In popup` + +## API Caveats + +- The model is JSON text, not an automatically parsed application object. +- The CodeMirror and VS Code language-service packages remain part of the declared peer contract. + +## Common Mistakes + +- Do not silently coerce invalid JSON into a different value. +- Do not use it for ordinary multiline text. diff --git a/packages/web/skills/ui-web/references/components/InkLoading.md b/packages/web/skills/ui-web/references/components/InkLoading.md new file mode 100644 index 0000000..58f4121 --- /dev/null +++ b/packages/web/skills/ui-web/references/components/InkLoading.md @@ -0,0 +1,40 @@ + + +# InkLoading + +## Intent + +- loading indicator +- async progress feedback +- pending state + +## Prefer When + +- An operation is currently pending and the user needs immediate visual feedback. +- A screen, region, or inline action has no determinate progress value. + +## Avoid When + +- Content is empty or failed; use InkPlaceholder. +- A static skeleton layout is required. + +## Compose With + +`InkButton`, `InkDialog`, `InkImage` + +## Public API Facts + +- Import: `import { InkLoading } from "@inkcre/ui-web";` +- Props: `density`, `size` +- Events: None +- Slots: None +- Public types: None +- Story variants: `Basic` + +## API Caveats + +- Choose size and density for the surrounding context instead of scaling with local CSS. + +## Common Mistakes + +- Do not leave a loading indicator active after the operation reaches an empty or error state. diff --git a/packages/web/skills/ui-web/references/components/InkPagination.md b/packages/web/skills/ui-web/references/components/InkPagination.md new file mode 100644 index 0000000..02c6ef6 --- /dev/null +++ b/packages/web/skills/ui-web/references/components/InkPagination.md @@ -0,0 +1,41 @@ + + +# InkPagination + +## Intent + +- pagination +- previous and next navigation +- paged collection + +## Prefer When + +- A dataset has an explicit current page and total page count. +- Users need deterministic movement through bounded pages. + +## Avoid When + +- All content fits comfortably on one page. +- The product deliberately uses infinite scrolling without page identity. + +## Compose With + +`InkLoading`, `InkPlaceholder` + +## Public API Facts + +- Import: `import { InkPagination } from "@inkcre/ui-web";` +- Props: `currentPage`, `totalPages`, `type` +- Events: `page-change` +- Slots: None +- Public types: None +- Story variants: `Few Pages (1-5)`, `Many Pages (1-20)`, `First Page`, `Last Page`, `Single Page Only`, `Text Type - Few Pages`, `Text Type - Many Pages` + +## API Caveats + +- Keep the controlled page value aligned with the data request lifecycle. +- Use the text mode only when numbered page affordances are intentionally unnecessary. + +## Common Mistakes + +- Do not render inaccessible or clickable pages outside the valid range. diff --git a/packages/web/skills/ui-web/references/components/InkPicker.md b/packages/web/skills/ui-web/references/components/InkPicker.md new file mode 100644 index 0000000..de9a620 --- /dev/null +++ b/packages/web/skills/ui-web/references/components/InkPicker.md @@ -0,0 +1,42 @@ + + +# InkPicker + +## Intent + +- formatted value picker +- date or time field +- custom popup selection + +## Prefer When + +- A value needs field layout, display formatting, a popup, and a custom selection surface. +- Date, time, datetime, or weekday selection should use InkDatetimePickerView. + +## Avoid When + +- A simple option list fits InkDropdown. +- A single-line text field fits InkInput. + +## Compose With + +`InkDatetimePickerView`, `InkField`, `InkForm`, `InkPopup` + +## Public API Facts + +- Import: `import { InkPicker } from "@inkcre/ui-web";` +- Props: `displayValueAs`, `editable`, `formatter`, `label`, `layout`, `modelValue`, `prop`, `required`, `showPopup`, `type` +- Events: `pick`, `update:modelValue`, `update:showPopup` +- Slots: `default` +- Public types: None +- Story variants: `Date Picker`, `Required Field` + +## API Caveats + +- The generic value type must stay consistent across formatter, model value, and emitted update. +- Custom picker content uses the default slot and owns its own value interaction. + +## Common Mistakes + +- Do not assume every picker value is a Date. +- Do not duplicate the popup state outside the component without a controlled-state reason. diff --git a/packages/web/skills/ui-web/references/components/InkPlaceholder.md b/packages/web/skills/ui-web/references/components/InkPlaceholder.md new file mode 100644 index 0000000..d5d8288 --- /dev/null +++ b/packages/web/skills/ui-web/references/components/InkPlaceholder.md @@ -0,0 +1,40 @@ + + +# InkPlaceholder + +## Intent + +- empty state +- error state +- unavailable content guidance + +## Prefer When + +- A region has no data, no results, or a recoverable error. +- The user needs a concise explanation and optional next action. + +## Avoid When + +- The operation is still pending; use InkLoading. +- The message is a transient hover hint; use InkTooltip. + +## Compose With + +`InkButton`, `InkLoading` + +## Public API Facts + +- Import: `import { InkPlaceholder } from "@inkcre/ui-web";` +- Props: `description`, `illustration`, `state`, `title` +- Events: None +- Slots: `actions`, `description`, `illustration`, `title` +- Public types: None +- Story variants: `Empty State - Default`, `Empty State - Custom Text`, `Empty State - Custom Illustration`, `Error State - Default`, `Error State - Custom Text`, `Empty State - With Actions`, `Error State - With Retry Action`, `Custom Illustration Slot`, `Custom Title Slot`, `Fully Custom Content`, `Long Description` + +## API Caveats + +- Make the state and recovery action specific to the surrounding task. + +## Common Mistakes + +- Do not use the same generic copy for empty, filtered-empty, permission, and error states. diff --git a/packages/web/skills/ui-web/references/components/InkPopup.md b/packages/web/skills/ui-web/references/components/InkPopup.md new file mode 100644 index 0000000..4803c19 --- /dev/null +++ b/packages/web/skills/ui-web/references/components/InkPopup.md @@ -0,0 +1,42 @@ + + +# InkPopup + +## Intent + +- positioned popup +- low-level overlay surface +- custom controlled overlay + +## Prefer When + +- A custom overlay needs center, edge, or coordinate positioning. +- The consumer must own most of the interior composition. + +## Avoid When + +- A standard confirmation dialog fits InkDialog. +- Only supplementary hover information is needed; use InkTooltip. + +## Compose With + +`InkButton`, `InkPicker`, `InkScrim` + +## Public API Facts + +- Import: `import { InkPopup } from "@inkcre/ui-web";` +- Props: `closeOnScrim`, `position` +- Events: `scrim-click` +- Slots: `default` +- Public types: None +- Story variants: `Default Popup` + +## API Caveats + +- Treat open state as controlled through the component's public model contract. +- Decide explicitly whether scrim click should close the popup. + +## Common Mistakes + +- Do not stack multiple scrims for one overlay. +- Do not omit focus and keyboard behavior when building a dialog-like custom popup. diff --git a/packages/web/skills/ui-web/references/components/InkScrim.md b/packages/web/skills/ui-web/references/components/InkScrim.md new file mode 100644 index 0000000..5f1611e --- /dev/null +++ b/packages/web/skills/ui-web/references/components/InkScrim.md @@ -0,0 +1,41 @@ + + +# InkScrim + +## Intent + +- backdrop +- background interaction blocker +- overlay foundation + +## Prefer When + +- A custom overlay composition needs a full-screen backdrop and dismiss interaction. +- No higher-level component already owns the scrim. + +## Avoid When + +- InkDialog or InkPopup already supplies the required backdrop behavior. +- Background interaction should remain available. + +## Compose With + +`InkPopup` + +## Public API Facts + +- Import: `import { InkScrim } from "@inkcre/ui-web";` +- Props: `closeOnEscape`, `closeOnScrim`, `showCloseButton` +- Events: `close`, `scrim-click`, `update:open` +- Slots: `default` +- Public types: None +- Story variants: `Open Scrim`, `Closed Scrim`, `Dismissible (closeOnScrim: true)`, `Non-Dismissible (closeOnScrim: false)` + +## API Caveats + +- Keep open state and click-to-close behavior synchronized with the owning overlay. + +## Common Mistakes + +- Do not render an orphan scrim without an overlay lifecycle. +- Do not rely on visual dimming alone for modal accessibility. diff --git a/packages/web/skills/ui-web/references/components/InkSwitch.md b/packages/web/skills/ui-web/references/components/InkSwitch.md new file mode 100644 index 0000000..00572a8 --- /dev/null +++ b/packages/web/skills/ui-web/references/components/InkSwitch.md @@ -0,0 +1,41 @@ + + +# InkSwitch + +## Intent + +- boolean toggle +- immediate setting +- async on/off control + +## Prefer When + +- A boolean setting takes effect immediately. +- The state transition may be asynchronous and needs pending feedback. + +## Avoid When + +- The choice is one of several values. +- The decision is deferred until form submission and checkbox semantics are more accurate. + +## Compose With + +`InkField`, `InkForm`, `InkLoading` + +## Public API Facts + +- Import: `import { InkSwitch } from "@inkcre/ui-web";` +- Props: `isSwitching`, `modelValue`, `offText`, `onText`, `showLabel`, `size` +- Events: `update:modelValue` +- Slots: None +- Public types: None +- Story variants: `Extra Small`, `Small`, `Medium`, `Large`, `Off`, `On` + +## API Caveats + +- Keep the controlled boolean or promised state consistent with the async update lifecycle. +- Provide a visible label whenever context is not otherwise explicit. + +## Common Mistakes + +- Do not optimistically show a successful state when the async change can still fail. diff --git a/packages/web/skills/ui-web/references/components/InkTextarea.md b/packages/web/skills/ui-web/references/components/InkTextarea.md new file mode 100644 index 0000000..52508bf --- /dev/null +++ b/packages/web/skills/ui-web/references/components/InkTextarea.md @@ -0,0 +1,41 @@ + + +# InkTextarea + +## Intent + +- multiline text input +- description field +- long-form form value + +## Prefer When + +- The user enters comments, descriptions, or other multiline text. +- The field should share InkForm layout context. + +## Avoid When + +- The value is one line; use InkInput. +- The content is JSON and needs structured editing; use InkJsonEditor. + +## Compose With + +`InkField`, `InkForm` + +## Public API Facts + +- Import: `import { InkTextarea } from "@inkcre/ui-web";` +- Props: `editable`, `label`, `layout`, `placeholder`, `prop`, `required`, `rows`, `value` +- Events: `update:value` +- Slots: None +- Public types: None +- Story variants: `Default`, `Optional`, `Required`, `Customize Rows` + +## API Caveats + +- The public value contract uses value/update:value rather than modelValue. +- Use rows to communicate expected input scale without forcing fixed-height local CSS. + +## Common Mistakes + +- Do not assume v-model without checking the argument used by the value contract. diff --git a/packages/web/skills/ui-web/references/components/InkTooltip.md b/packages/web/skills/ui-web/references/components/InkTooltip.md new file mode 100644 index 0000000..11054c3 --- /dev/null +++ b/packages/web/skills/ui-web/references/components/InkTooltip.md @@ -0,0 +1,40 @@ + + +# InkTooltip + +## Intent + +- contextual hint +- hover explanation +- supplementary label + +## Prefer When + +- An icon, abbreviation, or truncated value needs optional supporting context. +- The information is helpful but not required to complete the task. + +## Avoid When + +- The information is critical and must remain visible. +- The interaction is touch-only with no dependable hover or focus path. + +## Compose With + +`InkButton`, `InkHeader` + +## Public API Facts + +- Import: `import { InkTooltip } from "@inkcre/ui-web";` +- Props: `content`, `position` +- Events: None +- Slots: `default` +- Public types: None +- Story variants: `Top`, `Bottom`, `Left`, `Right`, `Long Text` + +## API Caveats + +- Keep tooltip content concise and preserve keyboard access to the trigger. + +## Common Mistakes + +- Do not hide validation errors, destructive consequences, or required instructions in a tooltip. diff --git a/packages/web/skills/ui-web/references/composition-recipes.md b/packages/web/skills/ui-web/references/composition-recipes.md new file mode 100644 index 0000000..4ef672b --- /dev/null +++ b/packages/web/skills/ui-web/references/composition-recipes.md @@ -0,0 +1,98 @@ + + +# Composition Recipes + +These recipes are reviewed product guidance. Component references provide the +current generated API facts used to implement them. + +## Structured Form + +**Intent:** Build a form whose fields share labels and layout while each control keeps its own value semantics. + +**Components:** `InkForm`, `InkField`, `InkInput`, `InkTextarea`, `InkDropdown`, `InkPicker`, `InkSwitch`, `InkButton` + +1. Use InkForm to establish the shared field layout. +2. Choose one control per value intent: input, textarea, dropdown, picker, or switch. +3. Use InkField for custom controls or explicit field structure not already supplied by form context. +4. Keep validation, submission, and async error handling in application code. +5. Use InkButton for submit or secondary commands. + +**Caveats** + +- Do not normalize every control to the same model prop name; use each component's actual API. +- Do not wrap controls in duplicate InkField labels when they already consume InkForm context. + +## Schema Configuration + +**Intent:** Choose between an automatically generated primitive form and raw schema-aware JSON editing. + +**Components:** `InkAutoForm`, `InkJsonEditor`, `InkForm`, `InkPlaceholder` + +1. Use InkAutoForm when a flat schema maps cleanly to ordinary primitive controls. +2. Use InkJsonEditor when users must see or edit the raw JSON document. +3. Keep parsing and persistence errors separate from schema validation feedback. +4. Use InkPlaceholder for unrecoverable schema-loading or permission states. + +**Caveats** + +- Do not claim complete JSON Schema support from InkAutoForm. +- Do not silently replace invalid user JSON with a generated object. + +## Destructive Confirmation + +**Intent:** Prevent accidental irreversible actions with the least disruptive accurate confirmation. + +**Components:** `InkDoubleCheck`, `InkDialog`, `InkButton`, `InkLoading` + +1. Use InkDoubleCheck when the consequence is already understood and a second deliberate click is enough. +2. Use InkDialog when the user needs explanation, choices, or a focused decision. +3. Show pending state while the destructive request is running. +4. Close or reset confirmation state only after the application lifecycle is explicit. + +**Caveats** + +- A danger theme is not a confirmation mechanism. +- Do not combine a double-check and dialog unless risk evidence justifies both layers. + +## Custom Overlay + +**Intent:** Build a positioned or custom overlay while keeping backdrop and state ownership unambiguous. + +**Components:** `InkPopup`, `InkScrim`, `InkButton` + +1. Use InkPopup as the controlled surface and choose its position deliberately. +2. Use the popup's existing scrim behavior when it meets the interaction contract. +3. Add InkScrim only when the custom composition has no backdrop owner. +4. Provide explicit close controls and application-appropriate keyboard behavior. + +**Caveats** + +- Prefer InkDialog when the composition is semantically a standard dialog. +- Never render two independently controlled backdrops for one overlay. + +## Collection Feedback + +**Intent:** Represent pending, empty, error, and paged collection states without conflating them. + +**Components:** `InkLoading`, `InkPlaceholder`, `InkPagination`, `InkButton` + +1. Show InkLoading only while a request is pending. +2. After completion, choose a specific empty, filtered-empty, permission, or error InkPlaceholder. +3. Render InkPagination only when a valid bounded page model exists. +4. Offer a retry or recovery InkButton only when the action is available. + +**Caveats** + +- Do not leave stale pagination controls visible for an invalid result set. +- Do not use loading feedback as a permanent empty state. + +## Expandable Media + +**Intent:** Show compact imagery with optional larger viewing and explicit fallback states. + +**Components:** `InkImage`, `InkLoading`, `InkPlaceholder` + +1. Provide the image source and meaningful alt text. +2. Enable expansion only when a useful larger asset or view exists. +3. Use image loading and error events to align surrounding feedback. +4. Use InkPlaceholder only when the surrounding media region needs recovery guidance. diff --git a/packages/web/skills/ui-web/references/integration.md b/packages/web/skills/ui-web/references/integration.md new file mode 100644 index 0000000..567fd25 --- /dev/null +++ b/packages/web/skills/ui-web/references/integration.md @@ -0,0 +1,36 @@ + + +# Integration + +## Package Boundary + +```sh +pnpm add @inkcre/ui-web +``` + +Import runtime components and the plugin from `@inkcre/ui-web`. Import +utilities, locales, UnoCSS, Sass helpers, and tokens only through documented +package exports. + +- Load @inkcre/ui-web/styles once in the application style entry unless the Sass entry is intentionally compiled instead. +- Use the package root for components and public types, /utils for utilities, /locales for locale bundles, and /uno for presetInk. +- Keep declared peer dependencies resolvable in the consumer installation graph. +- Do not alias raw package source or dist internals in the registry-backed production contract. + +## Router Adapter + +- Adapt the consuming router to reactive currentPath and currentName values. +- Provide the adapter through INK_ROUTER_KEY. +- Treat useOptionalRouter returning null as a supported no-router state. + +Provide an object matching `InkRouter` through `INK_ROUTER_KEY`; the package +does not own the consuming application's router instance. + +## Internationalization Adapter + +- Adapt the consuming i18n system to a t function and reactive locale. +- Provide the adapter through INK_I18N_KEY. +- Treat useOptionalI18n returning null as the package fallback state. + +Provide an object matching `InkI18n` through `INK_I18N_KEY`. Locale bundles +are available from `@inkcre/ui-web/locales`. diff --git a/packages/web/skills/ui-web/references/styling-and-themes.md b/packages/web/skills/ui-web/references/styling-and-themes.md new file mode 100644 index 0000000..54e3949 --- /dev/null +++ b/packages/web/skills/ui-web/references/styling-and-themes.md @@ -0,0 +1,28 @@ + + +# Styling And Themes + +Load the built stylesheet through `@inkcre/ui-web/styles`. For Sass, +use only the exported functions, mixins, and token subpaths. + +```scss +@use "@inkcre/ui-web/styles/functions" as functions; +@use "@inkcre/ui-web/styles/mixins" as mixins; +@use "@inkcre/ui-web/tokens/ref" as ref; +@use "@inkcre/ui-web/tokens/sys" as sys; +@use "@inkcre/ui-web/tokens/comp" as comp; +``` + +## Rules + +- Prefer system and component tokens over hard-coded visual values. +- Use exported Sass functions and mixins rather than importing package files by filesystem path. +- Keep ref, sys, and comp token output generated from tokens/inkcre.tokens.json. +- Use CSS custom properties and the package theme contract instead of duplicating token maps in application code. +- Import presetInk from @inkcre/ui-web/uno when the consuming UnoCSS setup needs the package preset. + +## Token Layers + +- Reference tokens (ref) are primitive values such as color, space, typography, radius, elevation, breakpoint, and opacity. +- System tokens (sys) map primitives to semantic light and dark theme roles. +- Component tokens (comp) own component-specific semantic values. diff --git a/packages/web/src/App.vue b/packages/web/src/App.vue index 1381366..f5c8bf3 100644 --- a/packages/web/src/App.vue +++ b/packages/web/src/App.vue @@ -15,21 +15,12 @@

inkField

- +

inkInput

- +

Value: {{ inputValue }}

diff --git a/packages/web/src/components/AGENTS.comp-doc.md b/packages/web/src/components/AGENTS.comp-doc.md index a9b2f2f..0711549 100644 --- a/packages/web/src/components/AGENTS.comp-doc.md +++ b/packages/web/src/components/AGENTS.comp-doc.md @@ -18,7 +18,7 @@ The words in `>` is what you (agent) need to follow, do NOT includes in the fina ## Rationale -> Explain Why this component exists. +> Explain Why this component exists. > Define when to use and when not to use. @@ -27,26 +27,28 @@ The words in `>` is what you (agent) need to follow, do NOT includes in the fina ### Concepts > For example: +> > - `ConceptA`: one-sentence definition > - `ConceptB` ### Visual / UX Meaning -> The semantic differences between states / variants. +> The semantic differences between states / variants. > What changes the user is expected to perceive ## Canonical Examples > The examples represent canonical usage, not an exhaustive combination of all parameters. For example: +> > - Subtle(default): Used as the normal/secondary action. > ```vue > > ``` -> ## Behavioral Contract > Behavioral guarantees consumers can rely on. For example: +> > - In `loading` state: > - No primary events should be emitted > - No repeated submissions should occur @@ -58,6 +60,7 @@ The words in `>` is what you (agent) need to follow, do NOT includes in the fina ## Extension & Composition > For example: +> > - Can be composed with `CompGroup` / `FormItem` > - Supports both controlled and uncontrolled usage > - Not recommended to nest inside high-frequency reflow containers @@ -65,6 +68,7 @@ The words in `>` is what you (agent) need to follow, do NOT includes in the fina ## Non-Goals > Explicitly out of scope for this component. For example: +> > - Handling permissions or authorization logic > - Data persistence > - Business workflow orchestration @@ -72,10 +76,11 @@ The words in `>` is what you (agent) need to follow, do NOT includes in the fina ## Implementation Notes > Notes for maintainers, for example: +> > - Internal state is managed via `useXXX` > - Relies on browser capabilities such as `ResizeObserver` > - Notes on handling SSR / non-DOM environments -> Note do not repeat project level implementation notes. +> Note do not repeat project level implementation notes. ```` ## References diff --git a/packages/web/src/components/inkAutoForm/inkAutoForm.ts b/packages/web/src/components/inkAutoForm/inkAutoForm.ts index 5809faa..4d49ea3 100644 --- a/packages/web/src/components/inkAutoForm/inkAutoForm.ts +++ b/packages/web/src/components/inkAutoForm/inkAutoForm.ts @@ -45,7 +45,7 @@ export const inkAutoFormProps = { // --- Emits --- export const inkAutoFormEmits = { - "update:formData": (value: Record) => true, + "update:formData": (_value: Record) => true, } as const; // --- Utilities --- @@ -53,9 +53,7 @@ export const inkAutoFormEmits = { /** * Maps JSON Schema property to appropriate form control component */ -export function mapSchemaPropertyToComponent( - property: JSONSchemaProperty -): FieldComponentMapping { +export function mapSchemaPropertyToComponent(property: JSONSchemaProperty): FieldComponentMapping { const { type, enum: enumValues, format, maxLength } = property; // Boolean -> Switch @@ -81,8 +79,7 @@ export function mapSchemaPropertyToComponent( // String with date/time format -> Picker if (type === "string" && format) { - const pickerType = - format === "date-time" ? "datetime" : format === "time" ? "time" : "date"; + const pickerType = format === "date-time" ? "datetime" : format === "time" ? "time" : "date"; return { component: "inkPicker", props: { @@ -126,20 +123,13 @@ export function mapSchemaPropertyToComponent( export async function validateFormData( formData: Record, schema: JSONSchema, - jsonService: any + jsonService: any, ): Promise<{ valid: boolean; errors: Record }> { try { const jsonString = JSON.stringify(formData, null, 2); - const TextDocument = await import("vscode-json-languageservice").then( - (m) => m.TextDocument - ); - - const doc = TextDocument.create( - "autoform://form-data.json", - "json", - 0, - jsonString - ); + const TextDocument = await import("vscode-json-languageservice").then((m) => m.TextDocument); + + const doc = TextDocument.create("autoform://form-data.json", "json", 0, jsonString); const jsonDocument = jsonService.parseJSONDocument(doc); // Configure schema diff --git a/packages/web/src/components/inkAutoForm/inkAutoForm.vue b/packages/web/src/components/inkAutoForm/inkAutoForm.vue index c086e42..1ce0d3f 100644 --- a/packages/web/src/components/inkAutoForm/inkAutoForm.vue +++ b/packages/web/src/components/inkAutoForm/inkAutoForm.vue @@ -51,9 +51,7 @@ const fields = computed(() => { const required = props.schema.required || []; for (const [key, property] of Object.entries(properties)) { - const mapping = mapSchemaPropertyToComponent( - property as JSONSchemaProperty - ); + const mapping = mapSchemaPropertyToComponent(property as JSONSchemaProperty); result.push({ key, @@ -78,11 +76,7 @@ const updateFieldValue = (key: string, value: any) => { }; const performValidation = async () => { - const result = await validateFormData( - internalFormData.value, - props.schema, - jsonService - ); + const result = await validateFormData(internalFormData.value, props.schema, jsonService); validationErrors.value = result.errors; }; @@ -93,7 +87,7 @@ watch( (newData) => { internalFormData.value = { ...newData }; }, - { deep: true } + { deep: true }, ); watch( @@ -115,7 +109,7 @@ watch( emit("update:formData", { ...newData }); } }, - { immediate: true } + { immediate: true }, ); // Validate on data change @@ -124,7 +118,7 @@ watch( () => { performValidation(); }, - { deep: true } + { deep: true }, ); // --- lifecycle hooks --- @@ -139,11 +133,7 @@ watch(