Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/calm-ui-source-loop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@inkcre/ui-web": patch
---

Document the consumer-owned local source loop and make the source entry compatible with strict consumer type graphs.
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,34 @@ Generate derived files with `pnpm generate`. The public component manifest
drives the runtime registry, global component types, package version, Story
coverage, and generated Agent Skills.

## Joint development with client-web

The consuming Vite/Vitest/TypeScript pipeline owns source consumption. Keep
this workspace installed and generated, then opt in from a sibling
`client-web` checkout:

```bash
pnpm install --frozen-lockfile
pnpm generate
pnpm --dir ../client-web dev:ui --ui-source ../ui/packages/web
```

Replace `../ui` with this checkout's actual location while the local directory
is still named `design` or is stored elsewhere.

The command validates this package root and maps only its public specifiers for
the current development process. It does not use `pnpm link`, persist an
absolute path, or modify either manifest or lockfile. Token JSON changes still
require `pnpm generate`.

Run the consumer source-graph check with
`pnpm --dir ../client-web type-check:ui --ui-source ../ui/packages/web`.
Normal client development, builds, checks, and CI remain pinned to the
published registry artifact. The consumer's
[`apps/client-web/docs/development.md`](https://github.com/InKCre/client-web/blob/main/apps/client-web/docs/development.md#joint-dev-with-inkcreui-web)
owns the full startup, remotes, cleanup, troubleshooting, and release-fidelity
contract.

## GitHub Packages authentication

The committed `.npmrc` routes only the `@inkcre` scope to GitHub Packages. It
Expand Down
20 changes: 20 additions & 0 deletions packages/web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,26 @@ subpath.
- Provider-agnostic internationalization support
- Agent Skills for AI-assisted development

## Local consumer source loop

`client-web` can opt into this package's source without changing its
registry-backed dependency:

```bash
pnpm --dir ../client-web dev:ui --ui-source ../ui/packages/web
pnpm --dir ../client-web type-check:ui --ui-source ../ui/packages/web
```

Replace `../ui` with the actual source checkout location when needed.

The consumer validates the package identity and public entries, uses exact
Vite/Vitest/TypeScript mappings, and keeps Vue and other peer runtimes
consumer-owned. This lane is development-only: it rejects production builds
and never replaces packed or published-package verification. Generate tokens
before expecting token changes to reach Sass HMR. See the
[consumer development guide](https://github.com/InKCre/client-web/blob/main/apps/client-web/docs/development.md#joint-dev-with-inkcreui-web)
for prerequisites, remote development, cleanup, and rollback.

## Agent Skills

The package ships one progressively disclosed Agent Skill under
Expand Down
15 changes: 14 additions & 1 deletion packages/web/src/components/inkJsonEditor/inkJsonEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ const editorRef = ref<HTMLDivElement>();
let editorView: EditorView | null = null;
const editableCompartment = new Compartment();

function diagnosticMessage(message: unknown): string {
if (typeof message === "string") return message;
if (
message &&
typeof message === "object" &&
"value" in message &&
typeof message.value === "string"
) {
return message.value;
}
return String(message);
}

const jsonSchemaLinter = linter(async (view) => {
if (!props.schema) return [];

Expand All @@ -46,7 +59,7 @@ const jsonSchemaLinter = linter(async (view) => {
from,
to,
severity: d.severity === 1 ? "error" : "warning",
message: d.message,
message: diagnosticMessage(d.message),
};
});
});
Expand Down
12 changes: 3 additions & 9 deletions packages/web/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,6 @@ import type { JSONSchema, JSONSchemaProperty } from "./components/inkAutoForm/in
import type { InkRouter } from "./router";
import type { InkI18n } from "./i18n";

export {
DropdownOption,
JSONSchema,
JSONSchemaProperty,
InkRouter,
INK_ROUTER_KEY,
InkI18n,
INK_I18N_KEY,
};
export { INK_ROUTER_KEY, INK_I18N_KEY };

export type { DropdownOption, JSONSchema, JSONSchemaProperty, InkRouter, InkI18n };
7 changes: 4 additions & 3 deletions tasks/ui-engineering/execution-08a.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Execution 08A — Web DX And Native TypeScript

Execution 08A is implemented and locally verified. Sir authorized it on
2026-07-28 after the registry-backed consumer migration and remote identity
closure. Ubuntu glibc CI is the next external proof.
Execution 08A is complete and published in `@inkcre/ui-web@1.3.0`. Sir
authorized it on 2026-07-28 after the registry-backed consumer migration and
remote identity closure. The bounded change was committed as `f9a65a3`, and
Ubuntu glibc CI passed before release.

## Why This Slice Exists

Expand Down
13 changes: 7 additions & 6 deletions tasks/ui-engineering/execution-08b.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Execution 08B — Intent-Based Agent Skill Delivery

Execution 08B is implemented and locally verified. Sir authorized it on
2026-07-28 together with the web DX slice after consumer and remote identity
closure. Its release-PR refresh, exact registry publication, and
post-publication probe are the next external gates.
Execution 08B is complete and published in `@inkcre/ui-web@1.3.0`. Sir
authorized it on 2026-07-28 together with the web DX slice after consumer and
remote identity closure. Release PR #31 merged, and the exact registry
artifact passed installed-package discovery and loading.

## Problem Statement

Expand Down Expand Up @@ -154,8 +154,9 @@ reopened rather than silently adding a permanent mirror.
- The working and frozen-install root checks pass Intent, generation,
package-contract, tests, build, and Histoire.
- Sir authorized the bounded commit and release-PR merge on 2026-07-28.
Registry publication remains successful only after CI, the release workflow,
and the exact installed-package probe pass.
Commit `f9a65a3` passed CI; release PR #31 merged as `7b0034c`, and the exact
`@inkcre/ui-web@1.3.0` registry artifact passed installed-package discovery
and loading.

## Primary References

Expand Down
144 changes: 144 additions & 0 deletions tasks/ui-engineering/execution-09.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# Execution 09 — Opt-In Local UI Source Loop

Execution 09 is implemented and locally verified across this producer and
`../client-web`. Sir authorized the slice on 2026-07-28 after
`@inkcre/ui-web@1.3.0` was published and its installed-package contract was
proven. The bounded producer and consumer changes are committed locally and
remain unpushed.

## Outcome

The normal consumer lane remains an exact registry dependency. A developer can
now opt one process into the sibling UI source graph without linking packages,
editing a manifest, changing the workspace, or writing a machine path:

```bash
pnpm dev:ui --ui-source ../ui/packages/web
pnpm type-check:ui --ui-source ../ui/packages/web
```

`../client-web` owns the overlay because its Vite, Vitest, TypeScript, Sass,
Module Federation, SVC, and Portless pipelines compile and serve the external
source. This producer owns package-source compatibility, generated inputs, its
package README, and the Changeset.

## Consumer Contract

The tracked consumer helper validates the real package root before returning
any configuration:

- `package.json` must name `@inkcre/ui-web`;
- all ten public source entries must exist: root, styles, functions, mixins,
ref/sys/comp tokens, utilities, locales, and UnoCSS;
- the global component declaration must exist;
- environment-based configuration must be an absolute path;
- build mode rejects the source overlay.

The resulting aliases are exact regular expressions, so an undeclared private
subpath cannot silently become public. Source mode extends Vite's filesystem
allowlist with both the detected client workspace and the validated package
root. No absolute path is stored in Vite configuration.

Vue and other shared peers are deduplicated to the consumer installation. The
UI package is excluded from dependency pre-bundling in source mode. The same
contract is used by the client Vite server, client Vitest project, and Twitter
remote.

## Sass Ownership

The previous substring test for `src/components/` could classify a sibling UI
component as client source and inject client-only `@/styles`. Source mode uses
real path containment instead:

- client components, views, and host extensions receive UI functions/mixins
plus client styles;
- sibling UI components receive only UI functions/mixins;
- unrelated files receive no injected prelude.

This keeps `@/` consumer-owned while allowing UI component Sass to compile and
hot-reload inside the host graph.

## Lifecycle And Identity

The root `dev:ui` launcher:

1. accepts `--ui-source` or `INKCRE_UI_SOURCE_ROOT`, never both;
2. resolves and validates the source package;
3. prints one `NON-RELEASE` banner with its version and real root;
4. asks SVC to ensure the separate worktree-scoped `web-ui` capability;
5. lets the Vite identity endpoint and SVC probe compare a non-path source
identity hash, preventing a route for a different checkout from being
reused silently.

`dev:all:ui` carries the same environment into extension development.
`dev:stop` knows the additional Portless route. The established database
capability remains shared with normal client development.

## Type Graph

`type-check:ui` writes a temporary tsconfig below ignored
`.runtime/ui-source/`, maps the same exact source entries, maps peer packages
to the consumer installation, includes the UI global component declaration,
runs Vue TSC, and removes the directory.

The strict cross-repository graph exposed two producer compatibility defects:

- type-only public exports were emitted as value exports;
- JSON language-service versions disagree on whether a diagnostic message is
always a string or may be markup content.

The producer now uses type-only exports and normalizes an unknown diagnostic
message into a CodeMirror string. Both the producer's pinned graph and the
consumer source graph pass.

## Durable Consumer Documentation

The complete operating guide lives in
`../client-web/apps/client-web/docs/development.md`. It covers prerequisites,
the recommended checkout shape, environment-variable use, remote development,
source type checking, generated tokens, cleanup, Portless behavior, and the
release-fidelity boundary.

Short routes to that guide are present in the consumer root README, docs
index, app `AGENTS.md`, extension guide, architecture, and filesystem map. This
producer links to it from both the repository README and the published
package-local README, so a consumer inspecting the repository or installed
package can find the canonical instructions.

## Local Proof

- `pnpm exec vitest run scripts/ui-source.test.mjs`: 5 contract tests pass.
- `pnpm type-check:ui --ui-source <real-package-root>` passes and removes its
temporary config.
- Source-mode client Vitest passes 3 files / 19 tests.
- Vite-transformed client and Twitter modules import the sibling UI source
rather than `node_modules`.
- WebSocket observation proves both a Vue template edit and an SCSS edit under
the sibling package emit HMR updates for the external source path.
- UI-owned Sass compiles in both the client and Twitter source graphs.
- A source-enabled production build fails with the intentional
development-only error.
- Source SVC startup reports a healthy `web-ui` capability and a distinct
`client-web-ui-<instance>` route.
- SHA-256 values for the consumer root manifest, client manifest, Twitter
manifest, and lockfile are byte-identical before startup and after route
shutdown.
- The complete consumer check passes 11 test files / 40 tests and all builds.
- The complete producer check passes 12 test files / 104 tests, both packed
contracts, and 21 stories / 108 variants.

The initial consumer run warned that the interactive shell used Node `26.3.0`
while the repository declared Node `22.22.3`. Sir chose organization
consistency rather than a Node 26 adoption. The consumer now matches this
producer's pnpm-managed `devEngines.runtime`: `pnpm exec node` and the
repository doctor report exact Node `22.22.3` independently of system Node,
and setup-node reads the same package authority. Frozen installation, the
complete consumer check, source-mode tests, and the source type graph pass
after the migration.

## Rollback

The consumer overlay is isolated to the new helper, launch/type-check scripts,
three Vite-family configurations, one SVC target, and documentation. Removing
that slice restores the exact registry-only graph. No dependency, lockfile,
workspace, or persisted local-link state must be repaired.
Loading