@zaaxch/tailframe 4.0.9 → 4.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zaaxch/tailframe",
3
- "version": "4.0.9",
3
+ "version": "4.0.10",
4
4
  "description": "Tailframe architecture toolkit: validates the Tailframe structure, import-boundary, and file-convention contracts. The package version is the contract version.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/generate.mjs CHANGED
@@ -404,35 +404,14 @@ export function themeToggleSource() {
404
404
  :title="\`Switch to \${themeStore.isDark ? 'light' : 'dark'} mode\`"
405
405
  @click="themeStore.toggleTheme"
406
406
  >
407
- <svg
408
- v-if="themeStore.isDark"
409
- aria-hidden="true"
410
- class="size-5"
411
- viewBox="0 0 24 24"
412
- fill="none"
413
- stroke="currentColor"
414
- stroke-width="1.8"
415
- >
416
- <circle cx="12" cy="12" r="4" />
417
- <path
418
- d="M12 2v2m0 16v2M4.93 4.93l1.42 1.42m11.3 11.3 1.42 1.42M2 12h2m16 0h2M4.93 19.07l1.42-1.42m11.3-11.3 1.42-1.42"
419
- />
420
- </svg>
421
- <svg
422
- v-else
423
- aria-hidden="true"
424
- class="size-5"
425
- viewBox="0 0 24 24"
426
- fill="none"
427
- stroke="currentColor"
428
- stroke-width="1.8"
429
- >
430
- <path d="M20.5 15.2A8.5 8.5 0 0 1 8.8 3.5a8.5 8.5 0 1 0 11.7 11.7Z" />
431
- </svg>
407
+ <FontAwesomeIcon v-if="themeStore.isDark" :icon="faSun" aria-hidden="true" class="size-5" />
408
+ <FontAwesomeIcon v-else :icon="faMoon" aria-hidden="true" class="size-5" />
432
409
  </button>
433
410
  </template>
434
411
 
435
412
  <script setup lang="ts">
413
+ import { faMoon, faSun } from "@fortawesome/free-solid-svg-icons";
414
+ import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
436
415
  import { useThemeStore } from "@/app/stores/theme.store";
437
416
 
438
417
  const themeStore = useThemeStore();
package/src/new.mjs CHANGED
@@ -202,7 +202,7 @@ Use this workflow for any new domain capability or operation. Add only the layer
202
202
  2. Read the root and every applicable child \`AGENTS.md\`.
203
203
  3. Inspect the nearest working module and tests.
204
204
  4. Create architecture files only with the tailframe CLI: \`tailframe generate --app <service|ui>\` creates canonical service and UI architecture files. Hand-creating architecture files or editing generated shell-catalog implementations is a conformance violation; inspect the generated diff and complete only the behavior-specific checklist.
205
- 5. Colocate product capability behavior under \`src/modules/<module>\` in both the service and UI. Never substitute \`features\` or global \`apis\`, \`views\`, \`services\`, \`stores\`, or \`types\` directories. UI \`app/stores\` is closed to generated \`auth.store.ts\`, \`notification.store.ts\`, and \`theme.store.ts\`; \`app/public\` is closed to generated \`ThemeToggle.vue\`. Never create \`src/modules/theme\`, \`src/modules/auth\`, or \`src/modules/notification\`. Product records, current-user domain records, selections, filters, and workflows stay in their owning module; when the service exposes a user capability, its current-user record belongs to a UI \`user\` module. Start backend modules with \`use-cases/\`, \`http/\`, and tests; add \`domain/\` or \`persistence/\` only when required. Add only the UI module directories earned by the capability.
205
+ 5. Colocate product capability behavior under \`src/modules/<module>\` in both the service and UI. Never substitute \`features\` or global \`apis\`, \`views\`, \`services\`, \`stores\`, or \`types\` directories. UI \`app/stores\` is closed to generated \`auth.store.ts\`, \`notification.store.ts\`, and \`theme.store.ts\`; \`app/public\` is closed to generated \`ThemeToggle.vue\`. Never create \`src/modules/theme\`, \`src/modules/auth\`, or \`src/modules/notification\`. Product records, current-user domain records, selections, filters, and workflows stay in their owning module; when the service exposes a user capability, its current-user record belongs to a UI \`user\` module. Start backend modules with \`use-cases/\`, \`http/\`, and tests; add \`domain/\` or \`persistence/\` only when required. Add only the UI module directories earned by the capability. Font Awesome's Vue component and free solid icon set are the standard icon system for shell and product UI.
206
206
  6. Define use cases as \`execute(context, input)\`. Keep trusted \`RequestContext\` separate from client input.
207
207
  7. Put ownership, RBAC, entitlements, and domain policies in use-case or domain code, not client bodies or route metadata.
208
208
  8. Define repository contracts in the owning module and implement them in persistence adapters.
@@ -595,7 +595,7 @@ Run \`pnpm validate:architecture\` and \`pnpm format:check\` after changing file
595
595
  `);
596
596
 
597
597
  if (options.ui) {
598
- const uiDeps = { "@tailwindcss/vite": "^4.1.18", "@vueuse/core": "^14.3.0", axios: "^1.9.0", pinia: "^3.0.1", primevue: "^4.2.5", vue: "^3.5.13", "vue-router": "^4.5.0", ...(options.auth === "firebase" ? { firebase: "^11.0.0" } : {}) };
598
+ const uiDeps = { "@fortawesome/fontawesome-svg-core": "^7.3.1", "@fortawesome/free-solid-svg-icons": "^7.3.1", "@fortawesome/vue-fontawesome": "^3.3.3", "@tailwindcss/vite": "^4.1.18", "@vueuse/core": "^14.3.0", axios: "^1.9.0", pinia: "^3.0.1", primevue: "^4.2.5", vue: "^3.5.13", "vue-router": "^4.5.0", ...(options.auth === "firebase" ? { firebase: "^11.0.0" } : {}) };
599
599
  const uiDevDeps = { "@tsconfig/node22": "^22.0.1", "@types/node": "^22.13.14", "@vitejs/plugin-vue": "^5.2.3", "@vue/test-utils": "^2.4.10", "@vue/tsconfig": "^0.7.0", jsdom: "^29.1.1", "npm-run-all2": "^7.0.2", prettier: "3.5.3", typescript: "~5.8.0", vite: "^6.2.4", vitest: "^4.1.7", "vue-tsc": "^2.2.8" };
600
600
  add(`${ui}/package.json`, JSON.stringify({ name: uiPackage, version: "0.1.0", private: true, type: "module", scripts: { dev: "vite --host 0.0.0.0", build: "run-p type-check \"build-only {@}\" --", "build-only": "vite build", "type-check": "vue-tsc --build", test: "vitest run", "test:watch": "vitest", "validate:architecture": "tailframe validate ../.. --app ui", "sync:architecture": "tailframe sync --check ../..", format: "prettier --write src/", "format:check": "prettier --check src/" }, dependencies: uiDeps, devDependencies: uiDevDeps }, null, "\t"));
601
601
 
@@ -734,7 +734,7 @@ These conventions apply to \`${ui}/src\`. Inspect the nearest working module bef
734
734
  - Use \`src/core\` only for small technology-neutral client contracts.
735
735
  - Use \`src/platform\` for Axios, Firebase, browser APIs, and other technical adapters.
736
736
  - Use \`src/modules/<module>\` for every product capability. Never substitute \`src/features\` or global \`apis\`, \`views\`, \`services\`, \`stores\`, or \`types\` directories.
737
- - These are the only UI architecture roots. Keep \`src/core\` flat. Shared capability code belongs to the module that owns it. \`src/app/stores\` is closed to generated \`auth.store.ts\`, \`notification.store.ts\`, and \`theme.store.ts\`; \`src/app/public\` is closed to generated \`ThemeToggle.vue\`. Do not edit their implementations; only the generated theme storage key varies. Internal shell components remain private under \`src/app/components\`.
737
+ - These are the only UI architecture roots. Keep \`src/core\` flat. Shared capability code belongs to the module that owns it. \`src/app/stores\` is closed to generated \`auth.store.ts\`, \`notification.store.ts\`, and \`theme.store.ts\`; \`src/app/public\` is closed to generated \`ThemeToggle.vue\`. Do not edit their implementations; only the generated theme storage key varies. Internal shell components remain private under \`src/app/components\`. Font Awesome's Vue component and free solid icon set are the standard icon system for shell and product UI.
738
738
  - Never create \`src/modules/theme\`, \`src/modules/auth\`, or \`src/modules/notification\`. Product records, current-user domain records, selections, filters, workflows, and module API calls always belong to the owning product module. When the service exposes a user capability, its current-user record belongs to a UI \`user\` module rather than whichever consumer happens to use fields from the response.
739
739
 
740
740
  ## Change placement
@@ -765,7 +765,7 @@ Use named routes.${options.auth === "firebase" ? " Preserve Firebase authenticat
765
765
 
766
766
  ## State management
767
767
  Keep local state local. Use Pinia only for state shared across routes or unrelated components.
768
- Keep all product state in the owning module. The auth/notification/theme stores, NotificationHost, and ThemeToggle are Tailframe-owned generated sources shared byte-for-byte across projects apart from the explicit theme storage key.
768
+ Keep all product state in the owning module. The auth/notification/theme stores, NotificationHost, and ThemeToggle are Tailframe-owned generated sources shared byte-for-byte across projects apart from the explicit theme storage key. ThemeToggle uses Tailframe's Font Awesome Vue integration.
769
769
 
770
770
  ## UI behavior
771
771
  - Prefer PrimeVue for standard interactive controls and overlays when an appropriate component exists. Use native HTML for semantic structure, links, and genuinely custom interactions; use Tailwind for layout and composition.
package/src/sync.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { appRoot, loadConfig } from "./config.mjs";
4
+ import { themeToggleSource } from "./generate.mjs";
4
5
  import { appGuidance, OWNED_GUIDANCE_END, OWNED_GUIDANCE_START, productGuidance } from "./owned-guidance.mjs";
5
6
  import { ownedSources } from "./owned-sources.mjs";
6
7
 
@@ -47,7 +48,11 @@ export function runSync(rootArgument, mode, runningVersion, selectedKind) {
47
48
  continue;
48
49
  }
49
50
  syncGuidance(loaded.root, root, appGuidance(loaded.config, app), mode, changed, errors, app.path);
50
- for (const [relative, expected] of Object.entries(ownedSources(app))) {
51
+ const sources = ownedSources(app);
52
+ if (app.kind === "ui" && fs.existsSync(path.join(root, "src/app/public/ThemeToggle.vue"))) {
53
+ sources["src/app/public/ThemeToggle.vue"] = themeToggleSource();
54
+ }
55
+ for (const [relative, expected] of Object.entries(sources)) {
51
56
  const absolute = path.join(root, relative);
52
57
  const productRelative = path.join(app.path, relative);
53
58
  const actual = fs.existsSync(absolute) ? fs.readFileSync(absolute, "utf8") : undefined;
package/src/validate.mjs CHANGED
@@ -8,6 +8,11 @@ import { runSync } from "./sync.mjs";
8
8
 
9
9
  const NON_EXCEPTABLE_RULES = new Set(["S8", "S9", "U5", "U6", "U7"]);
10
10
  const PNPM_VERSION = "11.22.0";
11
+ const FONT_AWESOME_UI_DEPENDENCIES = {
12
+ "@fortawesome/fontawesome-svg-core": "^7.3.1",
13
+ "@fortawesome/free-solid-svg-icons": "^7.3.1",
14
+ "@fortawesome/vue-fontawesome": "^3.3.3"
15
+ };
11
16
 
12
17
  export function runValidate(root, kind) {
13
18
  const structural = validateArchitecture(root, kind);
@@ -133,6 +138,13 @@ function validateAppMetadata(productRoot, app, productConfig) {
133
138
  if (manifest.devDependencies?.["@zaaxch/tailframe"] || manifest.dependencies?.["@zaaxch/tailframe"]) {
134
139
  errors.push(`${app.path}/package.json must not depend on @zaaxch/tailframe; pin it at the product root`);
135
140
  }
141
+ if (app.kind === "ui") {
142
+ for (const [dependency, version] of Object.entries(FONT_AWESOME_UI_DEPENDENCIES)) {
143
+ if (manifest.dependencies?.[dependency] !== version) {
144
+ errors.push(`${app.path}/package.json must pin Tailframe's ${dependency} dependency to ${version}`);
145
+ }
146
+ }
147
+ }
136
148
  } catch {
137
149
  errors.push(`${app.path}/package.json is not valid JSON`);
138
150
  }