unspaghettit 0.1.7 → 0.2.0
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/README.md +30 -10
- package/build/client/_app/immutable/assets/0.DFMDYAU9.css +1 -0
- package/build/client/_app/immutable/assets/0.DFMDYAU9.css.br +0 -0
- package/build/client/_app/immutable/assets/0.DFMDYAU9.css.gz +0 -0
- package/build/client/_app/immutable/assets/3.B3akUL9A.css +1 -0
- package/build/client/_app/immutable/assets/3.B3akUL9A.css.br +0 -0
- package/build/client/_app/immutable/assets/3.B3akUL9A.css.gz +0 -0
- package/build/client/_app/immutable/chunks/{Buny4Lxq.js → BO66rBOa2.js} +1 -1
- package/build/client/_app/immutable/chunks/BO66rBOa2.js.br +0 -0
- package/build/client/_app/immutable/chunks/BO66rBOa2.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{BTLTzKaZ.js → BOrLmFwJ.js} +1 -1
- package/build/client/_app/immutable/chunks/BOrLmFwJ.js.br +0 -0
- package/build/client/_app/immutable/chunks/BOrLmFwJ.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BPl9BITm.js +3 -0
- package/build/client/_app/immutable/chunks/BPl9BITm.js.br +0 -0
- package/build/client/_app/immutable/chunks/BPl9BITm.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BfMYiIUi.js +1 -0
- package/build/client/_app/immutable/chunks/BfMYiIUi.js.br +0 -0
- package/build/client/_app/immutable/chunks/BfMYiIUi.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Bp2_Vghh.js +1 -0
- package/build/client/_app/immutable/chunks/Bp2_Vghh.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bp2_Vghh.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{-s9hRK72.js → Bp8HpEPe2.js} +1 -1
- package/build/client/_app/immutable/chunks/Bp8HpEPe2.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bp8HpEPe2.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C8rRKIXT.js +1 -0
- package/build/client/_app/immutable/chunks/C8rRKIXT.js.br +0 -0
- package/build/client/_app/immutable/chunks/C8rRKIXT.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{CDgx1pP22.js → CJjJLwzv2.js} +1 -1
- package/build/client/_app/immutable/chunks/CJjJLwzv2.js.br +0 -0
- package/build/client/_app/immutable/chunks/CJjJLwzv2.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Cl7Z3e6-.js +1 -0
- package/build/client/_app/immutable/chunks/Cl7Z3e6-.js.br +0 -0
- package/build/client/_app/immutable/chunks/Cl7Z3e6-.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CxyLMlpt2.js +2 -0
- package/build/client/_app/immutable/chunks/CxyLMlpt2.js.br +0 -0
- package/build/client/_app/immutable/chunks/CxyLMlpt2.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D6jhrpTg.js +1 -0
- package/build/client/_app/immutable/chunks/D6jhrpTg.js.br +0 -0
- package/build/client/_app/immutable/chunks/D6jhrpTg.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D8YEa1po.js +1 -0
- package/build/client/_app/immutable/chunks/D8YEa1po.js.br +0 -0
- package/build/client/_app/immutable/chunks/D8YEa1po.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D9dKmajw.js +1 -0
- package/build/client/_app/immutable/chunks/D9dKmajw.js.br +0 -0
- package/build/client/_app/immutable/chunks/D9dKmajw.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DBJWcC6Y.js +1 -0
- package/build/client/_app/immutable/chunks/DBJWcC6Y.js.br +0 -0
- package/build/client/_app/immutable/chunks/DBJWcC6Y.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DHoA038D.js +1 -0
- package/build/client/_app/immutable/chunks/DHoA038D.js.br +2 -0
- package/build/client/_app/immutable/chunks/DHoA038D.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DKgYpi6_.js +1 -0
- package/build/client/_app/immutable/chunks/DKgYpi6_.js.br +0 -0
- package/build/client/_app/immutable/chunks/DKgYpi6_.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DatGSObE.js +1 -0
- package/build/client/_app/immutable/chunks/DatGSObE.js.br +0 -0
- package/build/client/_app/immutable/chunks/DatGSObE.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dg0acPpT.js +1 -0
- package/build/client/_app/immutable/chunks/Dg0acPpT.js.br +0 -0
- package/build/client/_app/immutable/chunks/Dg0acPpT.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DjWKKtqp.js +1 -0
- package/build/client/_app/immutable/chunks/DjWKKtqp.js.br +1 -0
- package/build/client/_app/immutable/chunks/DjWKKtqp.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dq0DUAz1.js +1 -0
- package/build/client/_app/immutable/chunks/Dq0DUAz1.js.br +0 -0
- package/build/client/_app/immutable/chunks/Dq0DUAz1.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{Q8pmKDJe.js → HKLi1Yz3.js} +1 -1
- package/build/client/_app/immutable/chunks/HKLi1Yz3.js.br +0 -0
- package/build/client/_app/immutable/chunks/HKLi1Yz3.js.gz +0 -0
- package/build/client/_app/immutable/chunks/N7PXjlRH.js +1 -0
- package/build/client/_app/immutable/chunks/N7PXjlRH.js.br +0 -0
- package/build/client/_app/immutable/chunks/N7PXjlRH.js.gz +0 -0
- package/build/client/_app/immutable/chunks/aNMRV4sP2.js +5 -0
- package/build/client/_app/immutable/chunks/aNMRV4sP2.js.br +0 -0
- package/build/client/_app/immutable/chunks/aNMRV4sP2.js.gz +0 -0
- package/build/client/_app/immutable/chunks/cMamJTsF.js +1 -0
- package/build/client/_app/immutable/chunks/cMamJTsF.js.br +0 -0
- package/build/client/_app/immutable/chunks/cMamJTsF.js.gz +0 -0
- package/build/client/_app/immutable/chunks/hpWJNn0t2.js +2 -0
- package/build/client/_app/immutable/chunks/hpWJNn0t2.js.br +0 -0
- package/build/client/_app/immutable/chunks/hpWJNn0t2.js.gz +0 -0
- package/build/client/_app/immutable/chunks/iQu0D9Ux.js +1 -0
- package/build/client/_app/immutable/chunks/iQu0D9Ux.js.br +0 -0
- package/build/client/_app/immutable/chunks/iQu0D9Ux.js.gz +0 -0
- package/build/client/_app/immutable/chunks/ib21i2T_.js +6 -0
- package/build/client/_app/immutable/chunks/ib21i2T_.js.br +0 -0
- package/build/client/_app/immutable/chunks/ib21i2T_.js.gz +0 -0
- package/build/client/_app/immutable/chunks/suTxxv1t.js +1 -0
- package/build/client/_app/immutable/chunks/suTxxv1t.js.br +1 -0
- package/build/client/_app/immutable/chunks/suTxxv1t.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.CLgh6Mx_.js +2 -0
- package/build/client/_app/immutable/entry/app.CLgh6Mx_.js.br +0 -0
- package/build/client/_app/immutable/entry/app.CLgh6Mx_.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.D5GPCQZD.js +1 -0
- package/build/client/_app/immutable/entry/start.D5GPCQZD.js.br +0 -0
- package/build/client/_app/immutable/entry/start.D5GPCQZD.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.BOoI-hsu.js +4 -0
- package/build/client/_app/immutable/nodes/0.BOoI-hsu.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.BOoI-hsu.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.KYdA6ppX.js +1 -0
- package/build/client/_app/immutable/nodes/1.KYdA6ppX.js.br +2 -0
- package/build/client/_app/immutable/nodes/1.KYdA6ppX.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.DBz20KgG.js +1 -0
- package/build/client/_app/immutable/nodes/2.DBz20KgG.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.DBz20KgG.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.19DIoFtw.js +1 -0
- package/build/client/_app/immutable/nodes/3.19DIoFtw.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.19DIoFtw.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.BvMzqBJj.js +3 -0
- package/build/client/_app/immutable/nodes/4.BvMzqBJj.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.BvMzqBJj.js.gz +0 -0
- package/build/client/_app/immutable/nodes/5.Dq6obSGG.js +42 -0
- package/build/client/_app/immutable/nodes/5.Dq6obSGG.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.Dq6obSGG.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.BOHISqs-.js +5 -0
- package/build/client/_app/immutable/nodes/6.BOHISqs-.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.BOHISqs-.js.gz +0 -0
- package/build/client/_app/immutable/nodes/7.CemgNJfw.js +1 -0
- package/build/client/_app/immutable/nodes/7.CemgNJfw.js.br +0 -0
- package/build/client/_app/immutable/nodes/7.CemgNJfw.js.gz +0 -0
- package/build/client/_app/immutable/nodes/8.DejSfIYh.js +5 -0
- package/build/client/_app/immutable/nodes/8.DejSfIYh.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.DejSfIYh.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{8.aFYV9MSb.js → 9.CMW6a2Lg.js} +51 -48
- package/build/client/_app/immutable/nodes/9.CMW6a2Lg.js.br +0 -0
- package/build/client/_app/immutable/nodes/9.CMW6a2Lg.js.gz +0 -0
- package/build/client/_app/version.json +1 -1
- package/build/client/_app/version.json.br +0 -0
- package/build/client/_app/version.json.gz +0 -0
- package/build/server/chunks/0-C_o0oz-N.js +15 -0
- package/build/server/chunks/0-C_o0oz-N.js.map +1 -0
- package/build/server/chunks/1-DPpKAKXV.js +9 -0
- package/build/server/chunks/{1-BM_rl7Bx.js.map → 1-DPpKAKXV.js.map} +1 -1
- package/build/server/chunks/2-AlfFqtL1.js +9 -0
- package/build/server/chunks/2-AlfFqtL1.js.map +1 -0
- package/build/server/chunks/3-vbjUt_51.js +16 -0
- package/build/server/chunks/3-vbjUt_51.js.map +1 -0
- package/build/server/chunks/4-BNow4x6D.js +16 -0
- package/build/server/chunks/4-BNow4x6D.js.map +1 -0
- package/build/server/chunks/5-D7OCJpxD.js +9 -0
- package/build/server/chunks/5-D7OCJpxD.js.map +1 -0
- package/build/server/chunks/6-QQ7r8Rd5.js +9 -0
- package/build/server/chunks/6-QQ7r8Rd5.js.map +1 -0
- package/build/server/chunks/7-CbPLGaIG.js +9 -0
- package/build/server/chunks/7-CbPLGaIG.js.map +1 -0
- package/build/server/chunks/8-CVO-E-sf.js +9 -0
- package/build/server/chunks/8-CVO-E-sf.js.map +1 -0
- package/build/server/chunks/9-DxT1baO5.js +9 -0
- package/build/server/chunks/9-DxT1baO5.js.map +1 -0
- package/build/server/chunks/{FeatureCard-gVIYCpot.js → FeatureCard-BQOY6gJQ.js} +2 -2
- package/build/server/chunks/{FeatureCard-gVIYCpot.js.map → FeatureCard-BQOY6gJQ.js.map} +1 -1
- package/build/server/chunks/{FeatureJson-BKCG8vk2.js → FeatureJson-Bv-qiOSf.js} +2 -2
- package/build/server/chunks/{FeatureJson-BKCG8vk2.js.map → FeatureJson-Bv-qiOSf.js.map} +1 -1
- package/build/server/chunks/FeatureTransforms-UWDHmWEg.js +311 -0
- package/build/server/chunks/FeatureTransforms-UWDHmWEg.js.map +1 -0
- package/build/server/chunks/FeatureValidator-B8qEjAUW.js +546 -0
- package/build/server/chunks/FeatureValidator-B8qEjAUW.js.map +1 -0
- package/build/server/chunks/{ManageTagsDialog-DO6XplIb.js → ManageTagsDialog-DBxA3tM2.js} +4 -3
- package/build/server/chunks/ManageTagsDialog-DBxA3tM2.js.map +1 -0
- package/build/server/chunks/{ProjectsIndex-Boeoj8Zh.js → ProjectsIndex-CoDrvRya.js} +8 -8
- package/build/server/chunks/ProjectsIndex-CoDrvRya.js.map +1 -0
- package/build/server/chunks/{RenameTag-nSZ6Leh-.js → RenameTag-BuWLMl1m.js} +2 -2
- package/build/server/chunks/{RenameTag-nSZ6Leh-.js.map → RenameTag-BuWLMl1m.js.map} +1 -1
- package/build/server/chunks/TagDotStrip-B1XQ0m0Y.js +88 -0
- package/build/server/chunks/TagDotStrip-B1XQ0m0Y.js.map +1 -0
- package/build/server/chunks/{TagFilterSelect-5EaFX5CY.js → TagFilterSelect-DbO0Qduh.js} +4 -3
- package/build/server/chunks/TagFilterSelect-DbO0Qduh.js.map +1 -0
- package/build/server/chunks/{TagPalette-CvQJ40bt.js → TagPalette-CtMNYCmu.js} +6 -2
- package/build/server/chunks/TagPalette-CtMNYCmu.js.map +1 -0
- package/build/server/chunks/{_layout.svelte-BV7JjoXo.js → _layout.svelte-BREws55o.js} +99 -37
- package/build/server/chunks/_layout.svelte-BREws55o.js.map +1 -0
- package/build/server/chunks/{_page.svelte-PO6PTc2Z.js → _page.svelte-B1s7jxaQ.js} +9 -8
- package/build/server/chunks/{_page.svelte-PO6PTc2Z.js.map → _page.svelte-B1s7jxaQ.js.map} +1 -1
- package/build/server/chunks/_page.svelte-B7hT3P8E.js +29 -0
- package/build/server/chunks/{_page.svelte-D-CofieG.js.map → _page.svelte-B7hT3P8E.js.map} +1 -1
- package/build/server/chunks/{_page.svelte-B0w194rX.js → _page.svelte-BKKCa9H5.js} +11 -8
- package/build/server/chunks/{_page.svelte-B0w194rX.js.map → _page.svelte-BKKCa9H5.js.map} +1 -1
- package/build/server/chunks/{_page.svelte-DDOUOH7d.js → _page.svelte-BKTveFAj.js} +16 -14
- package/build/server/chunks/_page.svelte-BKTveFAj.js.map +1 -0
- package/build/server/chunks/_page.svelte-BqSC-1vK.js +404 -0
- package/build/server/chunks/_page.svelte-BqSC-1vK.js.map +1 -0
- package/build/server/chunks/{_page.svelte-AXKie8xV.js → _page.svelte-BtI2zZ_Z.js} +38 -282
- package/build/server/chunks/_page.svelte-BtI2zZ_Z.js.map +1 -0
- package/build/server/chunks/_page.svelte-De508ek8.js +29 -0
- package/build/server/chunks/{_page.svelte-D4bCUYdF.js.map → _page.svelte-De508ek8.js.map} +1 -1
- package/build/server/chunks/{_page.svelte-BSo9Np-N.js → _page.svelte-Zf9H4KOP.js} +16 -14
- package/build/server/chunks/_page.svelte-Zf9H4KOP.js.map +1 -0
- package/build/server/chunks/{_server.ts-B3vd_taO.js → _server.ts-0uNTZKwF.js} +7 -6
- package/build/server/chunks/{_server.ts-B3vd_taO.js.map → _server.ts-0uNTZKwF.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CzrUSRs3.js → _server.ts-BIBWjQI-.js} +5 -4
- package/build/server/chunks/{_server.ts-CzrUSRs3.js.map → _server.ts-BIBWjQI-.js.map} +1 -1
- package/build/server/chunks/{_server.ts-DHMUKVg3.js → _server.ts-BihpbxOW.js} +5 -4
- package/build/server/chunks/{_server.ts-DHMUKVg3.js.map → _server.ts-BihpbxOW.js.map} +1 -1
- package/build/server/chunks/{_server.ts-das509Hg.js → _server.ts-BuoQ02BW.js} +5 -4
- package/build/server/chunks/{_server.ts-das509Hg.js.map → _server.ts-BuoQ02BW.js.map} +1 -1
- package/build/server/chunks/{_server.ts-C7c3v6sB.js → _server.ts-CWvU15tL.js} +5 -4
- package/build/server/chunks/{_server.ts-C7c3v6sB.js.map → _server.ts-CWvU15tL.js.map} +1 -1
- package/build/server/chunks/{_server.ts-DG5OCqvO.js → _server.ts-D4EO0VHu.js} +6 -5
- package/build/server/chunks/{_server.ts-DG5OCqvO.js.map → _server.ts-D4EO0VHu.js.map} +1 -1
- package/build/server/chunks/{_server.ts-DYI7zGy6.js → _server.ts-DDa0Nk_S.js} +5 -4
- package/build/server/chunks/{_server.ts-DYI7zGy6.js.map → _server.ts-DDa0Nk_S.js.map} +1 -1
- package/build/server/chunks/{_server.ts-C_4ug89F.js → _server.ts-DcZPhyhK.js} +7 -6
- package/build/server/chunks/{_server.ts-C_4ug89F.js.map → _server.ts-DcZPhyhK.js.map} +1 -1
- package/build/server/chunks/{_server.ts-96Zq9H2j.js → _server.ts-DsbbVoOn.js} +5 -4
- package/build/server/chunks/{_server.ts-96Zq9H2j.js.map → _server.ts-DsbbVoOn.js.map} +1 -1
- package/build/server/chunks/{_server.ts-D0xxTo-S.js → _server.ts-Dy5zQHe2.js} +8 -7
- package/build/server/chunks/{_server.ts-D0xxTo-S.js.map → _server.ts-Dy5zQHe2.js.map} +1 -1
- package/build/server/chunks/{_server.ts-Dz_KfmPw.js → _server.ts-YnfXkeMJ.js} +8 -7
- package/build/server/chunks/_server.ts-YnfXkeMJ.js.map +1 -0
- package/build/server/chunks/{_server.ts-BRlHKXzL.js → _server.ts-vcaqdwxS.js} +5 -4
- package/build/server/chunks/_server.ts-vcaqdwxS.js.map +1 -0
- package/build/server/chunks/{browserContainer-Cx0w0pZx.js → browserContainer-CNX7ANld.js} +123 -37
- package/build/server/chunks/browserContainer-CNX7ANld.js.map +1 -0
- package/build/server/chunks/builderModeStore.svelte-ihupr-3p.js +681 -0
- package/build/server/chunks/builderModeStore.svelte-ihupr-3p.js.map +1 -0
- package/build/server/chunks/{client-f1rhU0pP.js → client-DfpLcAZ9.js} +2 -2
- package/build/server/chunks/{client-f1rhU0pP.js.map → client-DfpLcAZ9.js.map} +1 -1
- package/build/server/chunks/{error.svelte-Sw2nazu6.js → error.svelte-C35KOpru.js} +4 -4
- package/build/server/chunks/{error.svelte-Sw2nazu6.js.map → error.svelte-C35KOpru.js.map} +1 -1
- package/build/server/chunks/eventBus-B9k-Vzso.js +19 -0
- package/build/server/chunks/eventBus-B9k-Vzso.js.map +1 -0
- package/build/server/chunks/{featuresStore.svelte-B-f41QwP.js → featuresStore.svelte-DR1gYZyu.js} +6 -5
- package/build/server/chunks/featuresStore.svelte-DR1gYZyu.js.map +1 -0
- package/build/server/chunks/{internal-cdqLNkR1.js → internal-BPKrFkK1.js} +2 -2
- package/build/server/chunks/{internal-cdqLNkR1.js.map → internal-BPKrFkK1.js.map} +1 -1
- package/build/server/chunks/{projectsStore.svelte-C2Swf2Gc.js → projectsStore.svelte-QDz5PchX.js} +3 -2
- package/build/server/chunks/projectsStore.svelte-QDz5PchX.js.map +1 -0
- package/build/server/chunks/{reconcile-BIZjLR73.js → reconcile-Dv7jS3C8.js} +3 -3
- package/build/server/chunks/{reconcile-BIZjLR73.js.map → reconcile-Dv7jS3C8.js.map} +1 -1
- package/build/server/chunks/shared-server-CCs2CV3b.js +14 -0
- package/build/server/chunks/shared-server-CCs2CV3b.js.map +1 -0
- package/build/server/chunks/{snapshotRepository-DoWj6ZGG.js → snapshotRepository-BPXCjX92.js} +43 -11
- package/build/server/chunks/snapshotRepository-BPXCjX92.js.map +1 -0
- package/build/server/chunks/{state-CxL934Yr.js → state-CpLVNZq7.js} +2 -2
- package/build/server/chunks/{state-CxL934Yr.js.map → state-CpLVNZq7.js.map} +1 -1
- package/build/server/chunks/{sync-1tNFVUfQ.js → sync-DZ3dn761.js} +2 -2
- package/build/server/chunks/{sync-1tNFVUfQ.js.map → sync-DZ3dn761.js.map} +1 -1
- package/build/server/chunks/{syncBridge-BQDytwK1.js → syncBridge-BwXzPdjJ.js} +2 -2
- package/build/server/chunks/{syncBridge-BQDytwK1.js.map → syncBridge-BwXzPdjJ.js.map} +1 -1
- package/build/server/chunks/{TagDotStrip-BR9Owgav.js → tagPaletteStore.svelte-DfZOBvgY.js} +5 -87
- package/build/server/chunks/tagPaletteStore.svelte-DfZOBvgY.js.map +1 -0
- package/build/server/chunks/{tourStore.svelte-ExebiCB4.js → tourStore.svelte-CYs4oLjF.js} +2 -2
- package/build/server/chunks/tourStore.svelte-CYs4oLjF.js.map +1 -0
- package/build/server/index.js +2 -12
- package/build/server/index.js.map +1 -1
- package/build/server/manifest.js +36 -28
- package/build/server/manifest.js.map +1 -1
- package/cli/README.md +46 -20
- package/cli/clients/claude-code.ts +5 -4
- package/cli/clients/claude-desktop.ts +5 -4
- package/cli/commands/dashboard.ts +38 -4
- package/cli/commands/init.ts +147 -84
- package/cli/commands/view.ts +77 -0
- package/cli/skills/unspa-audit/SKILL.md +9 -0
- package/cli/skills/unspa-edit/SKILL.md +91 -0
- package/cli/skills/unspa-implement/SKILL.md +46 -1
- package/cli/unspa.ts +57 -13
- package/cli/util/context-files.ts +5 -3
- package/cli/util/views.ts +38 -0
- package/mcp-server/resources/guide.ts +6 -4
- package/mcp-server/resources/operations.ts +11 -4
- package/mcp-server/server.ts +5 -1
- package/mcp-server/tools/_entity_builders.ts +44 -2
- package/mcp-server/tools/_evolution.ts +53 -0
- package/mcp-server/tools/action.ts +64 -6
- package/mcp-server/tools/batch.ts +1276 -1217
- package/mcp-server/tools/behavioralIndex.ts +193 -193
- package/mcp-server/tools/feature.ts +17 -9
- package/mcp-server/tools/parameter.ts +179 -172
- package/mcp-server/tools/queue.ts +101 -9
- package/mcp-server/tools/scenario.ts +19 -4
- package/mcp-server/tools/specGaps.ts +14 -1
- package/mcp-server/tools/state.ts +165 -156
- package/mcp-server/tools/valueSet.ts +95 -0
- package/package.json +3 -1
- package/src/features/behavior-model/application/use-cases/MutateFeature.ts +20 -31
- package/src/features/behavior-model/application/use-cases/SaveFeature.ts +13 -9
- package/src/features/behavior-model/domain/entities/Action.ts +167 -95
- package/src/features/behavior-model/domain/entities/Feature.ts +60 -51
- package/src/features/behavior-model/domain/entities/Parameter.ts +40 -34
- package/src/features/behavior-model/domain/entities/Scenario.ts +109 -67
- package/src/features/behavior-model/domain/entities/StateDefinition.ts +8 -1
- package/src/features/behavior-model/domain/entities/ValueSet.ts +16 -0
- package/src/features/behavior-model/domain/services/EnumValues.ts +27 -0
- package/src/features/behavior-model/domain/services/FeatureTransforms.ts +1052 -984
- package/src/features/behavior-model/domain/services/FeatureValidator.ts +968 -815
- package/src/features/behavior-model/domain/services/ParameterValidator.ts +12 -8
- package/src/features/behavior-model/domain/value-objects/ids.ts +35 -33
- package/src/features/behavior-model/infrastructure/persistence/InMemoryFeatureRepository.ts +37 -37
- package/src/features/behavior-model/infrastructure/persistence/JsonFolderFeatureRepository.ts +1 -1
- package/src/features/behavior-model/infrastructure/persistence/snapshot-discovery.ts +28 -3
- package/src/features/behavior-model/presentation/components/ActionEditor.svelte +55 -7
- package/src/features/builder-mode/application/use-cases/GetBuilderModeDashboard.ts +41 -0
- package/src/features/builder-mode/application/use-cases/GetBuilderModeProject.ts +42 -0
- package/src/features/builder-mode/domain/BuilderModeDashboard.ts +172 -0
- package/src/features/builder-mode/infrastructure/adapters/defaultBuilderHost.ts +27 -0
- package/src/features/builder-mode/presentation/components/BuilderModeDashboard.svelte +1335 -0
- package/src/features/builder-mode/presentation/components/BuilderTagChips.svelte +289 -0
- package/src/features/builder-mode/presentation/components/ClampedDescription.svelte +174 -0
- package/src/features/builder-mode/presentation/components/GameScore.svelte +194 -0
- package/src/features/builder-mode/presentation/ports/BuilderHostPorts.ts +39 -0
- package/src/features/builder-mode/presentation/stores/builderModeStore.svelte.ts +679 -0
- package/src/features/domains/infrastructure/persistence/InMemoryDomainRepository.ts +1 -1
- package/src/features/domains/infrastructure/persistence/JsonFolderDomainRepository.ts +1 -1
- package/src/features/implementation-queue/application/use-cases/EnqueueQueueItem.ts +16 -5
- package/src/features/implementation-queue/application/use-cases/SetQueueItemTarget.ts +38 -0
- package/src/features/implementation-queue/domain/entities/QueueItem.ts +43 -0
- package/src/features/implementation-queue/domain/services/QueueOperations.ts +26 -1
- package/src/features/maturity/domain/MaturityScorer.ts +13 -4
- package/src/features/mcp-tools/application/tools/generateTypes.ts +254 -250
- package/src/features/mcp-tools/application/tools/listActions.ts +9 -1
- package/src/features/projects/infrastructure/io/ProjectJson.ts +25 -4
- package/src/features/projects/infrastructure/persistence/InMemoryProjectRepository.ts +33 -33
- package/src/features/projects/infrastructure/persistence/JsonFolderProjectRepository.ts +1 -1
- package/src/features/simulator/application/use-cases/RunScenarios.ts +449 -310
- package/src/features/simulator/domain/SimulatorEngine.ts +324 -320
- package/src/lib/views/enabled.ts +19 -0
- package/src/lib/views/registry.ts +72 -0
- package/src/routes/+layout.svelte +583 -348
- package/src/routes/builder-mode/+page.svelte +9 -0
- package/src/routes/builder-mode/+page.ts +12 -0
- package/src/routes/tutorial/+page.svelte +8 -0
- package/src/shared/infrastructure/container.ts +208 -199
- package/build/client/_app/immutable/assets/0.BgNAkMzG.css +0 -1
- package/build/client/_app/immutable/assets/0.BgNAkMzG.css.br +0 -0
- package/build/client/_app/immutable/assets/0.BgNAkMzG.css.gz +0 -0
- package/build/client/_app/immutable/chunks/-s9hRK72.js.br +0 -0
- package/build/client/_app/immutable/chunks/-s9hRK72.js.gz +0 -0
- package/build/client/_app/immutable/chunks/1_r1S7Ly.js +0 -1
- package/build/client/_app/immutable/chunks/1_r1S7Ly.js.br +0 -0
- package/build/client/_app/immutable/chunks/1_r1S7Ly.js.gz +0 -0
- package/build/client/_app/immutable/chunks/B-lvxr_Y.js +0 -1
- package/build/client/_app/immutable/chunks/B-lvxr_Y.js.br +0 -0
- package/build/client/_app/immutable/chunks/B-lvxr_Y.js.gz +0 -0
- package/build/client/_app/immutable/chunks/B4Mul34I.js +0 -1
- package/build/client/_app/immutable/chunks/B4Mul34I.js.br +0 -2
- package/build/client/_app/immutable/chunks/B4Mul34I.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BQQLoxQo.js +0 -5
- package/build/client/_app/immutable/chunks/BQQLoxQo.js.br +0 -0
- package/build/client/_app/immutable/chunks/BQQLoxQo.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BTLTzKaZ.js.br +0 -1
- package/build/client/_app/immutable/chunks/BTLTzKaZ.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BY81EmU42.js +0 -2
- package/build/client/_app/immutable/chunks/BY81EmU42.js.br +0 -0
- package/build/client/_app/immutable/chunks/BY81EmU42.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BiHfqo9Y.js +0 -2
- package/build/client/_app/immutable/chunks/BiHfqo9Y.js.br +0 -0
- package/build/client/_app/immutable/chunks/BiHfqo9Y.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Buny4Lxq.js.br +0 -0
- package/build/client/_app/immutable/chunks/Buny4Lxq.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CDgx1pP22.js.br +0 -0
- package/build/client/_app/immutable/chunks/CDgx1pP22.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CKUA0lwj.js +0 -1
- package/build/client/_app/immutable/chunks/CKUA0lwj.js.br +0 -0
- package/build/client/_app/immutable/chunks/CKUA0lwj.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CM5qK71w.js +0 -6
- package/build/client/_app/immutable/chunks/CM5qK71w.js.br +0 -0
- package/build/client/_app/immutable/chunks/CM5qK71w.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CvxLPre7.js +0 -1
- package/build/client/_app/immutable/chunks/CvxLPre7.js.br +0 -0
- package/build/client/_app/immutable/chunks/CvxLPre7.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D9BWIMZD.js +0 -1
- package/build/client/_app/immutable/chunks/D9BWIMZD.js.br +0 -0
- package/build/client/_app/immutable/chunks/D9BWIMZD.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DC_h2his.js +0 -1
- package/build/client/_app/immutable/chunks/DC_h2his.js.br +0 -0
- package/build/client/_app/immutable/chunks/DC_h2his.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DF6QYH3p.js +0 -3
- package/build/client/_app/immutable/chunks/DF6QYH3p.js.br +0 -0
- package/build/client/_app/immutable/chunks/DF6QYH3p.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DHnrY7Sb.js +0 -1
- package/build/client/_app/immutable/chunks/DHnrY7Sb.js.br +0 -0
- package/build/client/_app/immutable/chunks/DHnrY7Sb.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dv09pO87.js +0 -1
- package/build/client/_app/immutable/chunks/Dv09pO87.js.br +0 -0
- package/build/client/_app/immutable/chunks/Dv09pO87.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dvd8XBNO.js +0 -1
- package/build/client/_app/immutable/chunks/Dvd8XBNO.js.br +0 -1
- package/build/client/_app/immutable/chunks/Dvd8XBNO.js.gz +0 -0
- package/build/client/_app/immutable/chunks/K3JxBPSQ.js +0 -1
- package/build/client/_app/immutable/chunks/K3JxBPSQ.js.br +0 -0
- package/build/client/_app/immutable/chunks/K3JxBPSQ.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Q8pmKDJe.js.br +0 -0
- package/build/client/_app/immutable/chunks/Q8pmKDJe.js.gz +0 -0
- package/build/client/_app/immutable/chunks/gQ3oD_Dm.js +0 -1
- package/build/client/_app/immutable/chunks/gQ3oD_Dm.js.br +0 -0
- package/build/client/_app/immutable/chunks/gQ3oD_Dm.js.gz +0 -0
- package/build/client/_app/immutable/chunks/lHLL3RmT.js +0 -1
- package/build/client/_app/immutable/chunks/lHLL3RmT.js.br +0 -0
- package/build/client/_app/immutable/chunks/lHLL3RmT.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.DdaNJvmo.js +0 -2
- package/build/client/_app/immutable/entry/app.DdaNJvmo.js.br +0 -0
- package/build/client/_app/immutable/entry/app.DdaNJvmo.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.Cu21IWs-.js +0 -1
- package/build/client/_app/immutable/entry/start.Cu21IWs-.js.br +0 -0
- package/build/client/_app/immutable/entry/start.Cu21IWs-.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.C1BTEwKm.js +0 -3
- package/build/client/_app/immutable/nodes/0.C1BTEwKm.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.C1BTEwKm.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.C3Ve_ouq.js +0 -1
- package/build/client/_app/immutable/nodes/1.C3Ve_ouq.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.C3Ve_ouq.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.CRcsCTzc.js +0 -1
- package/build/client/_app/immutable/nodes/2.CRcsCTzc.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.CRcsCTzc.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.rv7UrHHg.js +0 -3
- package/build/client/_app/immutable/nodes/3.rv7UrHHg.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.rv7UrHHg.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.Cev4WT4u.js +0 -42
- package/build/client/_app/immutable/nodes/4.Cev4WT4u.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.Cev4WT4u.js.gz +0 -0
- package/build/client/_app/immutable/nodes/5.0DKVfFv2.js +0 -5
- package/build/client/_app/immutable/nodes/5.0DKVfFv2.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.0DKVfFv2.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.DCJwg-TV.js +0 -1
- package/build/client/_app/immutable/nodes/6.DCJwg-TV.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.DCJwg-TV.js.gz +0 -0
- package/build/client/_app/immutable/nodes/7.BFdt4Nqg.js +0 -5
- package/build/client/_app/immutable/nodes/7.BFdt4Nqg.js.br +0 -0
- package/build/client/_app/immutable/nodes/7.BFdt4Nqg.js.gz +0 -0
- package/build/client/_app/immutable/nodes/8.aFYV9MSb.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.aFYV9MSb.js.gz +0 -0
- package/build/server/chunks/0-XDHB9lbZ.js +0 -15
- package/build/server/chunks/0-XDHB9lbZ.js.map +0 -1
- package/build/server/chunks/1-BM_rl7Bx.js +0 -9
- package/build/server/chunks/2-DmFhuHKK.js +0 -9
- package/build/server/chunks/2-DmFhuHKK.js.map +0 -1
- package/build/server/chunks/3-B3Ro5GyH.js +0 -16
- package/build/server/chunks/3-B3Ro5GyH.js.map +0 -1
- package/build/server/chunks/4-cj58VJgj.js +0 -9
- package/build/server/chunks/4-cj58VJgj.js.map +0 -1
- package/build/server/chunks/5-BW_j9O5v.js +0 -9
- package/build/server/chunks/5-BW_j9O5v.js.map +0 -1
- package/build/server/chunks/6-Bj0uWvDK.js +0 -9
- package/build/server/chunks/6-Bj0uWvDK.js.map +0 -1
- package/build/server/chunks/7-DiKTsjh1.js +0 -9
- package/build/server/chunks/7-DiKTsjh1.js.map +0 -1
- package/build/server/chunks/8-CZiwFSj3.js +0 -9
- package/build/server/chunks/8-CZiwFSj3.js.map +0 -1
- package/build/server/chunks/FeatureValidator-BcY63x9D.js +0 -262
- package/build/server/chunks/FeatureValidator-BcY63x9D.js.map +0 -1
- package/build/server/chunks/ManageTagsDialog-DO6XplIb.js.map +0 -1
- package/build/server/chunks/ProjectsIndex-Boeoj8Zh.js.map +0 -1
- package/build/server/chunks/TagDotStrip-BR9Owgav.js.map +0 -1
- package/build/server/chunks/TagFilterSelect-5EaFX5CY.js.map +0 -1
- package/build/server/chunks/TagPalette-CvQJ40bt.js.map +0 -1
- package/build/server/chunks/_layout.svelte-BV7JjoXo.js.map +0 -1
- package/build/server/chunks/_page.svelte-AXKie8xV.js.map +0 -1
- package/build/server/chunks/_page.svelte-BSo9Np-N.js.map +0 -1
- package/build/server/chunks/_page.svelte-D-CofieG.js +0 -27
- package/build/server/chunks/_page.svelte-D4bCUYdF.js +0 -27
- package/build/server/chunks/_page.svelte-DDOUOH7d.js.map +0 -1
- package/build/server/chunks/_server.ts-BRlHKXzL.js.map +0 -1
- package/build/server/chunks/_server.ts-Dz_KfmPw.js.map +0 -1
- package/build/server/chunks/browserContainer-Cx0w0pZx.js.map +0 -1
- package/build/server/chunks/featuresStore.svelte-B-f41QwP.js.map +0 -1
- package/build/server/chunks/projectsStore.svelte-C2Swf2Gc.js.map +0 -1
- package/build/server/chunks/snapshotRepository-DoWj6ZGG.js.map +0 -1
- package/build/server/chunks/tourStore.svelte-ExebiCB4.js.map +0 -1
- /package/build/client/_app/immutable/assets/{8.nv0I59TU.css → 9.nv0I59TU.css} +0 -0
- /package/build/client/_app/immutable/assets/{8.nv0I59TU.css.br → 9.nv0I59TU.css.br} +0 -0
- /package/build/client/_app/immutable/assets/{8.nv0I59TU.css.gz → 9.nv0I59TU.css.gz} +0 -0
- /package/build/client/_app/immutable/chunks/{BQ9GodWX.js → BQ9GodWX2.js} +0 -0
- /package/build/client/_app/immutable/chunks/{BQ9GodWX.js.br → BQ9GodWX2.js.br} +0 -0
- /package/build/client/_app/immutable/chunks/{BQ9GodWX.js.gz → BQ9GodWX2.js.gz} +0 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
let nextDatalistSerial = 0;
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import { tick } from 'svelte';
|
|
7
|
+
import { humanizeTagText, tagKey, tagLabel, type Tag } from '$shared/domain/Tags';
|
|
8
|
+
import { builderModeStore } from '$features/builder-mode/presentation/stores/builderModeStore.svelte';
|
|
9
|
+
import type { TagColor } from '$shared/domain/TagPalette';
|
|
10
|
+
import type { Action } from 'svelte/action';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Smoothly tween the wrapper's height to its content height whenever that
|
|
14
|
+
* content changes — the add `+` appearing on hover, or chips wrapping to a
|
|
15
|
+
* new line. Without this the card would jump in height.
|
|
16
|
+
*
|
|
17
|
+
* While a form is open (`editing`) the wrapper goes `overflow: visible` /
|
|
18
|
+
* `height: auto`: a clipped, fixed-height box throws off the native datalist
|
|
19
|
+
* dropdown's anchor (it renders far from the input) and would clip the form.
|
|
20
|
+
* Honors prefers-reduced-motion.
|
|
21
|
+
*/
|
|
22
|
+
const animateHeight: Action<HTMLElement, boolean> = (node, editing = false) => {
|
|
23
|
+
const inner = node.firstElementChild as HTMLElement | null;
|
|
24
|
+
if (!inner) return;
|
|
25
|
+
const reduce =
|
|
26
|
+
typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
27
|
+
const hasObserver = typeof ResizeObserver !== 'undefined';
|
|
28
|
+
let last = inner.offsetHeight;
|
|
29
|
+
|
|
30
|
+
const toOpen = () => {
|
|
31
|
+
// Unclip so inputs and the native type dropdown lay out / anchor correctly.
|
|
32
|
+
node.style.transition = '';
|
|
33
|
+
node.style.overflow = 'visible';
|
|
34
|
+
node.style.height = 'auto';
|
|
35
|
+
};
|
|
36
|
+
const toClipped = () => {
|
|
37
|
+
node.style.overflow = 'hidden';
|
|
38
|
+
if (!hasObserver) {
|
|
39
|
+
node.style.height = 'auto';
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const start = node.offsetHeight;
|
|
43
|
+
last = inner.offsetHeight;
|
|
44
|
+
if (reduce) {
|
|
45
|
+
node.style.height = `${last}px`;
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
node.style.transition = 'height 240ms cubic-bezier(0.22, 1, 0.36, 1)';
|
|
49
|
+
node.style.height = `${start}px`;
|
|
50
|
+
void node.offsetHeight; // commit the start height before transitioning
|
|
51
|
+
node.style.height = `${last}px`;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
let isEditing = editing;
|
|
55
|
+
if (isEditing) toOpen();
|
|
56
|
+
else {
|
|
57
|
+
node.style.overflow = 'hidden';
|
|
58
|
+
node.style.height = `${last}px`;
|
|
59
|
+
if (!reduce) node.style.transition = 'height 240ms cubic-bezier(0.22, 1, 0.36, 1)';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let observer: ResizeObserver | null = null;
|
|
63
|
+
if (hasObserver) {
|
|
64
|
+
observer = new ResizeObserver(() => {
|
|
65
|
+
if (isEditing) return; // height:auto handles growth while a form is open
|
|
66
|
+
const next = inner.offsetHeight;
|
|
67
|
+
if (next === last) return;
|
|
68
|
+
if (reduce) {
|
|
69
|
+
node.style.height = `${next}px`;
|
|
70
|
+
last = next;
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
node.style.height = `${last}px`;
|
|
74
|
+
void node.offsetHeight;
|
|
75
|
+
node.style.height = `${next}px`;
|
|
76
|
+
last = next;
|
|
77
|
+
});
|
|
78
|
+
observer.observe(inner);
|
|
79
|
+
} else if (!isEditing) {
|
|
80
|
+
node.style.height = 'auto';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
update(next: boolean) {
|
|
85
|
+
if (next === isEditing) return;
|
|
86
|
+
isEditing = next;
|
|
87
|
+
if (isEditing) toOpen();
|
|
88
|
+
else toClipped();
|
|
89
|
+
},
|
|
90
|
+
destroy: () => observer?.disconnect()
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
let {
|
|
95
|
+
tags,
|
|
96
|
+
onToggle,
|
|
97
|
+
isActive,
|
|
98
|
+
onAdd,
|
|
99
|
+
onRemove,
|
|
100
|
+
onRename,
|
|
101
|
+
typeOptions = [],
|
|
102
|
+
class: className = ''
|
|
103
|
+
}: {
|
|
104
|
+
readonly tags: readonly Tag[];
|
|
105
|
+
readonly onToggle?: (tag: Tag) => void;
|
|
106
|
+
readonly isActive?: (tag: Tag) => boolean;
|
|
107
|
+
readonly onAdd?: (type: string, value: string) => void | Promise<void>;
|
|
108
|
+
readonly onRemove?: (tag: Tag) => void | Promise<void>;
|
|
109
|
+
readonly onRename?: (from: Tag, to: Tag) => void | Promise<void>;
|
|
110
|
+
readonly typeOptions?: readonly string[];
|
|
111
|
+
readonly class?: string;
|
|
112
|
+
} = $props();
|
|
113
|
+
|
|
114
|
+
const datalistId = `buildertags-types-${++nextDatalistSerial}`;
|
|
115
|
+
|
|
116
|
+
// Mix the palette color toward a deep slate for the dark builder plate (the
|
|
117
|
+
// shared light-theme styles mix toward white). `active` brightens the chip.
|
|
118
|
+
const SLATE = '#64748b';
|
|
119
|
+
const chipStyle = (color: TagColor | null, active: boolean): string => {
|
|
120
|
+
const base = color ?? SLATE;
|
|
121
|
+
const fill = active ? 42 : 20;
|
|
122
|
+
const ring = active ? 72 : 42;
|
|
123
|
+
return [
|
|
124
|
+
`background-color: color-mix(in srgb, ${base} ${fill}%, #0b1220)`,
|
|
125
|
+
`color: color-mix(in srgb, ${base} 72%, white)`,
|
|
126
|
+
`box-shadow: inset 0 0 0 1px color-mix(in srgb, ${base} ${ring}%, transparent)`
|
|
127
|
+
].join('; ');
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// ── add ──
|
|
131
|
+
let adding = $state(false);
|
|
132
|
+
let addType = $state('');
|
|
133
|
+
let addValue = $state('');
|
|
134
|
+
let addInput = $state<HTMLInputElement | null>(null);
|
|
135
|
+
|
|
136
|
+
async function startAdd() {
|
|
137
|
+
adding = true;
|
|
138
|
+
await tick();
|
|
139
|
+
addInput?.focus();
|
|
140
|
+
}
|
|
141
|
+
function cancelAdd() {
|
|
142
|
+
adding = false;
|
|
143
|
+
addType = '';
|
|
144
|
+
addValue = '';
|
|
145
|
+
}
|
|
146
|
+
async function submitAdd(event: SubmitEvent) {
|
|
147
|
+
event.preventDefault();
|
|
148
|
+
const value = addValue.trim();
|
|
149
|
+
if (!value) return;
|
|
150
|
+
const type = addType.trim() || 'tag';
|
|
151
|
+
cancelAdd(); // close the form immediately; the chip appears once persisted
|
|
152
|
+
await onAdd?.(type, value);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ── rename (propagates everywhere the tag is used) ──
|
|
156
|
+
let editingKey = $state<string | null>(null);
|
|
157
|
+
let editType = $state('');
|
|
158
|
+
let editValue = $state('');
|
|
159
|
+
let editInput = $state<HTMLInputElement | null>(null);
|
|
160
|
+
|
|
161
|
+
async function startEdit(tag: Tag) {
|
|
162
|
+
editingKey = tagKey(tag);
|
|
163
|
+
editType = humanizeTagText(tag.type);
|
|
164
|
+
editValue = humanizeTagText(tag.value);
|
|
165
|
+
await tick();
|
|
166
|
+
editInput?.select();
|
|
167
|
+
}
|
|
168
|
+
function cancelEdit() {
|
|
169
|
+
editingKey = null;
|
|
170
|
+
}
|
|
171
|
+
async function submitEdit(event: SubmitEvent, from: Tag) {
|
|
172
|
+
event.preventDefault();
|
|
173
|
+
const value = editValue.trim();
|
|
174
|
+
const type = editType.trim();
|
|
175
|
+
if (!value || !type) return;
|
|
176
|
+
cancelEdit();
|
|
177
|
+
await onRename?.(from, { type, value });
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function onFormKey(event: KeyboardEvent, cancel: () => void) {
|
|
181
|
+
if (event.key === 'Escape') cancel();
|
|
182
|
+
}
|
|
183
|
+
</script>
|
|
184
|
+
|
|
185
|
+
{#if tags.length > 0 || onAdd}
|
|
186
|
+
<div class={className} use:animateHeight={adding || editingKey !== null}>
|
|
187
|
+
<div class="flex flex-wrap items-center gap-1.5">
|
|
188
|
+
{#each tags as tag (tagKey(tag))}
|
|
189
|
+
{#if editingKey === tagKey(tag)}
|
|
190
|
+
<form class="inline-flex items-center gap-1" onsubmit={(e) => submitEdit(e, tag)}>
|
|
191
|
+
<input
|
|
192
|
+
bind:this={editInput}
|
|
193
|
+
bind:value={editType}
|
|
194
|
+
list={typeOptions.length > 0 ? datalistId : undefined}
|
|
195
|
+
placeholder="Type"
|
|
196
|
+
onkeydown={(e) => onFormKey(e, cancelEdit)}
|
|
197
|
+
class="h-6 w-16 rounded border border-slate-700 bg-slate-900/70 px-1.5 text-[11px] text-slate-200 outline-none focus:border-brand-500"
|
|
198
|
+
/>
|
|
199
|
+
<input
|
|
200
|
+
bind:value={editValue}
|
|
201
|
+
placeholder="Value"
|
|
202
|
+
onkeydown={(e) => onFormKey(e, cancelEdit)}
|
|
203
|
+
class="h-6 w-20 rounded border border-slate-700 bg-slate-900/70 px-1.5 text-[11px] text-slate-200 outline-none focus:border-brand-500"
|
|
204
|
+
/>
|
|
205
|
+
<button type="submit" class="text-[11px] font-semibold text-brand-300 hover:text-brand-200" title="Rename everywhere">✓</button>
|
|
206
|
+
<button type="button" class="text-[11px] text-slate-500 hover:text-slate-300" onclick={cancelEdit} title="Cancel">✕</button>
|
|
207
|
+
</form>
|
|
208
|
+
{:else}
|
|
209
|
+
{@const color = builderModeStore.colorForTag(tag)}
|
|
210
|
+
{@const active = isActive?.(tag) ?? false}
|
|
211
|
+
<span
|
|
212
|
+
class="group/chip inline-flex max-w-full items-center gap-1 rounded-full py-0.5 pl-2 pr-1.5 text-[10px] font-medium transition"
|
|
213
|
+
style={chipStyle(color, active)}
|
|
214
|
+
>
|
|
215
|
+
<span class="size-1.5 shrink-0 rounded-full" style="background-color: {color ?? SLATE}"></span>
|
|
216
|
+
{#if onToggle}
|
|
217
|
+
<button
|
|
218
|
+
type="button"
|
|
219
|
+
class="truncate"
|
|
220
|
+
onclick={() => onToggle(tag)}
|
|
221
|
+
aria-pressed={active}
|
|
222
|
+
title={`${tagLabel(tag)} — click to filter`}
|
|
223
|
+
>{humanizeTagText(tag.value)}</button>
|
|
224
|
+
{:else}
|
|
225
|
+
<span class="truncate" title={tagLabel(tag)}>{humanizeTagText(tag.value)}</span>
|
|
226
|
+
{/if}
|
|
227
|
+
{#if onRename}
|
|
228
|
+
<button
|
|
229
|
+
type="button"
|
|
230
|
+
class="shrink-0 opacity-0 transition group-hover/chip:opacity-70 hover:opacity-100!"
|
|
231
|
+
onclick={() => startEdit(tag)}
|
|
232
|
+
aria-label={`Rename ${tagLabel(tag)}`}
|
|
233
|
+
title="Rename (applies everywhere)"
|
|
234
|
+
>✎</button>
|
|
235
|
+
{/if}
|
|
236
|
+
{#if onRemove}
|
|
237
|
+
<button
|
|
238
|
+
type="button"
|
|
239
|
+
class="shrink-0 opacity-0 transition group-hover/chip:opacity-70 hover:opacity-100!"
|
|
240
|
+
onclick={() => onRemove(tag)}
|
|
241
|
+
aria-label={`Remove ${tagLabel(tag)}`}
|
|
242
|
+
title="Remove from this item"
|
|
243
|
+
>✕</button>
|
|
244
|
+
{/if}
|
|
245
|
+
</span>
|
|
246
|
+
{/if}
|
|
247
|
+
{/each}
|
|
248
|
+
|
|
249
|
+
{#if onAdd}
|
|
250
|
+
{#if adding}
|
|
251
|
+
<form class="inline-flex items-center gap-1" onsubmit={submitAdd}>
|
|
252
|
+
<input
|
|
253
|
+
bind:this={addInput}
|
|
254
|
+
bind:value={addType}
|
|
255
|
+
list={typeOptions.length > 0 ? datalistId : undefined}
|
|
256
|
+
placeholder="Type"
|
|
257
|
+
onkeydown={(e) => onFormKey(e, cancelAdd)}
|
|
258
|
+
class="h-6 w-16 rounded border border-slate-700 bg-slate-900/70 px-1.5 text-[11px] text-slate-200 outline-none focus:border-brand-500"
|
|
259
|
+
/>
|
|
260
|
+
<input
|
|
261
|
+
bind:value={addValue}
|
|
262
|
+
placeholder="Value"
|
|
263
|
+
onkeydown={(e) => onFormKey(e, cancelAdd)}
|
|
264
|
+
class="h-6 w-20 rounded border border-slate-700 bg-slate-900/70 px-1.5 text-[11px] text-slate-200 outline-none focus:border-brand-500"
|
|
265
|
+
/>
|
|
266
|
+
<button type="submit" class="text-[11px] font-semibold text-brand-300 hover:text-brand-200" title="Add tag">✓</button>
|
|
267
|
+
<button type="button" class="text-[11px] text-slate-500 hover:text-slate-300" onclick={cancelAdd} title="Cancel">✕</button>
|
|
268
|
+
</form>
|
|
269
|
+
{:else}
|
|
270
|
+
<button
|
|
271
|
+
type="button"
|
|
272
|
+
class="size-5 items-center justify-center rounded-full border border-dashed border-slate-600 text-[11px] leading-none text-slate-400 transition hover:border-brand-500 hover:text-brand-300 hidden group-hover:inline-flex group-focus-within:inline-flex"
|
|
273
|
+
onclick={startAdd}
|
|
274
|
+
aria-label="Add tag"
|
|
275
|
+
title="Add tag"
|
|
276
|
+
>+</button>
|
|
277
|
+
{/if}
|
|
278
|
+
{/if}
|
|
279
|
+
|
|
280
|
+
{#if typeOptions.length > 0}
|
|
281
|
+
<datalist id={datalistId}>
|
|
282
|
+
{#each typeOptions as option (option)}
|
|
283
|
+
<option value={option}></option>
|
|
284
|
+
{/each}
|
|
285
|
+
</datalist>
|
|
286
|
+
{/if}
|
|
287
|
+
</div>
|
|
288
|
+
</div>
|
|
289
|
+
{/if}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let {
|
|
3
|
+
text,
|
|
4
|
+
class: className = ''
|
|
5
|
+
}: {
|
|
6
|
+
readonly text: string;
|
|
7
|
+
readonly class?: string;
|
|
8
|
+
} = $props();
|
|
9
|
+
|
|
10
|
+
let expanded = $state(false);
|
|
11
|
+
let overflowing = $state(false);
|
|
12
|
+
let animating = $state(false);
|
|
13
|
+
let el = $state<HTMLParagraphElement | null>(null);
|
|
14
|
+
|
|
15
|
+
// Open feels expansive (easeOutExpo — fast then a long, soft settle); close
|
|
16
|
+
// is a touch quicker and symmetric (easeInOutCubic). The values are tuned to
|
|
17
|
+
// read as one physical motion rather than a linear height tween.
|
|
18
|
+
const OPEN = '560ms cubic-bezier(0.16, 1, 0.3, 1)';
|
|
19
|
+
const CLOSE = '440ms cubic-bezier(0.65, 0, 0.35, 1)';
|
|
20
|
+
const SOFT_EDGE = 'linear-gradient(to bottom, #000 calc(100% - 1.3em), transparent)';
|
|
21
|
+
|
|
22
|
+
const prefersReducedMotion = (): boolean =>
|
|
23
|
+
typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
24
|
+
|
|
25
|
+
const clampedHeight = (node: HTMLElement): number => {
|
|
26
|
+
const lineHeight = parseFloat(getComputedStyle(node).lineHeight) || 20;
|
|
27
|
+
return lineHeight * 2;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Cancels the in-flight animation's transitionend listener so a mid-flight
|
|
31
|
+
// re-toggle doesn't get its cleanup stomped by the previous run.
|
|
32
|
+
let cancelPending: (() => void) | null = null;
|
|
33
|
+
|
|
34
|
+
// Reveal only carries a toggle when the clamped text actually overflows.
|
|
35
|
+
// While clamped, scrollHeight (full content) exceeds clientHeight (2 lines).
|
|
36
|
+
// Re-measured on text changes and width changes (the columns resize).
|
|
37
|
+
$effect(() => {
|
|
38
|
+
const node = el;
|
|
39
|
+
if (!node) return;
|
|
40
|
+
void text;
|
|
41
|
+
const measure = () => {
|
|
42
|
+
if (expanded || animating) return; // clientHeight === scrollHeight once open
|
|
43
|
+
overflowing = node.scrollHeight > node.clientHeight + 1;
|
|
44
|
+
};
|
|
45
|
+
measure();
|
|
46
|
+
if (typeof ResizeObserver === 'undefined') return;
|
|
47
|
+
const observer = new ResizeObserver(measure);
|
|
48
|
+
observer.observe(node);
|
|
49
|
+
return () => observer.disconnect();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
function toggle(): void {
|
|
53
|
+
const node = el;
|
|
54
|
+
if (!node) return;
|
|
55
|
+
if (expanded) collapse(node);
|
|
56
|
+
else expand(node);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Run `max-height` from `startPx` to `endPx` with `timing`. The caller must
|
|
61
|
+
* already have the clamp in the right state; we pin the start height (no
|
|
62
|
+
* transition), commit it, then transition to the end. Starting from a caller-
|
|
63
|
+
* supplied height — captured BEFORE any clamp change — is what makes both
|
|
64
|
+
* directions animate, and makes mid-flight re-toggles reverse seamlessly.
|
|
65
|
+
*/
|
|
66
|
+
function animateHeight(
|
|
67
|
+
node: HTMLElement,
|
|
68
|
+
startPx: number,
|
|
69
|
+
endPx: number,
|
|
70
|
+
timing: string,
|
|
71
|
+
onDone: () => void
|
|
72
|
+
): void {
|
|
73
|
+
cancelPending?.();
|
|
74
|
+
animating = true;
|
|
75
|
+
node.style.overflow = 'hidden';
|
|
76
|
+
node.style.webkitMaskImage = SOFT_EDGE;
|
|
77
|
+
node.style.maskImage = SOFT_EDGE;
|
|
78
|
+
node.style.transition = 'none';
|
|
79
|
+
node.style.maxHeight = `${startPx}px`;
|
|
80
|
+
void node.offsetHeight; // commit the start frame before transitioning
|
|
81
|
+
node.style.transition = `max-height ${timing}`;
|
|
82
|
+
node.style.maxHeight = `${endPx}px`;
|
|
83
|
+
|
|
84
|
+
const done = (event: TransitionEvent) => {
|
|
85
|
+
if (event.propertyName !== 'max-height') return;
|
|
86
|
+
node.removeEventListener('transitionend', done);
|
|
87
|
+
cancelPending = null;
|
|
88
|
+
animating = false;
|
|
89
|
+
onDone();
|
|
90
|
+
};
|
|
91
|
+
node.addEventListener('transitionend', done);
|
|
92
|
+
cancelPending = () => node.removeEventListener('transitionend', done);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function expand(node: HTMLElement): void {
|
|
96
|
+
// Capture the clamped (2-line) height BEFORE unclamping — otherwise the
|
|
97
|
+
// element is already at full height and start === end (the instant-open bug).
|
|
98
|
+
const startPx = node.clientHeight;
|
|
99
|
+
expanded = true;
|
|
100
|
+
// Drop the line-clamp so content can exceed two lines; the pinned start
|
|
101
|
+
// height keeps it visually at two lines until the transition takes over.
|
|
102
|
+
node.classList.remove('clamp-2');
|
|
103
|
+
if (prefersReducedMotion()) return;
|
|
104
|
+
void node.offsetHeight; // let scrollHeight reflect the now-unclamped content
|
|
105
|
+
animateHeight(node, startPx, node.scrollHeight, OPEN, () => {
|
|
106
|
+
// Fully open: release every constraint so later reflow can't clip it.
|
|
107
|
+
node.style.transition = '';
|
|
108
|
+
node.style.maxHeight = 'none';
|
|
109
|
+
node.style.overflow = '';
|
|
110
|
+
node.style.webkitMaskImage = '';
|
|
111
|
+
node.style.maskImage = '';
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function collapse(node: HTMLElement): void {
|
|
116
|
+
const startPx = node.clientHeight; // current (expanded / mid-animation) height
|
|
117
|
+
expanded = false;
|
|
118
|
+
if (prefersReducedMotion()) {
|
|
119
|
+
node.classList.add('clamp-2');
|
|
120
|
+
node.style.maxHeight = '';
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
animateHeight(node, startPx, clampedHeight(node), CLOSE, () => {
|
|
124
|
+
// Restore the resting clamp (with its ellipsis) and drop inline overrides.
|
|
125
|
+
node.classList.add('clamp-2');
|
|
126
|
+
node.style.transition = '';
|
|
127
|
+
node.style.maxHeight = '';
|
|
128
|
+
node.style.overflow = '';
|
|
129
|
+
node.style.webkitMaskImage = '';
|
|
130
|
+
node.style.maskImage = '';
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
</script>
|
|
134
|
+
|
|
135
|
+
<div class={className}>
|
|
136
|
+
<p bind:this={el} class="clamp-2 text-sm leading-5 text-slate-400">{text}</p>
|
|
137
|
+
{#if overflowing || expanded || animating}
|
|
138
|
+
<button
|
|
139
|
+
type="button"
|
|
140
|
+
class="relative z-20 mt-1 inline-flex items-center gap-1 text-xs font-semibold text-brand-400 transition hover:text-brand-300"
|
|
141
|
+
onclick={toggle}
|
|
142
|
+
aria-expanded={expanded}
|
|
143
|
+
>
|
|
144
|
+
<span>{expanded ? 'View less' : 'View more'}</span>
|
|
145
|
+
<svg
|
|
146
|
+
viewBox="0 0 16 16"
|
|
147
|
+
class="h-3 w-3 transition-transform duration-500 ease-out {expanded ? 'rotate-180' : ''}"
|
|
148
|
+
fill="none"
|
|
149
|
+
stroke="currentColor"
|
|
150
|
+
stroke-width="2"
|
|
151
|
+
aria-hidden="true"
|
|
152
|
+
>
|
|
153
|
+
<path d="M4 6l4 4 4-4" stroke-linecap="round" stroke-linejoin="round" />
|
|
154
|
+
</svg>
|
|
155
|
+
</button>
|
|
156
|
+
{/if}
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
<style>
|
|
160
|
+
/*
|
|
161
|
+
* Resting two-line clamp with ellipsis. Toggled imperatively (not via a
|
|
162
|
+
* reactive class) because the open/close animation needs the clamp gone for
|
|
163
|
+
* the exact frame it swaps to a max-height transition — line-clamp itself
|
|
164
|
+
* can't be animated, and a deferred reactive update would race the freeze
|
|
165
|
+
* frame. Kept in the markup's initial class so the scoped rule isn't pruned.
|
|
166
|
+
*/
|
|
167
|
+
.clamp-2 {
|
|
168
|
+
display: -webkit-box;
|
|
169
|
+
-webkit-box-orient: vertical;
|
|
170
|
+
-webkit-line-clamp: 2;
|
|
171
|
+
line-clamp: 2;
|
|
172
|
+
overflow: hidden;
|
|
173
|
+
}
|
|
174
|
+
</style>
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let {
|
|
3
|
+
label,
|
|
4
|
+
value
|
|
5
|
+
}: {
|
|
6
|
+
readonly label: string;
|
|
7
|
+
readonly value: number | null;
|
|
8
|
+
} = $props();
|
|
9
|
+
|
|
10
|
+
const normalized = $derived(value === null ? 0 : Math.max(0, Math.min(100, value)));
|
|
11
|
+
const displayValue = $derived(`${normalized}%`);
|
|
12
|
+
const dash = $derived(normalized * 0.87965);
|
|
13
|
+
const toneClass = $derived(
|
|
14
|
+
normalized >= 80
|
|
15
|
+
? 'is-high'
|
|
16
|
+
: normalized >= 45
|
|
17
|
+
? 'is-mid'
|
|
18
|
+
: 'is-low'
|
|
19
|
+
);
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<div class="score-meter {toneClass}">
|
|
23
|
+
<div class="score-meter__dial">
|
|
24
|
+
<svg viewBox="0 0 36 36" class="score-meter__svg" aria-hidden="true">
|
|
25
|
+
<circle cx="18" cy="18" r="16" fill="none" class="score-meter__outer" stroke-width="1.2" />
|
|
26
|
+
<circle cx="18" cy="18" r="14" fill="none" class="score-meter__track" stroke-width="2.4" />
|
|
27
|
+
<circle
|
|
28
|
+
cx="18"
|
|
29
|
+
cy="18"
|
|
30
|
+
r="14"
|
|
31
|
+
fill="none"
|
|
32
|
+
class="score-meter__arc-glow"
|
|
33
|
+
stroke-width="8"
|
|
34
|
+
stroke-linecap={normalized > 0 ? 'round' : 'butt'}
|
|
35
|
+
stroke-dasharray={`${dash} 87.965`}
|
|
36
|
+
/>
|
|
37
|
+
<circle
|
|
38
|
+
cx="18"
|
|
39
|
+
cy="18"
|
|
40
|
+
r="14"
|
|
41
|
+
fill="none"
|
|
42
|
+
class="score-meter__arc"
|
|
43
|
+
stroke-width="2.4"
|
|
44
|
+
stroke-linecap={normalized > 0 ? 'round' : 'butt'}
|
|
45
|
+
stroke-dasharray={`${dash} 87.965`}
|
|
46
|
+
/>
|
|
47
|
+
</svg>
|
|
48
|
+
<span class="score-meter__readout">
|
|
49
|
+
{displayValue}
|
|
50
|
+
</span>
|
|
51
|
+
</div>
|
|
52
|
+
<span class="score-meter__label">{label}</span>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<style>
|
|
56
|
+
.score-meter {
|
|
57
|
+
--accent: #94a3b8;
|
|
58
|
+
--accent-soft: rgba(148, 163, 184, 0.24);
|
|
59
|
+
display: flex;
|
|
60
|
+
min-width: 76px;
|
|
61
|
+
flex-direction: column;
|
|
62
|
+
align-items: center;
|
|
63
|
+
gap: 8px;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.score-meter.is-high {
|
|
67
|
+
--accent: #34d399;
|
|
68
|
+
--accent-soft: rgba(52, 211, 153, 0.34);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.score-meter.is-mid {
|
|
72
|
+
--accent: #fb923c;
|
|
73
|
+
--accent-soft: rgba(251, 146, 60, 0.28);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.score-meter.is-low {
|
|
77
|
+
--accent: #f43f5e;
|
|
78
|
+
--accent-soft: rgba(244, 63, 94, 0.24);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.score-meter.is-empty {
|
|
82
|
+
--accent: #94a3b8;
|
|
83
|
+
--accent-soft: rgba(148, 163, 184, 0.2);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.score-meter__dial {
|
|
87
|
+
position: relative;
|
|
88
|
+
width: 68px;
|
|
89
|
+
height: 68px;
|
|
90
|
+
border: 1px solid rgba(15, 23, 42, 0.92);
|
|
91
|
+
border-radius: 999px;
|
|
92
|
+
background:
|
|
93
|
+
radial-gradient(circle at 34% 26%, rgba(255, 255, 255, 0.18), transparent 22%),
|
|
94
|
+
radial-gradient(circle at 50% 66%, rgba(255, 255, 255, 0.08), transparent 43%),
|
|
95
|
+
linear-gradient(180deg, rgba(71, 85, 105, 0.96), rgba(15, 23, 42, 0.98));
|
|
96
|
+
box-shadow:
|
|
97
|
+
0 14px 22px rgba(0, 0, 0, 0.34),
|
|
98
|
+
0 1px 0 rgba(255, 255, 255, 0.18) inset,
|
|
99
|
+
0 -3px 0 rgba(2, 6, 23, 0.9) inset,
|
|
100
|
+
0 0 0 6px rgba(15, 23, 42, 0.66) inset;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.score-meter__dial::before {
|
|
104
|
+
content: "";
|
|
105
|
+
position: absolute;
|
|
106
|
+
inset: 5px;
|
|
107
|
+
border-radius: inherit;
|
|
108
|
+
background:
|
|
109
|
+
linear-gradient(180deg, rgba(255, 255, 255, 0.12), transparent 42%),
|
|
110
|
+
linear-gradient(180deg, rgba(2, 6, 23, 0.2), rgba(2, 6, 23, 0.72));
|
|
111
|
+
box-shadow:
|
|
112
|
+
0 1px 1px rgba(255, 255, 255, 0.08) inset,
|
|
113
|
+
0 10px 18px rgba(0, 0, 0, 0.4) inset;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.score-meter__dial::after {
|
|
117
|
+
content: "";
|
|
118
|
+
position: absolute;
|
|
119
|
+
inset: 6px;
|
|
120
|
+
border-radius: inherit;
|
|
121
|
+
background: radial-gradient(circle, var(--accent-soft), transparent 58%);
|
|
122
|
+
opacity: 0.28;
|
|
123
|
+
filter: blur(7px);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.score-meter__svg {
|
|
127
|
+
position: absolute;
|
|
128
|
+
inset: 6px;
|
|
129
|
+
z-index: 1;
|
|
130
|
+
overflow: visible;
|
|
131
|
+
/* The lit well sits high (top-left light + thick bottom rim), so geometric-center
|
|
132
|
+
content reads as low — lift the arc + readout ~1px into the optical center. */
|
|
133
|
+
transform: translateY(-1px) rotate(-90deg);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.score-meter__outer {
|
|
137
|
+
stroke: rgba(226, 232, 240, 0.16);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.score-meter__track {
|
|
141
|
+
stroke: rgba(30, 41, 59, 0.72);
|
|
142
|
+
filter:
|
|
143
|
+
drop-shadow(0 1px 0 rgba(255, 255, 255, 0.08))
|
|
144
|
+
drop-shadow(0 -1px 0 rgba(0, 0, 0, 0.28))
|
|
145
|
+
drop-shadow(0 0 5px rgba(148, 163, 184, 0.12));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.score-meter.is-empty .score-meter__track {
|
|
149
|
+
stroke: rgba(71, 85, 105, 0.72);
|
|
150
|
+
filter:
|
|
151
|
+
drop-shadow(0 1px 0 rgba(255, 255, 255, 0.08))
|
|
152
|
+
drop-shadow(0 -1px 0 rgba(0, 0, 0, 0.2))
|
|
153
|
+
drop-shadow(0 0 7px rgba(148, 163, 184, 0.16));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.score-meter__arc-glow {
|
|
157
|
+
stroke: var(--accent);
|
|
158
|
+
opacity: 0.16;
|
|
159
|
+
filter: blur(2.8px);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.score-meter__arc {
|
|
163
|
+
stroke: var(--accent);
|
|
164
|
+
opacity: 0.68;
|
|
165
|
+
filter:
|
|
166
|
+
drop-shadow(0 1px 0 rgba(255, 255, 255, 0.07))
|
|
167
|
+
drop-shadow(0 0 7px var(--accent-soft));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.score-meter__readout {
|
|
171
|
+
position: absolute;
|
|
172
|
+
inset: 0;
|
|
173
|
+
z-index: 2;
|
|
174
|
+
display: grid;
|
|
175
|
+
place-items: center;
|
|
176
|
+
/* match the arc's optical lift and kill inherited line-height drift */
|
|
177
|
+
transform: translateY(-1px);
|
|
178
|
+
line-height: 1;
|
|
179
|
+
color: white;
|
|
180
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
|
|
181
|
+
font-size: 13px;
|
|
182
|
+
font-weight: 800;
|
|
183
|
+
font-variant-numeric: tabular-nums;
|
|
184
|
+
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.75);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.score-meter__label {
|
|
188
|
+
color: rgb(148, 163, 184);
|
|
189
|
+
font-size: 11px;
|
|
190
|
+
font-weight: 800;
|
|
191
|
+
letter-spacing: 0;
|
|
192
|
+
text-transform: uppercase;
|
|
193
|
+
}
|
|
194
|
+
</style>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Container } from '$shared/infrastructure/container';
|
|
2
|
+
import type { SyncEvent } from '$lib/client/sync/syncEvents';
|
|
3
|
+
import type { Tag } from '$shared/domain/Tags';
|
|
4
|
+
import type { TagColor } from '$shared/domain/TagPalette';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Driven ports for the builder view (hexagonal architecture). Each interface is
|
|
8
|
+
* one responsibility the view needs from its host; `BuilderHost` composes them.
|
|
9
|
+
*
|
|
10
|
+
* These are pure abstractions — type-only imports, no concrete singletons — so
|
|
11
|
+
* the view's store depends on *these*, never on the app's infrastructure
|
|
12
|
+
* directly (dependency inversion). Concrete implementations ("adapters") live
|
|
13
|
+
* under `infrastructure/adapters`, and the composition root wires them in.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** Hands the view the resolved DI container (repositories + use-cases). */
|
|
17
|
+
export interface BuilderDataGateway {
|
|
18
|
+
getContainer(): Promise<Container>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Tag colors and cross-entity tag rename. */
|
|
22
|
+
export interface TagPaletteGateway {
|
|
23
|
+
refresh(): Promise<void>;
|
|
24
|
+
registerTypes(types: readonly string[]): void;
|
|
25
|
+
colorForTag(tag: Tag): TagColor | null;
|
|
26
|
+
renameTag(from: Tag, to: Tag): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Live "projects/features changed" notifications. */
|
|
30
|
+
export interface SyncGateway {
|
|
31
|
+
subscribe(handler: (event: SyncEvent) => void): () => void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The composed set of host services the builder view depends on. */
|
|
35
|
+
export interface BuilderHost {
|
|
36
|
+
readonly data: BuilderDataGateway;
|
|
37
|
+
readonly tags: TagPaletteGateway;
|
|
38
|
+
readonly sync: SyncGateway;
|
|
39
|
+
}
|