From 8d6a001c2d9eeb4a3f6ebd320e29015d38d35962 Mon Sep 17 00:00:00 2001 From: "bert.hausmans" Date: Mon, 11 May 2026 00:52:56 +0200 Subject: [PATCH 01/10] =?UTF-8?q?docs(playwright):=20correct=20F3=E2=86=92?= =?UTF-8?q?F5=20comments=20in=20CT=20provider=20stack?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both comments in apps/app/playwright/index.ts (header block lines 38-45 and inline at line 66) state that the Vuetify provider gets replaced by PrimeVue in F3. This predates the RFC clarification that test-runtime flip is F5, not F3 (per ARCH-TESTING.md §7). F3 builds the PrimeVue runtime in main.ts but keeps the test runtime on Vuetify. Component tests continue to mount with the Vuetify provider until F5 deliberately swaps it. This commit aligns the comments with that decision so no future contributor wonders whether the F3 sprint should have touched this file. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/app/playwright/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/app/playwright/index.ts b/apps/app/playwright/index.ts index 5ad543fa..a3749fba 100644 --- a/apps/app/playwright/index.ts +++ b/apps/app/playwright/index.ts @@ -36,12 +36,12 @@ import '@/styles/tokens/_timetable.css' // } // // Defaults below render every component with the full Vuexy/Vuetify -// stack. F3 (PrimeVue foundation) replaces the Vuetify plugin line +// stack. F5 (test-runtime flip) replaces the Vuetify plugin line // here with PrimeVue and updates the sanity test — that is a ~2-hour // swap, not a rewrite. Vuetify is INTENTIONAL TEMPORARY STATE in this // file; do not abstract behind a "UI framework provider" indirection -// because the abstraction would itself need to be removed in F3. -// See dev-docs/ARCH-TESTING.md §6 for the migration timeline. +// because the abstraction would itself need to be removed in F5. +// See dev-docs/ARCH-TESTING.md §7 for the migration timeline. // ============================================================================= export interface HooksConfig { @@ -63,7 +63,7 @@ const defaultTheme: ThemeDefinition = { } beforeMount(async ({ app, hooksConfig }) => { - // ---- Vuetify (TEMPORARY: replaced by PrimeVue in F3) ----------------- + // ---- Vuetify (TEMPORARY: replaced by PrimeVue in F5) ----------------- const vuetify = createVuetify({ components, directives, -- 2.39.5 From c8dcecbb49e3f20cbdbdf230451f06f7994334ee Mon Sep 17 00:00:00 2001 From: "bert.hausmans" Date: Mon, 11 May 2026 00:58:36 +0200 Subject: [PATCH 02/10] chore(deps): install PrimeVue 4.5 + Tailwind v4 + form ecosystem for F3 foundation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Packages installed: - primevue@4.5.5 - @primeuix/themes@2.0.3 (substitutes @primevue/themes per ecosystem state — see rationale below) - @primevue/forms@4.5.5 - primelocale@1.6.0 (pinned to ^1 per RFC) - tailwindcss@4.3.0 - @tailwindcss/vite@4.3.0 - tailwindcss-primeui@0.6.1 Package substitution: @primevue/themes → @primeuix/themes RFC v1.0 §6 F3 specifies @primevue/themes@^4.5, but during install pnpm reported this package as deprecated by its maintainers (PrimeFaces) with explicit guidance to migrate to @primeuix/themes. Web verification confirms that the official PrimeVue 4 install documentation at primevue.org/vite/ now specifies `@primeuix/themes` directly, not the deprecated path: pnpm add primevue @primeuix/themes import Aura from '@primeuix/themes/aura'; @primeuix/themes is maintained by the same maintainers (mert.sincan, cagatay.civici), has the same API surface (Aura preset, definePreset, semantic tokens), and is the path PrimeVue 4's documentation now prescribes. The substitution is not a deviation from PrimeVue v4 conventions — it IS the current PrimeVue v4 convention. The RFC will be amended in B9 to align AD-2 and Appendix B with this ecosystem state. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/app/package.json | 7 + apps/app/pnpm-lock.yaml | 549 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 505 insertions(+), 51 deletions(-) diff --git a/apps/app/package.json b/apps/app/package.json index fa6d6dbe..ed643321 100644 --- a/apps/app/package.json +++ b/apps/app/package.json @@ -24,8 +24,11 @@ "@casl/ability": "6.7.3", "@casl/vue": "2.2.2", "@floating-ui/dom": "1.6.8", + "@primeuix/themes": "^2.0.3", + "@primevue/forms": "^4.5.5", "@sentry/vue": "10.52.0", "@sindresorhus/is": "7.1.0", + "@tailwindcss/vite": "^4.3.0", "@tanstack/vue-query": "^5.95.2", "@tiptap/extension-highlight": "^2.27.1", "@tiptap/extension-image": "^2.27.1", @@ -47,10 +50,14 @@ "mapbox-gl": "3.5.2", "ofetch": "1.5.0", "pinia": "3.0.3", + "primelocale": "^1.6.0", + "primevue": "^4.5.5", "prismjs": "1.30.0", "qrcode": "^1.5.4", "roboto-fontface": "0.10.0", "shepherd.js": "13.0.3", + "tailwindcss": "^4.3.0", + "tailwindcss-primeui": "^0.6.1", "ufo": "1.6.1", "unplugin-vue-define-options": "1.5.5", "vue": "3.5.22", diff --git a/apps/app/pnpm-lock.yaml b/apps/app/pnpm-lock.yaml index 63e3066f..19932509 100644 --- a/apps/app/pnpm-lock.yaml +++ b/apps/app/pnpm-lock.yaml @@ -21,12 +21,21 @@ importers: '@floating-ui/dom': specifier: 1.6.8 version: 1.6.8 + '@primeuix/themes': + specifier: ^2.0.3 + version: 2.0.3 + '@primevue/forms': + specifier: ^4.5.5 + version: 4.5.5(vue@3.5.22(typescript@5.9.3)) '@sentry/vue': specifier: 10.52.0 version: 10.52.0(pinia@3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3)))(vue@3.5.22(typescript@5.9.3)) '@sindresorhus/is': specifier: 7.1.0 version: 7.1.0 + '@tailwindcss/vite': + specifier: ^4.3.0 + version: 4.3.0(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)) '@tanstack/vue-query': specifier: ^5.95.2 version: 5.95.2(vue@3.5.22(typescript@5.9.3)) @@ -90,6 +99,12 @@ importers: pinia: specifier: 3.0.3 version: 3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3)) + primelocale: + specifier: ^1.6.0 + version: 1.6.0 + primevue: + specifier: ^4.5.5 + version: 4.5.5(vue@3.5.22(typescript@5.9.3)) prismjs: specifier: 1.30.0 version: 1.30.0 @@ -102,6 +117,12 @@ importers: shepherd.js: specifier: 13.0.3 version: 13.0.3 + tailwindcss: + specifier: ^4.3.0 + version: 4.3.0 + tailwindcss-primeui: + specifier: ^0.6.1 + version: 0.6.1(tailwindcss@4.3.0) ufo: specifier: 1.6.1 version: 1.6.1 @@ -207,7 +228,7 @@ importers: version: 1.0.3(pinia@3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3))) '@playwright/experimental-ct-vue': specifier: ^1.59.1 - version: 1.59.1(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))(yaml@2.8.1) + version: 1.59.1(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))(yaml@2.8.1) '@playwright/test': specifier: ^1.59.1 version: 1.59.1 @@ -264,10 +285,10 @@ importers: version: 7.18.0(eslint@8.57.1)(typescript@5.9.3) '@vitejs/plugin-vue': specifier: 6.0.1 - version: 6.0.1(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) + version: 6.0.1(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) '@vitejs/plugin-vue-jsx': specifier: 5.1.1 - version: 5.1.1(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) + version: 5.1.1(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) '@vue/compiler-dom': specifier: ^3.5.34 version: 3.5.34 @@ -405,25 +426,25 @@ importers: version: 0.8.8(rollup@4.52.5)(vue-router@4.5.1(vue@3.5.22(typescript@5.9.3)))(vue@3.5.22(typescript@5.9.3)) vite: specifier: 7.1.12 - version: 7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) + version: 7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) vite-plugin-vue-devtools: specifier: 8.0.2 - version: 8.0.2(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) + version: 8.0.2(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) vite-plugin-vue-meta-layouts: specifier: 0.6.1 - version: 0.6.1(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue-router@4.5.1(vue@3.5.22(typescript@5.9.3))) + version: 0.6.1(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue-router@4.5.1(vue@3.5.22(typescript@5.9.3))) vite-plugin-vuetify: specifier: 2.1.2 - version: 2.1.2(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))(vuetify@3.10.8) + version: 2.1.2(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))(vuetify@3.10.8) vite-svg-loader: specifier: 5.1.0 version: 5.1.0(vue@3.5.22(typescript@5.9.3)) vitest: specifier: ^4.1.5 - version: 4.1.5(@types/node@24.9.2)(happy-dom@20.9.0)(jsdom@29.1.1)(msw@2.6.8(@types/node@24.9.2)(typescript@5.9.3))(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)) + version: 4.1.5(@types/node@24.9.2)(happy-dom@20.9.0)(jsdom@29.1.1)(msw@2.6.8(@types/node@24.9.2)(typescript@5.9.3))(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)) vitest-axe: specifier: ^0.1.0 - version: 0.1.0(vitest@4.1.5(@types/node@24.9.2)(happy-dom@20.9.0)(jsdom@29.1.1)(msw@2.6.8(@types/node@24.9.2)(typescript@5.9.3))(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))) + version: 0.1.0(vitest@4.1.5(@types/node@24.9.2)(happy-dom@20.9.0)(jsdom@29.1.1)(msw@2.6.8(@types/node@24.9.2)(typescript@5.9.3))(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))) vue-eslint-parser: specifier: 9.4.3 version: 9.4.3(eslint@8.57.1) @@ -1203,6 +1224,38 @@ packages: '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + '@primeuix/forms@0.1.0': + resolution: {integrity: sha512-LctcQidb+B5PuvAFWH24YH/SIzmHlOabLHpaTeGY/k51iBv1WyCp+5w9JMYuMB/BplSvV0ZGySxQVkN5Azr/aQ==} + engines: {node: '>=12.11.0'} + + '@primeuix/styled@0.7.4': + resolution: {integrity: sha512-QSO/NpOQg8e9BONWRBx9y8VGMCMYz0J/uKfNJEya/RGEu7ARx0oYW0ugI1N3/KB1AAvyGxzKBzGImbwg0KUiOQ==} + engines: {node: '>=12.11.0'} + + '@primeuix/styles@2.0.3': + resolution: {integrity: sha512-2ykAB6BaHzR/6TwF8ShpJTsZrid6cVIEBVlookSdvOdmlWuevGu5vWOScgIwqWwlZcvkFYAGR/SUV3OHCTBMdw==} + + '@primeuix/themes@2.0.3': + resolution: {integrity: sha512-3fS1883mtCWhgUgNf/feiaaDSOND4EBIOu9tZnzJlJ8QtYyL6eFLcA6V3ymCWqLVXQ1+lTVEZv1gl47FIdXReg==} + + '@primeuix/utils@0.6.4': + resolution: {integrity: sha512-pZ5f+vj7wSzRhC7KoEQRU5fvYAe+RP9+m39CTscZ3UywCD1Y2o6Fe1rRgklMPSkzUcty2jzkA0zMYkiJBD1hgg==} + engines: {node: '>=12.11.0'} + + '@primevue/core@4.5.5': + resolution: {integrity: sha512-JpkXhq1ddc70JdsC3CC4dM+UbeeWuCW/8DpS9dNBfrOk824TLSlRlMEGFyVKqRMn5WPQvYLiy3xXfLQeNdSqhQ==} + engines: {node: '>=12.11.0'} + peerDependencies: + vue: ^3.5.0 + + '@primevue/forms@4.5.5': + resolution: {integrity: sha512-LUeIt6oItwCZyBreQ7ycErE8aEjPmBWOTq1VPLhyaATcnzMBOZRIiwclaYk8s/tf5VYMqN9N4B80XnXoO7OdvQ==} + engines: {node: '>=12.11.0'} + + '@primevue/icons@4.5.5': + resolution: {integrity: sha512-eteOhTdAOXEYE9qW1AOrBBgDxQ2szHJxSkEK1XVdV2TKxGM5FQf03Ovms0VDyZTc16XBIgvwYjXJQS0BPbhPaA==} + engines: {node: '>=12.11.0'} + '@remirror/core-constants@3.0.0': resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==} @@ -1441,6 +1494,100 @@ packages: peerDependencies: stylelint: ^16.0.2 + '@tailwindcss/node@4.3.0': + resolution: {integrity: sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==} + + '@tailwindcss/oxide-android-arm64@4.3.0': + resolution: {integrity: sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.3.0': + resolution: {integrity: sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.3.0': + resolution: {integrity: sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.3.0': + resolution: {integrity: sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0': + resolution: {integrity: sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.0': + resolution: {integrity: sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.3.0': + resolution: {integrity: sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.3.0': + resolution: {integrity: sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.3.0': + resolution: {integrity: sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.3.0': + resolution: {integrity: sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.0': + resolution: {integrity: sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.3.0': + resolution: {integrity: sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.3.0': + resolution: {integrity: sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==} + engines: {node: '>= 20'} + + '@tailwindcss/vite@4.3.0': + resolution: {integrity: sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 || ^8 + '@tanstack/match-sorter-utils@8.19.4': resolution: {integrity: sha512-Wo1iKt2b9OT7d+YGhvEPD3DXvPv2etTusIMhMUoG7fbhmxcXCtIjJDEygy91Y2JFlwGyjqiBPRozme7UD8hoqg==} engines: {node: '>=12'} @@ -2719,6 +2866,10 @@ packages: destr@2.0.5: resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} @@ -2790,6 +2941,10 @@ packages: end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + enhanced-resolve@5.21.2: + resolution: {integrity: sha512-xe9vQb5kReirPUxgQrXA3ihgbCqssmTiM7cOZ+Gzu+VeGWgpV98lLZvp0dl4yriyAePcewxGUs9UpKD8PET9KQ==} + engines: {node: '>=10.13.0'} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -3387,6 +3542,9 @@ packages: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} @@ -3696,6 +3854,10 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + js-beautify@1.15.4: resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==} engines: {node: '>=14'} @@ -3796,6 +3958,80 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -4356,6 +4592,14 @@ packages: resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} engines: {node: '>=18'} + primelocale@1.6.0: + resolution: {integrity: sha512-SDE3hKspR+RWg7j/xDXw2UfvcTlEVvtzo80CtWs6Q99qphEY4BQEXFDTa4ovVhdk2CZYXyWCFUxU/pO70RJh+A==} + engines: {node: '>=12.0.0', npm: '>=6.0.0'} + + primevue@4.5.5: + resolution: {integrity: sha512-Kv5REIewCdP806QaoU+4nBXfmpzOGFKkZ9qH4KsL6MjiAQVc4PUzypt8erl4r3Vzh3nr3aWZIxkxYRRsLGiX2A==} + engines: {node: '>=12.11.0'} + prismjs@1.30.0: resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} engines: {node: '>=6'} @@ -4953,6 +5197,18 @@ packages: resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} engines: {node: '>=20'} + tailwindcss-primeui@0.6.1: + resolution: {integrity: sha512-T69Rylcrmnt8zy9ik+qZvsLuRIrS9/k6rYJSIgZ1trnbEzGDDQSCIdmfyZknevqiHwpSJHSmQ9XT2C+S/hJY4A==} + peerDependencies: + tailwindcss: '>=3.1.0' + + tailwindcss@4.3.0: + resolution: {integrity: sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} @@ -6510,11 +6766,11 @@ snapshots: '@pkgr/core@0.2.9': {} - '@playwright/experimental-ct-core@1.59.1(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)': + '@playwright/experimental-ct-core@1.59.1(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)': dependencies: playwright: 1.59.1 playwright-core: 1.59.1 - vite: 6.4.2(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) + vite: 6.4.2(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) transitivePeerDependencies: - '@types/node' - jiti @@ -6528,10 +6784,10 @@ snapshots: - tsx - yaml - '@playwright/experimental-ct-vue@1.59.1(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))(yaml@2.8.1)': + '@playwright/experimental-ct-vue@1.59.1(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))(yaml@2.8.1)': dependencies: - '@playwright/experimental-ct-core': 1.59.1(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) - '@vitejs/plugin-vue': 5.2.4(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) + '@playwright/experimental-ct-core': 1.59.1(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) + '@vitejs/plugin-vue': 5.2.4(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) transitivePeerDependencies: - '@types/node' - jiti @@ -6555,6 +6811,45 @@ snapshots: '@popperjs/core@2.11.8': {} + '@primeuix/forms@0.1.0': + dependencies: + '@primeuix/utils': 0.6.4 + + '@primeuix/styled@0.7.4': + dependencies: + '@primeuix/utils': 0.6.4 + + '@primeuix/styles@2.0.3': + dependencies: + '@primeuix/styled': 0.7.4 + + '@primeuix/themes@2.0.3': + dependencies: + '@primeuix/styled': 0.7.4 + + '@primeuix/utils@0.6.4': {} + + '@primevue/core@4.5.5(vue@3.5.22(typescript@5.9.3))': + dependencies: + '@primeuix/styled': 0.7.4 + '@primeuix/utils': 0.6.4 + vue: 3.5.22(typescript@5.9.3) + + '@primevue/forms@4.5.5(vue@3.5.22(typescript@5.9.3))': + dependencies: + '@primeuix/forms': 0.1.0 + '@primeuix/utils': 0.6.4 + '@primevue/core': 4.5.5(vue@3.5.22(typescript@5.9.3)) + transitivePeerDependencies: + - vue + + '@primevue/icons@4.5.5(vue@3.5.22(typescript@5.9.3))': + dependencies: + '@primeuix/utils': 0.6.4 + '@primevue/core': 4.5.5(vue@3.5.22(typescript@5.9.3)) + transitivePeerDependencies: + - vue + '@remirror/core-constants@3.0.0': {} '@rolldown/pluginutils@1.0.0-beta.29': {} @@ -6755,6 +7050,74 @@ snapshots: style-search: 0.1.0 stylelint: 16.8.0(typescript@5.9.3) + '@tailwindcss/node@4.3.0': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.21.2 + jiti: 2.7.0 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.3.0 + + '@tailwindcss/oxide-android-arm64@4.3.0': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.3.0': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.3.0': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.3.0': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.0': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.3.0': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.3.0': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.3.0': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.3.0': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.0': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.3.0': + optional: true + + '@tailwindcss/oxide@4.3.0': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.3.0 + '@tailwindcss/oxide-darwin-arm64': 4.3.0 + '@tailwindcss/oxide-darwin-x64': 4.3.0 + '@tailwindcss/oxide-freebsd-x64': 4.3.0 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.0 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.0 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.0 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.0 + '@tailwindcss/oxide-linux-x64-musl': 4.3.0 + '@tailwindcss/oxide-wasm32-wasi': 4.3.0 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.0 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.0 + + '@tailwindcss/vite@4.3.0(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))': + dependencies: + '@tailwindcss/node': 4.3.0 + '@tailwindcss/oxide': 4.3.0 + tailwindcss: 4.3.0 + vite: 7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) + '@tanstack/match-sorter-utils@8.19.4': dependencies: remove-accents: 0.5.0 @@ -7411,27 +7774,27 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vitejs/plugin-vue-jsx@5.1.1(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': + '@vitejs/plugin-vue-jsx@5.1.1(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': dependencies: '@babel/core': 7.28.5 '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5) '@rolldown/pluginutils': 1.0.0-beta.45 '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.28.5) - vite: 7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) vue: 3.5.22(typescript@5.9.3) transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue@5.2.4(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': + '@vitejs/plugin-vue@5.2.4(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': dependencies: - vite: 7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) vue: 3.5.22(typescript@5.9.3) - '@vitejs/plugin-vue@6.0.1(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.1(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.0-beta.29 - vite: 7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) vue: 3.5.22(typescript@5.9.3) '@vitest/expect@4.1.5': @@ -7443,14 +7806,14 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.5(msw@2.6.8(@types/node@24.9.2)(typescript@5.9.3))(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))': + '@vitest/mocker@4.1.5(msw@2.6.8(@types/node@24.9.2)(typescript@5.9.3))(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))': dependencies: '@vitest/spy': 4.1.5 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.6.8(@types/node@24.9.2)(typescript@5.9.3) - vite: 7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) '@vitest/pretty-format@4.1.5': dependencies: @@ -7577,14 +7940,14 @@ snapshots: dependencies: '@vue/devtools-kit': 7.7.7 - '@vue/devtools-core@8.0.3(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': + '@vue/devtools-core@8.0.3(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': dependencies: '@vue/devtools-kit': 8.0.3 '@vue/devtools-shared': 8.0.3 mitt: 3.0.1 nanoid: 5.1.6 pathe: 2.0.3 - vite-hot-client: 2.1.0(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)) + vite-hot-client: 2.1.0(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)) vue: 3.5.22(typescript@5.9.3) transitivePeerDependencies: - vite @@ -8206,6 +8569,8 @@ snapshots: destr@2.0.5: {} + detect-libc@2.1.2: {} + devlop@1.1.0: dependencies: dequal: 2.0.3 @@ -8280,6 +8645,11 @@ snapshots: dependencies: once: 1.4.0 + enhanced-resolve@5.21.2: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + entities@4.5.0: {} entities@6.0.1: {} @@ -9169,6 +9539,8 @@ snapshots: gopd@1.2.0: {} + graceful-fs@4.2.11: {} + graphemer@1.4.0: {} graphql@16.11.0: {} @@ -9478,6 +9850,8 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 + jiti@2.7.0: {} + js-beautify@1.15.4: dependencies: config-chain: 1.1.13 @@ -9574,6 +9948,55 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + lines-and-columns@1.2.4: {} linkify-it@5.0.0: @@ -10165,6 +10588,18 @@ snapshots: dependencies: parse-ms: 4.0.0 + primelocale@1.6.0: {} + + primevue@4.5.5(vue@3.5.22(typescript@5.9.3)): + dependencies: + '@primeuix/styled': 0.7.4 + '@primeuix/styles': 2.0.3 + '@primeuix/utils': 0.6.4 + '@primevue/core': 4.5.5(vue@3.5.22(typescript@5.9.3)) + '@primevue/icons': 4.5.5(vue@3.5.22(typescript@5.9.3)) + transitivePeerDependencies: + - vue + prismjs@1.30.0: {} property-information@7.1.0: {} @@ -10895,6 +11330,14 @@ snapshots: tagged-tag@1.0.0: {} + tailwindcss-primeui@0.6.1(tailwindcss@4.3.0): + dependencies: + tailwindcss: 4.3.0 + + tailwindcss@4.3.0: {} + + tapable@2.3.3: {} + tar@6.2.1: dependencies: chownr: 2.0.0 @@ -11248,17 +11691,17 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-dev-rpc@1.1.0(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)): + vite-dev-rpc@1.1.0(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)): dependencies: birpc: 2.6.1 - vite: 7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) - vite-hot-client: 2.1.0(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)) + vite: 7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) + vite-hot-client: 2.1.0(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)) - vite-hot-client@2.1.0(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)): + vite-hot-client@2.1.0(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)): dependencies: - vite: 7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) - vite-plugin-inspect@11.3.3(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)): + vite-plugin-inspect@11.3.3(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)): dependencies: ansis: 4.2.0 debug: 4.4.3 @@ -11268,27 +11711,27 @@ snapshots: perfect-debounce: 2.0.0 sirv: 3.0.2 unplugin-utils: 0.3.1 - vite: 7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) - vite-dev-rpc: 1.1.0(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)) + vite: 7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) + vite-dev-rpc: 1.1.0(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)) transitivePeerDependencies: - supports-color - vite-plugin-vue-devtools@8.0.2(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)): + vite-plugin-vue-devtools@8.0.2(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)): dependencies: - '@vue/devtools-core': 8.0.3(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) + '@vue/devtools-core': 8.0.3(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) '@vue/devtools-kit': 8.0.3 '@vue/devtools-shared': 8.0.3 execa: 9.6.0 sirv: 3.0.2 - vite: 7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) - vite-plugin-inspect: 11.3.3(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)) - vite-plugin-vue-inspector: 5.3.2(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)) + vite: 7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) + vite-plugin-inspect: 11.3.3(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)) + vite-plugin-vue-inspector: 5.3.2(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)) transitivePeerDependencies: - '@nuxt/kit' - supports-color - vue - vite-plugin-vue-inspector@5.3.2(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)): + vite-plugin-vue-inspector@5.3.2(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)): dependencies: '@babel/core': 7.28.5 '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.5) @@ -11299,22 +11742,22 @@ snapshots: '@vue/compiler-dom': 3.5.34 kolorist: 1.8.0 magic-string: 0.30.21 - vite: 7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) transitivePeerDependencies: - supports-color - vite-plugin-vue-meta-layouts@0.6.1(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue-router@4.5.1(vue@3.5.22(typescript@5.9.3))): + vite-plugin-vue-meta-layouts@0.6.1(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue-router@4.5.1(vue@3.5.22(typescript@5.9.3))): dependencies: local-pkg: 0.5.1 - vite: 7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) vue-router: 4.5.1(vue@3.5.22(typescript@5.9.3)) - vite-plugin-vuetify@2.1.2(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))(vuetify@3.10.8): + vite-plugin-vuetify@2.1.2(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))(vuetify@3.10.8): dependencies: '@vuetify/loader-shared': 2.1.1(vue@3.5.22(typescript@5.9.3))(vuetify@3.10.8) debug: 4.4.3 upath: 2.0.1 - vite: 7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) vue: 3.5.22(typescript@5.9.3) vuetify: 3.10.8(typescript@5.9.3)(vite-plugin-vuetify@2.1.2)(vue@3.5.22(typescript@5.9.3)) transitivePeerDependencies: @@ -11325,7 +11768,7 @@ snapshots: svgo: 3.3.2 vue: 3.5.22(typescript@5.9.3) - vite@6.4.2(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1): + vite@6.4.2(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1): dependencies: esbuild: 0.25.11 fdir: 6.5.0(picomatch@4.0.3) @@ -11336,11 +11779,13 @@ snapshots: optionalDependencies: '@types/node': 24.9.2 fsevents: 2.3.3 + jiti: 2.7.0 + lightningcss: 1.32.0 sass: 1.76.0 tsx: 4.20.6 yaml: 2.8.1 - vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1): + vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1): dependencies: esbuild: 0.25.11 fdir: 6.5.0(picomatch@4.0.3) @@ -11351,11 +11796,13 @@ snapshots: optionalDependencies: '@types/node': 24.9.2 fsevents: 2.3.3 + jiti: 2.7.0 + lightningcss: 1.32.0 sass: 1.76.0 tsx: 4.20.6 yaml: 2.8.1 - vitest-axe@0.1.0(vitest@4.1.5(@types/node@24.9.2)(happy-dom@20.9.0)(jsdom@29.1.1)(msw@2.6.8(@types/node@24.9.2)(typescript@5.9.3))(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))): + vitest-axe@0.1.0(vitest@4.1.5(@types/node@24.9.2)(happy-dom@20.9.0)(jsdom@29.1.1)(msw@2.6.8(@types/node@24.9.2)(typescript@5.9.3))(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))): dependencies: aria-query: 5.1.3 axe-core: 4.11.4 @@ -11363,12 +11810,12 @@ snapshots: dom-accessibility-api: 0.5.16 lodash-es: 4.18.1 redent: 3.0.0 - vitest: 4.1.5(@types/node@24.9.2)(happy-dom@20.9.0)(jsdom@29.1.1)(msw@2.6.8(@types/node@24.9.2)(typescript@5.9.3))(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)) + vitest: 4.1.5(@types/node@24.9.2)(happy-dom@20.9.0)(jsdom@29.1.1)(msw@2.6.8(@types/node@24.9.2)(typescript@5.9.3))(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)) - vitest@4.1.5(@types/node@24.9.2)(happy-dom@20.9.0)(jsdom@29.1.1)(msw@2.6.8(@types/node@24.9.2)(typescript@5.9.3))(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)): + vitest@4.1.5(@types/node@24.9.2)(happy-dom@20.9.0)(jsdom@29.1.1)(msw@2.6.8(@types/node@24.9.2)(typescript@5.9.3))(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)): dependencies: '@vitest/expect': 4.1.5 - '@vitest/mocker': 4.1.5(msw@2.6.8(@types/node@24.9.2)(typescript@5.9.3))(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)) + '@vitest/mocker': 4.1.5(msw@2.6.8(@types/node@24.9.2)(typescript@5.9.3))(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1)) '@vitest/pretty-format': 4.1.5 '@vitest/runner': 4.1.5 '@vitest/snapshot': 4.1.5 @@ -11385,7 +11832,7 @@ snapshots: tinyexec: 1.1.1 tinyglobby: 0.2.15 tinyrainbow: 3.1.0 - vite: 7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 24.9.2 @@ -11486,7 +11933,7 @@ snapshots: vue: 3.5.22(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 - vite-plugin-vuetify: 2.1.2(vite@7.1.12(@types/node@24.9.2)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))(vuetify@3.10.8) + vite-plugin-vuetify: 2.1.2(vite@7.1.12(@types/node@24.9.2)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.76.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))(vuetify@3.10.8) w3c-keyname@2.2.8: {} -- 2.39.5 From 0272961a95f37b7c6a63191b7b8c4091af3136db Mon Sep 17 00:00:00 2001 From: "bert.hausmans" Date: Mon, 11 May 2026 01:00:01 +0200 Subject: [PATCH 03/10] feat(primevue): add PrimeVue plugin with Aura preset and Crewli teal tokens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Scaffolds apps/app/src/plugins/primevue/ as three files mirroring the Vuetify plugin structure at apps/app/src/plugins/vuetify/: - theme.ts — CrewliPreset extends Aura via definePreset(). Primary palette (50–950) is the exact token plan from RFC Appendix B, centered on Crewli teal #0D9394 (light primary, primary.500) and #0B7F80 (dark primary, primary.600). Surface tokens use Aura defaults. colorScheme.light/dark map primary.color, hover, active, and contrastColor per Appendix B. - defaults.ts — empty pt (PassThrough) defaults object. F3 ships this scaffold; F4 sub-packages populate component-level defaults as each Vuetify surface migrates. - index.ts — installPrimeVue(app) registers PrimeVue with the preset, Dutch locale (primelocale/nl.json → nl.nl), darkModeSelector: '.dark' (matches Vuexy convention per AD-2), and the three services Toast, Confirmation, Dialog. Theme imports use @primeuix/themes (the maintained successor PrimeVue's official docs prescribe), not RFC v1.0's @primevue/themes. See B1 commit for substitution rationale. RFC will be aligned in B9. The plugin is not yet registered in main.ts — that lands in B4 after Tailwind v4 wiring (B3). Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/app/src/plugins/primevue/defaults.ts | 12 +++++ apps/app/src/plugins/primevue/index.ts | 36 +++++++++++++++ apps/app/src/plugins/primevue/theme.ts | 55 +++++++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 apps/app/src/plugins/primevue/defaults.ts create mode 100644 apps/app/src/plugins/primevue/index.ts create mode 100644 apps/app/src/plugins/primevue/theme.ts diff --git a/apps/app/src/plugins/primevue/defaults.ts b/apps/app/src/plugins/primevue/defaults.ts new file mode 100644 index 00000000..786c0c62 --- /dev/null +++ b/apps/app/src/plugins/primevue/defaults.ts @@ -0,0 +1,12 @@ +// Global PrimeVue PassThrough (pt) defaults. F3 ships this scaffold +// empty — F4 sub-packages will populate component-level defaults as +// each Vuetify surface migrates and we discover which props need +// consistent overrides (analogous to Vuetify's defaults registry at +// apps/app/src/plugins/vuetify/defaults.ts). +// +// Per RFC §6 + AD-1, this object is consumed by the PrimeVue install +// call in ./index.ts via the `pt` config option. + +export const ptDefaults = {} as const + +export default ptDefaults diff --git a/apps/app/src/plugins/primevue/index.ts b/apps/app/src/plugins/primevue/index.ts new file mode 100644 index 00000000..83f00a39 --- /dev/null +++ b/apps/app/src/plugins/primevue/index.ts @@ -0,0 +1,36 @@ +// PrimeVue plugin installer. Registers PrimeVue with the Crewli Aura +// preset (theme.ts), the Dutch locale (primelocale/nl), and the three +// service modules (Toast, Confirmation, Dialog) the SPA mounts at +// App.vue top level. +// +// Per RFC-WS-FRONTEND-PRIMEVUE AD-2: darkModeSelector matches Vuexy's +// `.dark` class convention so existing skin-toggle plumbing continues +// to work during the F3–F6 parallel-mode window. + +import type { App } from 'vue' +import PrimeVue from 'primevue/config' +import ConfirmationService from 'primevue/confirmationservice' +import DialogService from 'primevue/dialogservice' +import ToastService from 'primevue/toastservice' +import nl from 'primelocale/nl.json' + +import { CrewliPreset } from './theme' +import { ptDefaults } from './defaults' + +export default function installPrimeVue(app: App) { + app.use(PrimeVue, { + theme: { + preset: CrewliPreset, + options: { + darkModeSelector: '.dark', + cssLayer: false, + }, + }, + locale: nl.nl, + pt: ptDefaults, + }) + + app.use(ToastService) + app.use(ConfirmationService) + app.use(DialogService) +} diff --git a/apps/app/src/plugins/primevue/theme.ts b/apps/app/src/plugins/primevue/theme.ts new file mode 100644 index 00000000..c5116271 --- /dev/null +++ b/apps/app/src/plugins/primevue/theme.ts @@ -0,0 +1,55 @@ +// Crewli Aura preset. Token values per RFC-WS-FRONTEND-PRIMEVUE Appendix B. +// +// Primary palette derives from Crewli teal #0D9394 (light primary) and +// #0B7F80 (dark primary) — the same values the Vuetify side uses at +// apps/app/src/plugins/vuetify/theme.ts. The two themes are intentionally +// kept in lockstep during the F3–F6 parallel-mode window so that pages +// in either framework render with identical brand color. +// +// Imports use @primeuix/themes (PrimeVue 4's officially documented theme +// package, per primevue.org/vite/), substituting RFC v1.0's reference to +// the deprecated @primevue/themes. See B1 commit for rationale. + +import { definePreset } from '@primeuix/themes' +import Aura from '@primeuix/themes/aura' + +export const staticPrimaryColor = '#0D9394' +export const staticPrimaryDarkenColor = '#0B7F80' + +export const CrewliPreset = definePreset(Aura, { + semantic: { + primary: { + 50: '#E6F4F4', + 100: '#CCE9EA', + 200: '#99D3D4', + 300: '#66BDBE', + 400: '#33A7A8', + 500: '#0D9394', + 600: '#0B7F80', + 700: '#086B6C', + 800: '#055758', + 900: '#034344', + 950: '#012F30', + }, + colorScheme: { + light: { + primary: { + color: '{primary.500}', + contrastColor: '#ffffff', + hoverColor: '{primary.600}', + activeColor: '{primary.700}', + }, + }, + dark: { + primary: { + color: '{primary.400}', + contrastColor: '{surface.900}', + hoverColor: '{primary.300}', + activeColor: '{primary.200}', + }, + }, + }, + }, +}) + +export default CrewliPreset -- 2.39.5 From 90d5c1678cb5886337f9e61ec2d6a63e25aad778 Mon Sep 17 00:00:00 2001 From: "bert.hausmans" Date: Mon, 11 May 2026 01:02:05 +0200 Subject: [PATCH 04/10] feat(tailwind): install Tailwind v4 alongside Vuetify (parallel mode) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three additions wire Tailwind v4 into the SPA without disturbing the existing Vuetify pipeline: - apps/app/src/assets/styles/tailwind.css — Tailwind v4 CSS-first entry. Uses @import "tailwindcss"; @plugin "tailwindcss-primeui"; and @source pointing at apps/app/src/ to scan template content. - apps/app/vite.config.ts — adds the @tailwindcss/vite plugin between vue() and vuetify(). After vue() so it sees compiled template content; before vuetify() so Vuetify's SCSS pipeline runs unimpeded. - apps/app/src/main.ts — imports tailwind.css before the Vuetify/Vuexy SCSS so utility classes are available alongside Vuetify's cascade. optimizeDeps.exclude remains ['vuetify'] (no PrimeVue addition) — HMR behaves correctly in dev with the current config; revisit if needed. Verification: - pnpm typecheck — clean. - pnpm build — succeeds in 13.97s; CSS emitted per-route as expected. - pnpm test — 402 tests pass unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/app/src/assets/styles/tailwind.css | 14 ++++++++++++++ apps/app/src/main.ts | 1 + apps/app/vite.config.ts | 6 ++++++ 3 files changed, 21 insertions(+) create mode 100644 apps/app/src/assets/styles/tailwind.css diff --git a/apps/app/src/assets/styles/tailwind.css b/apps/app/src/assets/styles/tailwind.css new file mode 100644 index 00000000..a258726e --- /dev/null +++ b/apps/app/src/assets/styles/tailwind.css @@ -0,0 +1,14 @@ +/* Tailwind v4 CSS-first entry. Imports the framework and the + * tailwindcss-primeui plugin (semantic-token utilities that map + * PrimeVue Aura tokens like {primary.500} to Tailwind classes + * like bg-primary-500). + * + * Per Tailwind v4 with @tailwindcss/vite, the @source directive + * tells the plugin where to scan template content — we point it at + * apps/app/src/. Existing Vuetify/Vuexy SCSS stays untouched and is + * imported separately from main.ts. + */ + +@import "tailwindcss"; +@plugin "tailwindcss-primeui"; +@source "../../**/*.{vue,ts,js,tsx,jsx}"; diff --git a/apps/app/src/main.ts b/apps/app/src/main.ts index ff544415..2e9c479c 100644 --- a/apps/app/src/main.ts +++ b/apps/app/src/main.ts @@ -8,6 +8,7 @@ import App from '@/App.vue' import { registerPlugins } from '@core/utils/plugins' // Styles +import '@styles/tailwind.css' import '@core/scss/template/index.scss' import '@styles/styles.scss' diff --git a/apps/app/vite.config.ts b/apps/app/vite.config.ts index 41dd4f6f..26b954d6 100644 --- a/apps/app/vite.config.ts +++ b/apps/app/vite.config.ts @@ -12,6 +12,7 @@ import { defineConfig } from 'vite' import VueDevTools from 'vite-plugin-vue-devtools' import MetaLayouts from 'vite-plugin-vue-meta-layouts' import vuetify from 'vite-plugin-vuetify' +import tailwindcss from '@tailwindcss/vite' import svgLoader from 'vite-svg-loader' // https://vitejs.dev/config/ @@ -31,6 +32,11 @@ export default defineConfig({ VueDevTools(), vueJsx(), + // Tailwind v4 (F3 — parallel-mode with Vuetify until F6). Placed after + // vue() so it sees compiled template content; placed before vuetify() + // so Vuetify's SCSS pipeline runs unimpeded. + tailwindcss(), + // Docs: https://github.com/vuetifyjs/vuetify-loader/tree/master/packages/vite-plugin vuetify({ styles: { -- 2.39.5 From 7660d12a8c138b10da16647e6a783a6cc6471788 Mon Sep 17 00:00:00 2001 From: "bert.hausmans" Date: Mon, 11 May 2026 01:02:59 +0200 Subject: [PATCH 05/10] feat(primevue): register PrimeVue plugin in main.ts alongside Vuetify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit installPrimeVue(app) runs AFTER registerPlugins(app) (which registers Vuetify + router + Pinia via the Vuexy @core machine). Placing the PrimeVue install outside @core/utils/plugins is deliberate — it keeps PrimeVue free of the Vuexy plugin loader so F6 can remove @core/ without disturbing PrimeVue registration. Both frameworks are now active at runtime. Existing Vuetify pages continue to render unchanged; PrimeVue components become available for the layout-shell rewrite (B7) and the FormField wrapper (B5). Verification: - pnpm typecheck — clean. - pnpm build — succeeds in 14.26s, no PrimeVue or theme-related errors. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/app/src/main.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/app/src/main.ts b/apps/app/src/main.ts index 2e9c479c..04ebec50 100644 --- a/apps/app/src/main.ts +++ b/apps/app/src/main.ts @@ -6,6 +6,7 @@ import { router } from '@/plugins/1.router' import App from '@/App.vue' import { registerPlugins } from '@core/utils/plugins' +import installPrimeVue from '@/plugins/primevue' // Styles import '@styles/tailwind.css' @@ -29,6 +30,11 @@ initSentry({ // Register plugins (router, pinia, vuetify, …). registerPlugins(app) +// PrimeVue runs alongside Vuetify during the F3–F6 parallel-mode window. +// Registered AFTER registerPlugins(app) so PrimeVue lives outside the +// Vuexy @core/ machine — F6 can remove @core/ without affecting PrimeVue. +installPrimeVue(app) + // Bind auth-scope tags per route navigation. Must run after pinia is set // up by registerPlugins (the guard reads useAuthStore / useOrganisationStore). installContextBinding(router) -- 2.39.5 From c1190ab0456c6f62bee8dfe4d249f08cfc2608db Mon Sep 17 00:00:00 2001 From: "bert.hausmans" Date: Mon, 11 May 2026 01:04:58 +0200 Subject: [PATCH 06/10] feat(forms): add FormField wrapper + useFormError composable per RFC Appendix A MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two new artifacts that together provide the F4 form-migration target: apps/app/src/components/forms/FormField.vue — project-owned wrapper around @primevue/forms' built-in FormField. The default slot accepts the actual input (e.g. ); the wrapper renders label (with optional required asterisk), error Message, and hint chrome around it. Reads field state from the parent
via the built-in FormField's scoped slot, so call-sites do not need to thread $form manually. apps/app/src/composables/useFormError.ts — the API 422 bridge. Parent component calls useFormError() once; the composable provides an apiErrors ref through Vue inject. Each FormField in the component reads its own field name from that map. applyApiErrors() reads the Crewli backend's { errors: { field: string[] } } shape and surfaces the first message per field; clearApiErrors() resets between submits. Error precedence per RFC Appendix A: explicit apiError prop > inject apiErrors map > Zod resolver error from $field. Signature note: RFC's useFormError(formRef) is implemented as useFormError() — the formRef parameter is unused in the provide/inject implementation, and Crewli convention avoids unused parameters. RFC will be aligned in B9 if it remains a meaningful spec gap during F4. Verification: - pnpm typecheck — clean. - pnpm test — 402 tests pass unchanged. - B8 will exercise the components end-to-end on the login page; F4d validates against the public-registration multi-step form. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/app/src/components/forms/FormField.vue | 93 +++++++++++++++++++++ apps/app/src/composables/useFormError.ts | 44 ++++++++++ 2 files changed, 137 insertions(+) create mode 100644 apps/app/src/components/forms/FormField.vue create mode 100644 apps/app/src/composables/useFormError.ts diff --git a/apps/app/src/components/forms/FormField.vue b/apps/app/src/components/forms/FormField.vue new file mode 100644 index 00000000..04a8057c --- /dev/null +++ b/apps/app/src/components/forms/FormField.vue @@ -0,0 +1,93 @@ + + + diff --git a/apps/app/src/composables/useFormError.ts b/apps/app/src/composables/useFormError.ts new file mode 100644 index 00000000..05a225b7 --- /dev/null +++ b/apps/app/src/composables/useFormError.ts @@ -0,0 +1,44 @@ +// useFormError — merges Laravel 422 validation responses into the +// surrounding Form's per-field error display. +// +// Pattern: a parent component calls useFormError() once. The composable +// provides an apiErrors map via Vue inject. Each FormField inside the +// component reads its own field name from that map and falls through to +// its $field.error (the Zod resolver result) when no API error is set. +// apiError precedence > Zod error per RFC Appendix A. +// +// Crewli backend 422 shape: { message, errors: { field: string[] } }. +// We surface the first message per field. + +import { type Ref, provide, ref } from 'vue' + +export const FORM_API_ERRORS_KEY = Symbol('crewli-form-api-errors') + +export type FormApiErrorsMap = Record + +export interface UseFormErrorReturn { + applyApiErrors: (errors: Record) => void + clearApiErrors: () => void + apiErrors: Ref +} + +export function useFormError(): UseFormErrorReturn { + const apiErrors = ref({}) + + provide(FORM_API_ERRORS_KEY, apiErrors) + + function applyApiErrors(errors: Record): void { + const next: FormApiErrorsMap = {} + for (const [field, messages] of Object.entries(errors)) { + if (Array.isArray(messages) && messages.length > 0) + next[field] = messages[0] + } + apiErrors.value = next + } + + function clearApiErrors(): void { + apiErrors.value = {} + } + + return { applyApiErrors, clearApiErrors, apiErrors } +} -- 2.39.5 From f5a9e491cee7e1629158afd28f8a7473ace39cfb Mon Sep 17 00:00:00 2001 From: "bert.hausmans" Date: Mon, 11 May 2026 01:06:11 +0200 Subject: [PATCH 07/10] feat(primevue): add Icon component and mount Toast + ConfirmDialog services MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two additions complete the F3 runtime scaffolding: apps/app/src/components/Icon.vue — generic Iconify renderer wrapping @iconify/vue's component. F4 migration substitutes with at call-sites, producing real SVG output and using the existing Crewli "tabler-*" naming convention. Props: name (required, e.g. "tabler-eye"), optional size. The component avoids @iconify/vue's auto-import for clarity at call-sites. apps/app/src/App.vue — mounts and at the template root inside VLocaleProvider. Both render alongside the existing VSnackbar and VDialog confirm patterns during the F3–F4 parallel-mode window. F4 sub-packages migrate call-sites to PrimeVue's useToast() / useConfirm() composables. UnoCSS-style i-tabler-* utility-class rendering (RFC AD-5 v1.0 wording) is not adopted — UnoCSS is not installed in the Crewli stack. The RFC will be aligned in B9. Verification: - pnpm typecheck — clean. - pnpm test — 402 tests pass unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/app/src/App.vue | 10 +++++++++ apps/app/src/components/Icon.vue | 35 ++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 apps/app/src/components/Icon.vue diff --git a/apps/app/src/App.vue b/apps/app/src/App.vue index a1733b1f..d64ba1b0 100644 --- a/apps/app/src/App.vue +++ b/apps/app/src/App.vue @@ -1,5 +1,7 @@ + + -- 2.39.5 From 43915501401003cd14a2030bec25b9a00ef5df53 Mon Sep 17 00:00:00 2001 From: "bert.hausmans" Date: Mon, 11 May 2026 01:12:06 +0200 Subject: [PATCH 08/10] feat(layouts): rewrite layout shells with PrimeVue Drawer + Menubar + Avatar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Layout-shell rewrite per RFC AD-3, B7-option-B. R-10 isolation invariant honored — this single commit is revertible to roll back the layout change without losing B1–B6 progress. New component (PrimeVue-only, no Vuetify imports per F3 hard constraint): - apps/app/src/layouts/components/AppShell.vue (~210 lines) - Desktop sidebar (Tailwind grid, lg+ breakpoint) renders nav items as PrimeVue Buttons + Icons. Mobile ( for semantic structure F3 functional regressions (intentional — F4 sub-packages reintroduce each item through PrimeVue): - NavSearchBar (Vuetify-heavy combobox/overlay) — absent from top bar - ContextSwitcher (Vuetify VBtn + VMenu) — absent - NavbarThemeSwitcher (Vuetify IconBtn) — absent; dark mode driven by PrimeVue's darkModeSelector: '.dark' continues to work via the existing @core skin classes until F6 cleanup - NavbarShortcuts (Vuetify-heavy) — absent - NavBarNotifications (Vuetify-heavy) — absent - UserProfile from @/layouts/components/ (Vuetify-heavy menu) — replaced with the minimal Avatar + Menu dropdown described above; rich profile panel returns in F4 - ImpersonationBanner — absent; super-admin impersonation UX is F4 work - PortalLayout event-mode vs platform-mode topbar (route.meta.navMode driven) — absent; F4 reintroduces via AppShell prop or slot - Suspense + AppLoadingIndicator wrapping pages — dropped; pages handle their own loading via PrimeVue ProgressSpinner VApp at App.vue level still wraps everything, so Vuetify components inside still-Vuetify pages continue to render correctly during the parallel-mode window. Test updates (no Vuetify in layout structure to assert against anymore): - OrganizerLayout.spec.ts — mocks AppShell instead of the deleted DefaultLayoutWithVerticalNav reference; provides Pinia. - PortalLayout.spec.ts — same mock pattern; new structural assertions go through AppShell stub; the new third test verifies PortalLayout forwards portal nav items + title to AppShell. - PublicLayout.vue — uses
for semantics; PublicLayout.spec.ts still passes unchanged. Auto-generated component/auto-import dts files refreshed for the new AppShell component (committed for stable dev workflow). Verification: - pnpm typecheck — clean. - pnpm test — 402 tests pass (test count unchanged after spec rewrites). - pnpm build — succeeds in 14.05s; AppShell chunk is ~57 KB raw. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/app/auto-imports.d.ts | 4 + apps/app/components.d.ts | 2 + apps/app/src/layouts/OrganizerLayout.vue | 51 +++- apps/app/src/layouts/PortalLayout.vue | 255 ++---------------- apps/app/src/layouts/PublicLayout.vue | 19 +- .../layouts/__tests__/OrganizerLayout.spec.ts | 32 ++- .../layouts/__tests__/PortalLayout.spec.ts | 105 +++----- apps/app/src/layouts/blank.vue | 52 +--- apps/app/src/layouts/components/AppShell.vue | 214 +++++++++++++++ apps/app/src/layouts/default.vue | 88 +++--- 10 files changed, 415 insertions(+), 407 deletions(-) create mode 100644 apps/app/src/layouts/components/AppShell.vue diff --git a/apps/app/auto-imports.d.ts b/apps/app/auto-imports.d.ts index 2d95ae79..2bf7c39b 100644 --- a/apps/app/auto-imports.d.ts +++ b/apps/app/auto-imports.d.ts @@ -10,6 +10,7 @@ declare global { const COOKIE_MAX_AGE_1_YEAR: typeof import('./src/utils/constants')['COOKIE_MAX_AGE_1_YEAR'] const CreateUrl: typeof import('./src/@core/composable/CreateUrl')['CreateUrl'] const EffectScope: typeof import('vue')['EffectScope'] + const FORM_API_ERRORS_KEY: typeof import('./src/composables/useFormError')['FORM_API_ERRORS_KEY'] const PUBLIC_FORM_LOCALE_KEY: typeof import('./src/composables/publicFormInjection')['PUBLIC_FORM_LOCALE_KEY'] const PUBLIC_FORM_TOKEN_KEY: typeof import('./src/composables/publicFormInjection')['PUBLIC_FORM_TOKEN_KEY'] const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate'] @@ -245,6 +246,7 @@ declare global { const useFocus: typeof import('@vueuse/core')['useFocus'] const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin'] const useFormDraft: typeof import('./src/composables/useFormDraft')['useFormDraft'] + const useFormError: typeof import('./src/composables/useFormError')['useFormError'] const useFps: typeof import('@vueuse/core')['useFps'] const useFullscreen: typeof import('@vueuse/core')['useFullscreen'] const useGamepad: typeof import('@vueuse/core')['useGamepad'] @@ -394,6 +396,7 @@ declare module 'vue' { interface ComponentCustomProperties { readonly COOKIE_MAX_AGE_1_YEAR: UnwrapRef readonly EffectScope: UnwrapRef + readonly FORM_API_ERRORS_KEY: UnwrapRef readonly PUBLIC_FORM_LOCALE_KEY: UnwrapRef readonly PUBLIC_FORM_TOKEN_KEY: UnwrapRef readonly acceptHMRUpdate: UnwrapRef @@ -621,6 +624,7 @@ declare module 'vue' { readonly useFocus: UnwrapRef readonly useFocusWithin: UnwrapRef readonly useFormDraft: UnwrapRef + readonly useFormError: UnwrapRef readonly useFps: UnwrapRef readonly useFullscreen: UnwrapRef readonly useGamepad: UnwrapRef diff --git a/apps/app/components.d.ts b/apps/app/components.d.ts index 74de8c1d..1651ab32 100644 --- a/apps/app/components.d.ts +++ b/apps/app/components.d.ts @@ -96,9 +96,11 @@ declare module 'vue' { FormErrorState: typeof import('./src/components/shared/public-form/FormErrorState.vue')['default'] FormFailureDetail: typeof import('./src/components/form-failures/FormFailureDetail.vue')['default'] FormFailuresTable: typeof import('./src/components/form-failures/FormFailuresTable.vue')['default'] + FormField: typeof import('./src/components/forms/FormField.vue')['default'] FormStepper: typeof import('./src/components/shared/public-form/FormStepper.vue')['default'] GridBg: typeof import('./src/components/timetable/GridBg.vue')['default'] I18n: typeof import('./src/@core/components/I18n.vue')['default'] + Icon: typeof import('./src/components/Icon.vue')['default'] IdentityMatchBanner: typeof import('./src/components/shared/public-form/IdentityMatchBanner.vue')['default'] ImageUploadField: typeof import('./src/components/common/ImageUploadField.vue')['default'] ImpersonateDialog: typeof import('./src/components/platform/ImpersonateDialog.vue')['default'] diff --git a/apps/app/src/layouts/OrganizerLayout.vue b/apps/app/src/layouts/OrganizerLayout.vue index a576535c..838126e7 100644 --- a/apps/app/src/layouts/OrganizerLayout.vue +++ b/apps/app/src/layouts/OrganizerLayout.vue @@ -1,9 +1,54 @@ diff --git a/apps/app/src/layouts/PortalLayout.vue b/apps/app/src/layouts/PortalLayout.vue index 151a29ba..99a0963f 100644 --- a/apps/app/src/layouts/PortalLayout.vue +++ b/apps/app/src/layouts/PortalLayout.vue @@ -1,234 +1,35 @@ diff --git a/apps/app/src/layouts/PublicLayout.vue b/apps/app/src/layouts/PublicLayout.vue index c86af6c6..e18b76d1 100644 --- a/apps/app/src/layouts/PublicLayout.vue +++ b/apps/app/src/layouts/PublicLayout.vue @@ -1,16 +1,13 @@ diff --git a/apps/app/src/layouts/__tests__/OrganizerLayout.spec.ts b/apps/app/src/layouts/__tests__/OrganizerLayout.spec.ts index fcf2f112..32baf1df 100644 --- a/apps/app/src/layouts/__tests__/OrganizerLayout.spec.ts +++ b/apps/app/src/layouts/__tests__/OrganizerLayout.spec.ts @@ -1,13 +1,34 @@ import { describe, expect, it, vi } from 'vitest' import { mount } from '@vue/test-utils' +import { createPinia, setActivePinia } from 'pinia' -// Mock the import path so OrganizerLayout's `import DefaultLayoutWithVerticalNav` -// statement doesn't pull in the real component (which transitively imports -// Vuetify .css files that the trimmed-down vitest config can't transform). -vi.mock('@/layouts/components/DefaultLayoutWithVerticalNav.vue', () => ({ - default: { template: '
' }, +// Mock AppShell so the OrganizerLayout test isolates the layout +// wrapper's job (compute nav items, pass them to AppShell, expose +// RouterView via default slot). AppShell's own behavior is exercised +// by its own tests / Playwright CT in F4. +vi.mock('@/layouts/components/AppShell.vue', () => ({ + default: { + template: '
', + props: ['navItems', 'title'], + }, })) +vi.mock('@/stores/useAuthStore', () => ({ + useAuthStore: () => ({ + organisations: [], + currentOrganisation: null, + isSuperAdmin: false, + }), +})) + +vi.mock('@/stores/useImpersonationStore', () => ({ + useImpersonationStore: () => ({ + isImpersonating: false, + }), +})) + +setActivePinia(createPinia()) + const OrganizerLayout = (await import('../OrganizerLayout.vue')).default describe('OrganizerLayout', () => { @@ -19,6 +40,7 @@ describe('OrganizerLayout', () => { }) expect(wrapper.exists()).toBe(true) + expect(wrapper.find('[data-test="app-shell"]').exists()).toBe(true) }) it('renders a RouterView in its slot', () => { diff --git a/apps/app/src/layouts/__tests__/PortalLayout.spec.ts b/apps/app/src/layouts/__tests__/PortalLayout.spec.ts index 1893045f..df6457b1 100644 --- a/apps/app/src/layouts/__tests__/PortalLayout.spec.ts +++ b/apps/app/src/layouts/__tests__/PortalLayout.spec.ts @@ -2,97 +2,54 @@ import { describe, expect, it, vi } from 'vitest' import { mount } from '@vue/test-utils' import { createPinia, setActivePinia } from 'pinia' -// Mock auto-imported composables / external pinia stores so the layout's -// - - diff --git a/apps/app/src/layouts/components/AppShell.vue b/apps/app/src/layouts/components/AppShell.vue new file mode 100644 index 00000000..9761e1d2 --- /dev/null +++ b/apps/app/src/layouts/components/AppShell.vue @@ -0,0 +1,214 @@ + + + diff --git a/apps/app/src/layouts/default.vue b/apps/app/src/layouts/default.vue index 3a7257b5..11c00282 100644 --- a/apps/app/src/layouts/default.vue +++ b/apps/app/src/layouts/default.vue @@ -1,57 +1,55 @@ - - - -- 2.39.5 From ad82110a6943ce40779869fe1c1f14d09d8c04c3 Mon Sep 17 00:00:00 2001 From: "bert.hausmans" Date: Mon, 11 May 2026 01:14:15 +0200 Subject: [PATCH 09/10] feat(login): migrate login form to FormField + Zod (F3 sample, validates FormField API) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the Vuetify VForm + AppTextField + VBtn stack with the F3 form pattern: @primevue/forms' with a Zod resolver, the project-owned wrapper from B5, and PrimeVue InputText / Password / Checkbox / Button at the input layer. Surrounding chrome (VRow / VCol illustration column, VCard, VAlert reset-success banner, auth-logo link, MfaChallengeCard) stays Vuetify until F4b migrates the auth surface in full. Zod schema: - email: required, valid email format - password: required Both messages are Dutch (per F3 sprint plan convention). 422 error handling routes through useFormError() from B5. The Laravel response shape (errors.: string[]) feeds applyApiErrors directly. rate_limited and other reason-only failures are synthesized into the email field's error map so they surface visually under the email input, preserving the existing UX. The remember-me checkbox is rendered with PrimeVue Checkbox (no schema coverage — it's UI state, not validated input). The password visibility toggle is delegated to PrimeVue's Password component's built-in toggle-mask prop (replaces the previous manual isPasswordVisible ref and append-inner-icon plumbing). Verification: - pnpm typecheck — clean. - pnpm test — 402 tests pass unchanged. - pnpm build — succeeds; login chunk grew from ~21 KB to ~84 KB raw due to @primevue/forms + Password/Checkbox component code (gzip 22 KB). Will normalize during F4 as more pages share these modules. - Manual browser test deferred to Phase C brand-review screenshot capture. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/app/src/pages/login.vue | 201 ++++++++++++++++++----------------- 1 file changed, 102 insertions(+), 99 deletions(-) diff --git a/apps/app/src/pages/login.vue b/apps/app/src/pages/login.vue index 42ecb448..0384c735 100644 --- a/apps/app/src/pages/login.vue +++ b/apps/app/src/pages/login.vue @@ -1,5 +1,11 @@