Compare commits
14 Commits
a4245017f8
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| a45893a9bc | |||
| 1f39c4df7a | |||
| 97950adf8b | |||
| b21214d1f1 | |||
| bf81b8e04b | |||
| ad715a9409 | |||
| f09a249caa | |||
| d8a5917bad | |||
| 0958d52fa9 | |||
| bbd367a266 | |||
| cb0e8067b6 | |||
| 2073d936e2 | |||
| 3035351e6f | |||
| 9cf12b482f |
@@ -12,6 +12,17 @@
|
||||
"@tanstack/vue-table": "^8.21.3",
|
||||
"@tauri-apps/api": "^2.11.0",
|
||||
"@tauri-apps/cli": "^2.11.2",
|
||||
"@tauri-apps/plugin-http": "^2.5.9",
|
||||
"@tiptap/extension-code-block-lowlight": "^3.23.6",
|
||||
"@tiptap/extension-image": "^3.23.6",
|
||||
"@tiptap/extension-link": "^3.23.6",
|
||||
"@tiptap/extension-placeholder": "^3.23.6",
|
||||
"@tiptap/extension-table": "^3.23.6",
|
||||
"@tiptap/extension-table-cell": "^3.23.6",
|
||||
"@tiptap/extension-table-header": "^3.23.6",
|
||||
"@tiptap/extension-table-row": "^3.23.6",
|
||||
"@tiptap/starter-kit": "^3.23.6",
|
||||
"@tiptap/vue-3": "^3.23.6",
|
||||
"@vueuse/core": "^14.3.0",
|
||||
"ag-grid-community": "^35.3.0",
|
||||
"ag-grid-vue3": "^35.3.0",
|
||||
@@ -19,10 +30,13 @@
|
||||
"clsx": "^2.1.1",
|
||||
"dexie": "^4.0.4",
|
||||
"dnd-kit-vue": "^0.0.2",
|
||||
"docx": "^9.7.1",
|
||||
"lowlight": "^3.3.0",
|
||||
"lucide-vue-next": "^1.0.0",
|
||||
"mammoth": "^1.12.0",
|
||||
"pinia": "^2.1.7",
|
||||
"reka-ui": "^2.9.8",
|
||||
"shadcn-minimal-tiptap": "github:Aslam97/minimal-tiptap",
|
||||
"shadcn-vue": "^2.7.3",
|
||||
"sharp": "^0.34.5",
|
||||
"tailwind-merge": "^3.6.0",
|
||||
@@ -178,12 +192,16 @@
|
||||
|
||||
"@floating-ui/dom": ["@floating-ui/dom@1.7.6", "", { "dependencies": { "@floating-ui/core": "^1.7.5", "@floating-ui/utils": "^0.2.11" } }, "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ=="],
|
||||
|
||||
"@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.8", "", { "dependencies": { "@floating-ui/dom": "^1.7.6" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A=="],
|
||||
|
||||
"@floating-ui/utils": ["@floating-ui/utils@0.2.11", "", {}, "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg=="],
|
||||
|
||||
"@floating-ui/vue": ["@floating-ui/vue@1.1.11", "", { "dependencies": { "@floating-ui/dom": "^1.7.6", "@floating-ui/utils": "^0.2.11", "vue-demi": ">=0.13.0" } }, "sha512-HzHKCNVxnGS35r9fCHBc3+uCnjw9IWIlCPL683cGgM9Kgj2BiAl8x1mS7vtvP6F9S/e/q4O6MApwSHj8hNLGfw=="],
|
||||
|
||||
"@hono/node-server": ["@hono/node-server@1.19.14", "", { "peerDependencies": { "hono": "^4" } }, "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw=="],
|
||||
|
||||
"@hookform/resolvers": ["@hookform/resolvers@3.10.0", "", { "peerDependencies": { "react-hook-form": "^7.0.0" } }, "sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag=="],
|
||||
|
||||
"@humanfs/core": ["@humanfs/core@0.19.2", "", { "dependencies": { "@humanfs/types": "^0.15.0" } }, "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA=="],
|
||||
|
||||
"@humanfs/node": ["@humanfs/node@0.16.8", "", { "dependencies": { "@humanfs/core": "^0.19.2", "@humanfs/types": "^0.15.0", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ=="],
|
||||
@@ -286,6 +304,82 @@
|
||||
|
||||
"@preact/signals-core": ["@preact/signals-core@1.14.2", "", {}, "sha512-RZHdBj9ZF4n40Rp4jS052EHHjBWf96P9oNdXPfhQTovCuWY9iQn3Gq+gOTJSgBO9A/JBuPfMOWsSX/lIU9Pc/A=="],
|
||||
|
||||
"@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="],
|
||||
|
||||
"@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="],
|
||||
|
||||
"@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="],
|
||||
|
||||
"@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
|
||||
|
||||
"@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="],
|
||||
|
||||
"@radix-ui/react-context-menu": ["@radix-ui/react-context-menu@2.2.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww=="],
|
||||
|
||||
"@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw=="],
|
||||
|
||||
"@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="],
|
||||
|
||||
"@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="],
|
||||
|
||||
"@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw=="],
|
||||
|
||||
"@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="],
|
||||
|
||||
"@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="],
|
||||
|
||||
"@radix-ui/react-icons": ["@radix-ui/react-icons@1.3.2", "", { "peerDependencies": { "react": "^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc" } }, "sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g=="],
|
||||
|
||||
"@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
|
||||
|
||||
"@radix-ui/react-label": ["@radix-ui/react-label@2.1.8", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A=="],
|
||||
|
||||
"@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg=="],
|
||||
|
||||
"@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA=="],
|
||||
|
||||
"@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.8", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw=="],
|
||||
|
||||
"@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="],
|
||||
|
||||
"@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="],
|
||||
|
||||
"@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
|
||||
|
||||
"@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="],
|
||||
|
||||
"@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.8", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g=="],
|
||||
|
||||
"@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="],
|
||||
|
||||
"@radix-ui/react-switch": ["@radix-ui/react-switch@1.2.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ=="],
|
||||
|
||||
"@radix-ui/react-toggle": ["@radix-ui/react-toggle@1.1.10", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ=="],
|
||||
|
||||
"@radix-ui/react-toggle-group": ["@radix-ui/react-toggle-group@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-toggle": "1.1.10", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q=="],
|
||||
|
||||
"@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.2.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg=="],
|
||||
|
||||
"@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="],
|
||||
|
||||
"@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
|
||||
|
||||
"@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="],
|
||||
|
||||
"@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="],
|
||||
|
||||
"@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
|
||||
|
||||
"@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="],
|
||||
|
||||
"@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.1", "", { "dependencies": { "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w=="],
|
||||
|
||||
"@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="],
|
||||
|
||||
"@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="],
|
||||
|
||||
"@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="],
|
||||
|
||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.60.4", "", { "os": "android", "cpu": "arm" }, "sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ=="],
|
||||
|
||||
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.60.4", "", { "os": "android", "cpu": "arm64" }, "sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw=="],
|
||||
@@ -406,18 +500,114 @@
|
||||
|
||||
"@tauri-apps/cli-win32-x64-msvc": ["@tauri-apps/cli-win32-x64-msvc@2.11.2", "", { "os": "win32", "cpu": "x64" }, "sha512-d2JchlFIpZevZVReyqhQOekJmb1UH3rhZ5VX6sH3ty9ETE0TKQavpihvoScUXfKKpW6HZC0MrFGRU0ZtD+w3gA=="],
|
||||
|
||||
"@tauri-apps/plugin-http": ["@tauri-apps/plugin-http@2.5.9", "", { "dependencies": { "@tauri-apps/api": "^2.11.0" } }, "sha512-lCiY0+vs4HvIUSvZrBs8TC3TiCB0MOPRmiUjTq4prW7SlcJE2jdLeT6KBsJrT9Tlplufl7W1pY6SFAO3gCWxDA=="],
|
||||
|
||||
"@tiptap/core": ["@tiptap/core@3.23.6", "", { "peerDependencies": { "@tiptap/pm": "3.23.6" } }, "sha512-MRB3pHz4Oxqmcawh0cQ5iOGdY5xtNYp/1CoK7hdTLzw5K0C6/gTC2VvanB1R4INaB6EpBkxG/GiWkVirDRnuXw=="],
|
||||
|
||||
"@tiptap/extension-blockquote": ["@tiptap/extension-blockquote@3.23.6", "", { "peerDependencies": { "@tiptap/core": "3.23.6" } }, "sha512-2RmnqNqTltZ2k1F7IfjoDNs935Uq4rRDR7d98mqkg3OlDktcQIyBpv0t9dTay6H5bkQeZUuS8ogK2S1E8Edjug=="],
|
||||
|
||||
"@tiptap/extension-bold": ["@tiptap/extension-bold@3.23.6", "", { "peerDependencies": { "@tiptap/core": "3.23.6" } }, "sha512-1LMhjnytdbbhWHSoOwnLxZAOQZWPkKyXVCNmaIk0Mhi4tLPUXptG4qKS5sVYTCveE5H6IBPFrbgBFi5dMI6krA=="],
|
||||
|
||||
"@tiptap/extension-bubble-menu": ["@tiptap/extension-bubble-menu@3.23.6", "", { "dependencies": { "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "@tiptap/core": "3.23.6", "@tiptap/pm": "3.23.6" } }, "sha512-Mwkyp9LkDHFbqmWRIkp63FinRxFu3ajC4qSb9t4mnHsb4kAdbNLLsGtbFg+le0SWk4CxGwAOwM7SzeJ+6UGqCA=="],
|
||||
|
||||
"@tiptap/extension-bullet-list": ["@tiptap/extension-bullet-list@3.23.6", "", { "peerDependencies": { "@tiptap/extension-list": "3.23.6" } }, "sha512-RMRgfXZykr/13X8UBOwvpgysVOo9KchwqMoEbvqQSj4YFfU56iIn59C8sbxiQ1sKfeltUf0wH4fPc0I4iwKqAA=="],
|
||||
|
||||
"@tiptap/extension-code": ["@tiptap/extension-code@3.23.6", "", { "peerDependencies": { "@tiptap/core": "3.23.6" } }, "sha512-KG8KXFYyLrtYvT7AZ1WGV61ofx8pDe5g9pH658MERxqQGii+Pyfc6xkz04l7XeBts/7+571UQp/0O7i/z560TA=="],
|
||||
|
||||
"@tiptap/extension-code-block": ["@tiptap/extension-code-block@3.23.6", "", { "peerDependencies": { "@tiptap/core": "3.23.6", "@tiptap/pm": "3.23.6" } }, "sha512-4kccgcn5yHThxrzsIhJny3EwfEZYIk+BjUCL4uIuzOyWvExtGhZ6JMHVCZeMhI8D1/bX1LNkkAKN5DXPzH4lXQ=="],
|
||||
|
||||
"@tiptap/extension-code-block-lowlight": ["@tiptap/extension-code-block-lowlight@3.23.6", "", { "peerDependencies": { "@tiptap/core": "3.23.6", "@tiptap/extension-code-block": "3.23.6", "@tiptap/pm": "3.23.6", "highlight.js": "^11", "lowlight": "^2 || ^3" } }, "sha512-GuB7qDfWKp02FoUFB5SwjkczayE5JMlbgNRHr5H1fMN7j9ofsV4SijvbZpocj5X6kSjNyOnx5nhdOyPwlsvOgw=="],
|
||||
|
||||
"@tiptap/extension-color": ["@tiptap/extension-color@3.23.6", "", { "peerDependencies": { "@tiptap/extension-text-style": "3.23.6" } }, "sha512-2WxYc+X+OGFXbq/Ak0VXODEzfK1l5wz33oTWSuhdSw44HAQM7+SSyiDnMCXDQdqi063ANqMmQmlw5WngjQ6wCA=="],
|
||||
|
||||
"@tiptap/extension-document": ["@tiptap/extension-document@3.23.6", "", { "peerDependencies": { "@tiptap/core": "3.23.6" } }, "sha512-XDAIgG9KcKumFM9KJWUEUhXPbFIhhl47bfy5GknareWTRKke85rcoj/oxKKO9ihLZr8JfpbXjqnS4SCm5yhYPw=="],
|
||||
|
||||
"@tiptap/extension-dropcursor": ["@tiptap/extension-dropcursor@3.23.6", "", { "peerDependencies": { "@tiptap/extensions": "3.23.6" } }, "sha512-+XWEoRKf3lXxi7Le1aOM2xU1XHwxICGpXjT3m4QaYqUgIpsq8gQEuso6kVg8DnTD7biKQs6+oIQ0o2b/gTW9WA=="],
|
||||
|
||||
"@tiptap/extension-floating-menu": ["@tiptap/extension-floating-menu@3.23.6", "", { "peerDependencies": { "@floating-ui/dom": "^1.0.0", "@tiptap/core": "3.23.6", "@tiptap/pm": "3.23.6" } }, "sha512-2kjuDcEq69lEcECl75xqY5MyzUSh2zcC5aLrpwP1WwhJz5bxsIFHiaps5AP6h9R4A+ZBj5b2haay2Y1wDUU3VA=="],
|
||||
|
||||
"@tiptap/extension-gapcursor": ["@tiptap/extension-gapcursor@3.23.6", "", { "peerDependencies": { "@tiptap/extensions": "3.23.6" } }, "sha512-wbKmxXsszxWacEkrHucRpSQbiKjz4fmOebD6OVyL9AcrmlbxNk8vcM3iyh/8cVeRy09XY+morM165t/u7/z4IQ=="],
|
||||
|
||||
"@tiptap/extension-hard-break": ["@tiptap/extension-hard-break@3.23.6", "", { "peerDependencies": { "@tiptap/core": "3.23.6" } }, "sha512-KeUm+tkUfIVSX9QM9XOIhaay0Fn36sLKUo5NVYjN3uJaxFvaZXZmTlxdO85OTdgF2P5sqh9LomrIgliaFRGk4w=="],
|
||||
|
||||
"@tiptap/extension-heading": ["@tiptap/extension-heading@3.23.6", "", { "peerDependencies": { "@tiptap/core": "3.23.6" } }, "sha512-A/0jPhxnUh9THSZymlu0OGPZe1wdFdwHAXnRCmqvYUCwJjrG7LCC/ahzmcj1tcNzI9hgHyuYPSfev8RXYrNu/w=="],
|
||||
|
||||
"@tiptap/extension-horizontal-rule": ["@tiptap/extension-horizontal-rule@3.23.6", "", { "peerDependencies": { "@tiptap/core": "3.23.6", "@tiptap/pm": "3.23.6" } }, "sha512-hEUlz4H+I64r+TH6LCuNCRgO7JTHncXGmx9+WbU69EOfY8O0ZurcgeJc8HeiAKL+r9YuC1e5YHfFxgCaaC0jlg=="],
|
||||
|
||||
"@tiptap/extension-image": ["@tiptap/extension-image@3.23.6", "", { "peerDependencies": { "@tiptap/core": "3.23.6" } }, "sha512-vvNGxArvD2dW+XvV0KdYovRVUzCy8QVNulc2r5pV7umnG1E6cCmMkiHiif8J2ePJu2KtysAvJQe0iF+UqueGMw=="],
|
||||
|
||||
"@tiptap/extension-italic": ["@tiptap/extension-italic@3.23.6", "", { "peerDependencies": { "@tiptap/core": "3.23.6" } }, "sha512-wol5KdwCPAvpiYhH9PLlvO8ZnJHwZtIboVevrfOGgBcKlXRA3dedR4OAMXHnUtkkzu9KtliLg1+TYzEx4JZG9Q=="],
|
||||
|
||||
"@tiptap/extension-link": ["@tiptap/extension-link@3.23.6", "", { "dependencies": { "linkifyjs": "^4.3.3" }, "peerDependencies": { "@tiptap/core": "3.23.6", "@tiptap/pm": "3.23.6" } }, "sha512-KNZz7z7P2/qbQsx5bPAbSPjrKDg1VHsedGlLHJCr8U2VRD5VgmDLkMpkouP1CsDg15qgyUKv/nDib5KgPpLNWA=="],
|
||||
|
||||
"@tiptap/extension-list": ["@tiptap/extension-list@3.23.6", "", { "peerDependencies": { "@tiptap/core": "3.23.6", "@tiptap/pm": "3.23.6" } }, "sha512-z6vj9+Qht2sjdQkyyHcUpsC/yCIZqTrQiyHDhs/HGKrfvoANyAZGpqdNeKf1wSyjIso+27tQuIH5NDfk8ygyNw=="],
|
||||
|
||||
"@tiptap/extension-list-item": ["@tiptap/extension-list-item@3.23.6", "", { "peerDependencies": { "@tiptap/extension-list": "3.23.6" } }, "sha512-3zzyhdkUWcHVpXuvy6KiIwjh29rbH6gEDEqPQqHLrl1XGnO9pnShC7pSHctlCDjmcx3O4n9cd4QMtVBlUerbiA=="],
|
||||
|
||||
"@tiptap/extension-list-keymap": ["@tiptap/extension-list-keymap@3.23.6", "", { "peerDependencies": { "@tiptap/extension-list": "3.23.6" } }, "sha512-x8bPcLViGzg/RAmQM/XtmfqIwQ/Pv9Q8mkd+OgfUiTqjeJqKwVQmiqbLFNa7zw81+H61M+HDU+qGAaQ3vRIMjw=="],
|
||||
|
||||
"@tiptap/extension-ordered-list": ["@tiptap/extension-ordered-list@3.23.6", "", { "peerDependencies": { "@tiptap/extension-list": "3.23.6" } }, "sha512-1m/wWB/ZtXcmG2vNdiUkCqsOgqv5vBjCv/mVaHhF9OvV+zQS8YDjoWE7zEuT/GgELdT77Xq8lHrn4nCDudB3/A=="],
|
||||
|
||||
"@tiptap/extension-paragraph": ["@tiptap/extension-paragraph@3.23.6", "", { "peerDependencies": { "@tiptap/core": "3.23.6" } }, "sha512-+7m58LUSncodjrIyXks4RZ3tLNYrvgT77wRR4l3HnM5OABY3GDsDTqi7c1t1yI29NVOSk/DUacqy6UwYAj1DGg=="],
|
||||
|
||||
"@tiptap/extension-placeholder": ["@tiptap/extension-placeholder@3.23.6", "", { "peerDependencies": { "@tiptap/extensions": "3.23.6" } }, "sha512-8I6b2aevF74aLgymKMxbDxSLxWA2y+2dh0zZDeI8sRZ2m6WHHes+Kyuuwkq1HIPcR+ZLpbec74cmf6lcL/yvqQ=="],
|
||||
|
||||
"@tiptap/extension-strike": ["@tiptap/extension-strike@3.23.6", "", { "peerDependencies": { "@tiptap/core": "3.23.6" } }, "sha512-oF7FEZ37f15aCe5kPgzGDYf/m+hr7VdQ/Ko/Hds/UM9pX7AG1fdtmRrl6wqkRqDM/incZaC/AQR2/Dpo2VCNGQ=="],
|
||||
|
||||
"@tiptap/extension-table": ["@tiptap/extension-table@3.23.6", "", { "peerDependencies": { "@tiptap/core": "3.23.6", "@tiptap/pm": "3.23.6" } }, "sha512-XbhZXjhsS6AP7ThoZxjAnNs+NiR81YRori25l6E+ORqB7quiPkIXOAi5h4AIpkn/CYIqze6ere11lWsYpDjtaQ=="],
|
||||
|
||||
"@tiptap/extension-table-cell": ["@tiptap/extension-table-cell@3.23.6", "", { "peerDependencies": { "@tiptap/extension-table": "3.23.6" } }, "sha512-hS9TmmvRlT9/ikT+0ukACS+hmJuii4zQaH47cg3oJkz/Fv7O7tL7GZniKtK6l2OUZGPhY+4SV2RkDB6bD7DXfw=="],
|
||||
|
||||
"@tiptap/extension-table-header": ["@tiptap/extension-table-header@3.23.6", "", { "peerDependencies": { "@tiptap/extension-table": "3.23.6" } }, "sha512-D6o0a1cJXUU0xWakainBFGPnGHinQkPcdu1YqGd/PoFANY38lnuZt/NW2O/OLfLXu5LXDRfpqF1+dsKww27dUA=="],
|
||||
|
||||
"@tiptap/extension-table-row": ["@tiptap/extension-table-row@3.23.6", "", { "peerDependencies": { "@tiptap/extension-table": "3.23.6" } }, "sha512-OauWVzkyRQg0rKOqM/a3PuKPc1S7YXMb1LRN7Nh8Ytvglvd7GFRTbl1lVqdZRaz4Jzopag4PQnriIZfMPUpxWw=="],
|
||||
|
||||
"@tiptap/extension-text": ["@tiptap/extension-text@3.23.6", "", { "peerDependencies": { "@tiptap/core": "3.23.6" } }, "sha512-ipoC2TkIAIOTiF5ByiGgvQB1DqDyfP90wrUB3mohBcgvp7lQnwHszCDGv8dNnmcUek8uXV/uoLu2VXeVQlxjPA=="],
|
||||
|
||||
"@tiptap/extension-text-style": ["@tiptap/extension-text-style@3.23.6", "", { "peerDependencies": { "@tiptap/core": "3.23.6" } }, "sha512-Auasgj469wkQ5ip+Zi2gaRzvqxx9qKG58+1mkT8yPE3QAGnOIg/AaKyQ7pTV77UyL3FHvLnU+KsWCad+qcobww=="],
|
||||
|
||||
"@tiptap/extension-typography": ["@tiptap/extension-typography@3.23.6", "", { "peerDependencies": { "@tiptap/core": "3.23.6" } }, "sha512-eo/+e5TJ4cPfkk6ugMaoW+UVfTC3CEU97EH2g72H+L90qywnOJalrRNXTkdSEbYxGtLs9xthTAHpy2YImf4r1A=="],
|
||||
|
||||
"@tiptap/extension-underline": ["@tiptap/extension-underline@3.23.6", "", { "peerDependencies": { "@tiptap/core": "3.23.6" } }, "sha512-P55wGIZGYTVH92Fq0cgI4/O9AhLCaJC3hhxg15RSERP5/YegM9eJHDK/GQ1EE/DvYA+xpYGOV6agKwAUqfA/Iw=="],
|
||||
|
||||
"@tiptap/extensions": ["@tiptap/extensions@3.23.6", "", { "peerDependencies": { "@tiptap/core": "3.23.6", "@tiptap/pm": "3.23.6" } }, "sha512-X09/Db1teB+ifXzDGVVFmOeQRx7wTAayE9/280spxpsHkHZvJ5bHRvWIzUzviMIjbBz+NPDIKYPK7gMfh9iaig=="],
|
||||
|
||||
"@tiptap/markdown": ["@tiptap/markdown@3.23.6", "", { "dependencies": { "marked": "^17.0.1" }, "peerDependencies": { "@tiptap/core": "3.23.6", "@tiptap/pm": "3.23.6" } }, "sha512-11RS+WswVAtAJMn1CaXN1YblMdLn69I6TeFVoHwJXbprV8M45tjyID9uV0WoGYPnpjhfepPhaG7kivgAu5XukQ=="],
|
||||
|
||||
"@tiptap/pm": ["@tiptap/pm@3.23.6", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-keymap": "^1.2.2", "prosemirror-model": "^1.24.1", "prosemirror-schema-list": "^1.5.0", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.38.1" } }, "sha512-in5CaMaWlJcH2A1q6GJKFtrodE8WLS3M9tIi/f89jPmIVHJShpodC0KZDNyJkrVBQomYk0DEh86Utm6ASXzQww=="],
|
||||
|
||||
"@tiptap/react": ["@tiptap/react@3.23.6", "", { "dependencies": { "@types/use-sync-external-store": "^0.0.6", "fast-equals": "^5.3.3", "use-sync-external-store": "^1.4.0" }, "optionalDependencies": { "@tiptap/extension-bubble-menu": "^3.23.6", "@tiptap/extension-floating-menu": "^3.23.6" }, "peerDependencies": { "@tiptap/core": "3.23.6", "@tiptap/pm": "3.23.6", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "@types/react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Tw9KZkYqFMk3vaJAEQKqEYIO/iq3cSJe7OUEGBul4k4GaMQeLItLf5EYhUd0GIPXci1WVVPNntKJsHfX25M37w=="],
|
||||
|
||||
"@tiptap/starter-kit": ["@tiptap/starter-kit@3.23.6", "", { "dependencies": { "@tiptap/core": "^3.23.6", "@tiptap/extension-blockquote": "^3.23.6", "@tiptap/extension-bold": "^3.23.6", "@tiptap/extension-bullet-list": "^3.23.6", "@tiptap/extension-code": "^3.23.6", "@tiptap/extension-code-block": "^3.23.6", "@tiptap/extension-document": "^3.23.6", "@tiptap/extension-dropcursor": "^3.23.6", "@tiptap/extension-gapcursor": "^3.23.6", "@tiptap/extension-hard-break": "^3.23.6", "@tiptap/extension-heading": "^3.23.6", "@tiptap/extension-horizontal-rule": "^3.23.6", "@tiptap/extension-italic": "^3.23.6", "@tiptap/extension-link": "^3.23.6", "@tiptap/extension-list": "^3.23.6", "@tiptap/extension-list-item": "^3.23.6", "@tiptap/extension-list-keymap": "^3.23.6", "@tiptap/extension-ordered-list": "^3.23.6", "@tiptap/extension-paragraph": "^3.23.6", "@tiptap/extension-strike": "^3.23.6", "@tiptap/extension-text": "^3.23.6", "@tiptap/extension-underline": "^3.23.6", "@tiptap/extensions": "^3.23.6", "@tiptap/pm": "^3.23.6" } }, "sha512-gykwtGWrnWCmtql1hid3opac/KV8zQvOAnu3bTqIqcHrn1FusbUwKmNzavSbfGvcktHM3hFjb35W48JyVLyu/A=="],
|
||||
|
||||
"@tiptap/vue-3": ["@tiptap/vue-3@3.23.6", "", { "optionalDependencies": { "@tiptap/extension-bubble-menu": "^3.23.6", "@tiptap/extension-floating-menu": "^3.23.6" }, "peerDependencies": { "@floating-ui/dom": "^1.0.0", "@tiptap/core": "3.23.6", "@tiptap/pm": "3.23.6", "vue": "^3.0.0" } }, "sha512-MQ9uX1PGKqOrDpdq5O72zJIME7BA8AatcEDUmiJvEC8MH7cF2hpKBVt9TeBE+zTr1jrEuGG8wz76gaJsiykwhQ=="],
|
||||
|
||||
"@ts-morph/common": ["@ts-morph/common@0.28.1", "", { "dependencies": { "minimatch": "^10.0.1", "path-browserify": "^1.0.1", "tinyglobby": "^0.2.14" } }, "sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g=="],
|
||||
|
||||
"@types/esrecurse": ["@types/esrecurse@4.3.1", "", {}, "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw=="],
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||
|
||||
"@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="],
|
||||
|
||||
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
||||
|
||||
"@types/node": ["@types/node@25.9.1", "", { "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } }, "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg=="],
|
||||
|
||||
"@types/react": ["@types/react@19.2.15", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q=="],
|
||||
|
||||
"@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
|
||||
|
||||
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
|
||||
|
||||
"@types/use-sync-external-store": ["@types/use-sync-external-store@0.0.6", "", {}, "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg=="],
|
||||
|
||||
"@types/web-bluetooth": ["@types/web-bluetooth@0.0.21", "", {}, "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA=="],
|
||||
|
||||
"@unovue/detypes": ["@unovue/detypes@0.8.5", "", { "dependencies": { "@babel/core": "^7.24.5", "@babel/preset-typescript": "^7.24.1", "@vue/compiler-dom": "^3.4.27", "@vue/compiler-sfc": "^3.4.27", "@vuedx/template-ast-types": "0.7.1", "fast-glob": "^3.3.2", "prettier": "^3.2.5", "typescript": "^5.4.5" }, "bin": { "detypes": "detype.js" } }, "sha512-Yz4JeWOHGa+w/3YudVdng8hgN/VGW9cvp8xmFkmPPFzalGblLPPSpIRiwVo853yLstMZO2LLwe0vOoLAQsUQXw=="],
|
||||
|
||||
"@vercel/analytics": ["@vercel/analytics@1.6.1", "", { "peerDependencies": { "@remix-run/react": "^2", "@sveltejs/kit": "^1 || ^2", "next": ">= 13", "react": "^18 || ^19 || ^19.0.0-rc", "svelte": ">= 4", "vue": "^3", "vue-router": "^4" }, "optionalPeers": ["@remix-run/react", "@sveltejs/kit", "next", "react", "svelte", "vue", "vue-router"] }, "sha512-oH9He/bEM+6oKlv3chWuOOcp8Y6fo6/PSro8hEkgCW3pu9/OiCXiUpRUogDh3Fs3LH2sosDrx8CxeOLBEE+afg=="],
|
||||
|
||||
"@vitejs/plugin-vue": ["@vitejs/plugin-vue@5.2.4", "", { "peerDependencies": { "vite": "^5.0.0 || ^6.0.0", "vue": "^3.2.25" } }, "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA=="],
|
||||
|
||||
"@volar/language-core": ["@volar/language-core@2.4.15", "", { "dependencies": { "@volar/source-map": "2.4.15" } }, "sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA=="],
|
||||
@@ -616,10 +806,16 @@
|
||||
|
||||
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
|
||||
|
||||
"dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
|
||||
|
||||
"destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="],
|
||||
|
||||
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
||||
|
||||
"detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="],
|
||||
|
||||
"devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
|
||||
|
||||
"dexie": ["dexie@4.4.2", "", {}, "sha512-zMtV8q79EFE5U8FKZvt0Y/77PCU/Hr/RDxv1EDeo228L+m/HTbeN2AjoQm674rhQCX8n3ljK87lajt7UQuZfvw=="],
|
||||
|
||||
"diff": ["diff@8.0.4", "", {}, "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw=="],
|
||||
@@ -628,6 +824,8 @@
|
||||
|
||||
"dnd-kit-vue": ["dnd-kit-vue@0.0.2", "", { "dependencies": { "@dnd-kit/abstract": "^0.1.19", "@dnd-kit/dom": "^0.1.19", "@dnd-kit/state": "^0.1.19", "@vueuse/core": "^13.4.0", "reka-ui": "^2.3.1" }, "peerDependencies": { "vue": "^3.3.0" } }, "sha512-2ZQfqTulZI7vqFiYscV7VMQRXSEryjanlaCY5BvkDf5i+whEAvOKSckyBa6SK8LCPaF5f/IIcUhfh6TnbaWq3A=="],
|
||||
|
||||
"docx": ["docx@9.7.1", "", { "dependencies": { "@types/node": "^25.2.3", "hash.js": "^1.1.7", "jszip": "^3.10.1", "nanoid": "^5.1.3", "xml": "^1.0.1", "xml-js": "^1.6.8" } }, "sha512-ilXFf9Moz47ABjFpDiA5s1w9lpb4EFSp7+5iiJSbfyYDM+bpZdAgLlSr7fW4aXhVe/E+F6QCv0EvRVFEd5CsWg=="],
|
||||
|
||||
"dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
|
||||
|
||||
"domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
|
||||
@@ -708,6 +906,8 @@
|
||||
|
||||
"fast-diff": ["fast-diff@1.3.0", "", {}, "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="],
|
||||
|
||||
"fast-equals": ["fast-equals@5.4.0", "", {}, "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw=="],
|
||||
|
||||
"fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
|
||||
|
||||
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
|
||||
@@ -758,6 +958,8 @@
|
||||
|
||||
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
||||
|
||||
"get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
|
||||
|
||||
"get-own-enumerable-keys": ["get-own-enumerable-keys@1.0.0", "", {}, "sha512-PKsK2FSrQCyxcGHsGrLDcK0lx+0Ke+6e8KFFozA9/fIQLhQzPaRvJFdcz7+Axg3jUH/Mq+NI4xa5u/UT2tQskA=="],
|
||||
|
||||
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
||||
@@ -768,7 +970,7 @@
|
||||
|
||||
"giget": ["giget@3.2.0", "", { "bin": { "giget": "dist/cli.mjs" } }, "sha512-GvHTWcykIR/fP8cj8dMpuMMkvaeJfPvYnhq0oW+chSeIr+ldX21ifU2Ms6KBoyKZQZmVaUAAhQ2EZ68KJF8a7A=="],
|
||||
|
||||
"glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
|
||||
"glob": ["glob@11.1.0", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw=="],
|
||||
|
||||
"glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||
|
||||
@@ -780,10 +982,14 @@
|
||||
|
||||
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
||||
|
||||
"hash.js": ["hash.js@1.1.7", "", { "dependencies": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" } }, "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA=="],
|
||||
|
||||
"hasown": ["hasown@2.0.3", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg=="],
|
||||
|
||||
"he": ["he@1.2.0", "", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="],
|
||||
|
||||
"highlight.js": ["highlight.js@11.11.1", "", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="],
|
||||
|
||||
"hono": ["hono@4.12.22", "", {}, "sha512-7fvVPbB92zNRsQke+uiRGwtTuef0tB2Dg4hWxYfFNvkQhIltWoyi0ONReM5LWA+jJWS3nfT5lTq+qbsIpX0IQw=="],
|
||||
|
||||
"http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
|
||||
@@ -896,6 +1102,8 @@
|
||||
|
||||
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="],
|
||||
|
||||
"linkifyjs": ["linkifyjs@4.3.3", "", {}, "sha512-P8aEP5U/D1/IlTY2OeYsErdwh9bGuLE30NcXtKEjgdHcahveQoQwM2yZNsioQHsWFz0P7KKudisbrzCgR0sDHg=="],
|
||||
|
||||
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
|
||||
|
||||
"lodash-es": ["lodash-es@4.18.1", "", {}, "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A=="],
|
||||
@@ -908,7 +1116,11 @@
|
||||
|
||||
"lop": ["lop@0.4.2", "", { "dependencies": { "duck": "^0.1.12", "option": "~0.2.1", "underscore": "^1.13.1" } }, "sha512-RefILVDQ4DKoRZsJ4Pj22TxE3omDO47yFpkIBoDKzkqPRISs5U1cnAdg/5583YPkWPaLIYHOKRMQSvjFsO26cw=="],
|
||||
|
||||
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||
"lowlight": ["lowlight@3.3.0", "", { "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.0.0", "highlight.js": "~11.11.0" } }, "sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ=="],
|
||||
|
||||
"lru-cache": ["lru-cache@11.5.0", "", {}, "sha512-5YgH9UJd7wVb9hIouI2adWpgqrrICkt070Dnj8EUY1+B4B2P9eRLPAkAAo6NICA7CEhOIeBHl46u9zSNpNu7zA=="],
|
||||
|
||||
"lucide-react": ["lucide-react@0.486.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-xWop/wMsC1ikiEVLZrxXjPKw4vU/eAip33G2mZHgbWnr4Nr5Rt4Vx4s/q1D3B/rQVbxjOuqASkEZcUxDEKzecw=="],
|
||||
|
||||
"lucide-vue-next": ["lucide-vue-next@1.0.0", "", { "peerDependencies": { "vue": ">=3.0.1" } }, "sha512-V6SPvx1IHTj/UY+FrIYWV5faISsPSb8BnWSFDxAtezWKvWc9ZZ40PDrdu1/Qb5vg4lHWr1hs1BAMGVGm6V1Xdg=="],
|
||||
|
||||
@@ -918,6 +1130,8 @@
|
||||
|
||||
"mammoth": ["mammoth@1.12.0", "", { "dependencies": { "@xmldom/xmldom": "^0.8.6", "argparse": "~1.0.3", "base64-js": "^1.5.1", "bluebird": "~3.4.0", "dingbat-to-unicode": "^1.0.1", "jszip": "^3.7.1", "lop": "^0.4.2", "path-is-absolute": "^1.0.0", "underscore": "^1.13.1", "xmlbuilder": "^10.0.0" }, "bin": { "mammoth": "bin/mammoth" } }, "sha512-cwnK1RIcRdDMi2HRx2EXGYlxqIEh0Oo3bLhorgnsVJi2UkbX1+jKxuBNR9PC5+JaX7EkmJxFPmo6mjLpqShI2w=="],
|
||||
|
||||
"marked": ["marked@17.0.6", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA=="],
|
||||
|
||||
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
||||
|
||||
"media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
|
||||
@@ -938,6 +1152,8 @@
|
||||
|
||||
"mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="],
|
||||
|
||||
"minimalistic-assert": ["minimalistic-assert@1.0.1", "", {}, "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="],
|
||||
|
||||
"minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="],
|
||||
|
||||
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
||||
@@ -948,12 +1164,14 @@
|
||||
|
||||
"muggle-string": ["muggle-string@0.4.1", "", {}, "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ=="],
|
||||
|
||||
"nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="],
|
||||
"nanoid": ["nanoid@5.1.11", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-v+KEsUv2ps74PaSKv0gHTxTCgMXOIfBEbaqa6w6ISIGC7ZsvHN4N9oJ8d4cmf0n5oTzQz2SLmThbQWhjd/8eKg=="],
|
||||
|
||||
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
|
||||
|
||||
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
||||
|
||||
"next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="],
|
||||
|
||||
"node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="],
|
||||
|
||||
"node-html-parser": ["node-html-parser@7.1.0", "", { "dependencies": { "css-select": "^5.1.0", "he": "1.2.0" } }, "sha512-iJo8b2uYGT40Y8BTyy5ufL6IVbN8rbm/1QK2xffXU/1a/v3AAa0d1YAoqBNYqaS4R/HajkWIpIfdE6KcyFh1AQ=="],
|
||||
@@ -990,6 +1208,8 @@
|
||||
|
||||
"ora": ["ora@9.4.0", "", { "dependencies": { "chalk": "^5.6.2", "cli-cursor": "^5.0.0", "cli-spinners": "^3.2.0", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.1.0", "log-symbols": "^7.0.1", "stdin-discarder": "^0.3.2", "string-width": "^8.1.0" } }, "sha512-84cglkRILFxdtA8hAvLNdMrtBpPNBTrQ9/ulg0FA7xLMnD6mifv+enAIeRmvtv+WgdCE+LPGOfQmtJRrVaIVhQ=="],
|
||||
|
||||
"orderedmap": ["orderedmap@2.1.1", "", {}, "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g=="],
|
||||
|
||||
"p-event": ["p-event@6.0.1", "", { "dependencies": { "p-timeout": "^6.1.2" } }, "sha512-Q6Bekk5wpzW5qIyUP4gdMEujObYstZl6DMMOSenwBvV0BlE5LkDwkjs5yHbZmdCEq2o4RJx4tE1vwxFVf2FG1w=="],
|
||||
|
||||
"p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
|
||||
@@ -1050,6 +1270,30 @@
|
||||
|
||||
"prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="],
|
||||
|
||||
"prosemirror-changeset": ["prosemirror-changeset@2.4.1", "", { "dependencies": { "prosemirror-transform": "^1.0.0" } }, "sha512-96WBLhOaYhJ+kPhLg3uW359Tz6I/MfcrQfL4EGv4SrcqKEMC1gmoGrXHecPE8eOwTVCJ4IwgfzM8fFad25wNfw=="],
|
||||
|
||||
"prosemirror-commands": ["prosemirror-commands@1.7.1", "", { "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.10.2" } }, "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w=="],
|
||||
|
||||
"prosemirror-dropcursor": ["prosemirror-dropcursor@1.8.2", "", { "dependencies": { "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.1.0", "prosemirror-view": "^1.1.0" } }, "sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw=="],
|
||||
|
||||
"prosemirror-gapcursor": ["prosemirror-gapcursor@1.4.1", "", { "dependencies": { "prosemirror-keymap": "^1.0.0", "prosemirror-model": "^1.0.0", "prosemirror-state": "^1.0.0", "prosemirror-view": "^1.0.0" } }, "sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw=="],
|
||||
|
||||
"prosemirror-history": ["prosemirror-history@1.5.0", "", { "dependencies": { "prosemirror-state": "^1.2.2", "prosemirror-transform": "^1.0.0", "prosemirror-view": "^1.31.0", "rope-sequence": "^1.3.0" } }, "sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg=="],
|
||||
|
||||
"prosemirror-keymap": ["prosemirror-keymap@1.2.3", "", { "dependencies": { "prosemirror-state": "^1.0.0", "w3c-keyname": "^2.2.0" } }, "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw=="],
|
||||
|
||||
"prosemirror-model": ["prosemirror-model@1.25.7", "", { "dependencies": { "orderedmap": "^2.0.0" } }, "sha512-A79aN8QEFUwI6cax8Yq4Rpcx1TJZ3Kagn+ii7qLo4/V8H3mMiHrhFyhTyHHvpSnOgMPpWiDGSwM3etwrxE50ug=="],
|
||||
|
||||
"prosemirror-schema-list": ["prosemirror-schema-list@1.5.1", "", { "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.7.3" } }, "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q=="],
|
||||
|
||||
"prosemirror-state": ["prosemirror-state@1.4.4", "", { "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-transform": "^1.0.0", "prosemirror-view": "^1.27.0" } }, "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw=="],
|
||||
|
||||
"prosemirror-tables": ["prosemirror-tables@1.8.5", "", { "dependencies": { "prosemirror-keymap": "^1.2.3", "prosemirror-model": "^1.25.4", "prosemirror-state": "^1.4.4", "prosemirror-transform": "^1.10.5", "prosemirror-view": "^1.41.4" } }, "sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw=="],
|
||||
|
||||
"prosemirror-transform": ["prosemirror-transform@1.12.0", "", { "dependencies": { "prosemirror-model": "^1.21.0" } }, "sha512-GxboyN4AMIsoHNtz5uf2r2Ru551i5hWeCMD6E2Ib4Eogqoub0NflniaBPVQ4MrGE5yZ8JV9tUHg9qcZTTrcN4w=="],
|
||||
|
||||
"prosemirror-view": ["prosemirror-view@1.41.8", "", { "dependencies": { "prosemirror-model": "^1.20.0", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.1.0" } }, "sha512-TnKDdohEatgyZNGCDWIdccOHXhYloJwbwU+phw/a23KBvJIR9lWQWW7WHHK3vBdOLDNuF7TaX98GObUZOWkOnA=="],
|
||||
|
||||
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
|
||||
|
||||
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||
@@ -1064,6 +1308,20 @@
|
||||
|
||||
"rc9": ["rc9@3.0.1", "", { "dependencies": { "defu": "^6.1.6", "destr": "^2.0.5" } }, "sha512-gMDyleLWVE+i6Sgtc0QbbY6pEKqYs97NGi6isHQPqYlLemPoO8dxQ3uGi0f4NiP98c+jMW6cG1Kx9dDwfvqARQ=="],
|
||||
|
||||
"react": ["react@19.2.6", "", {}, "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q=="],
|
||||
|
||||
"react-dom": ["react-dom@19.2.6", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.6" } }, "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g=="],
|
||||
|
||||
"react-hook-form": ["react-hook-form@7.76.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-rYM7tPiWlu3nZchkR/ex7piyzui2vFPyaLnXnI/RnblB/L4qfMmyses8llJVtF1NpE9WBBsJlGtcSZzPCXW1qQ=="],
|
||||
|
||||
"react-medium-image-zoom": ["react-medium-image-zoom@5.4.5", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-58QSIRK6X3uw2fSTejJRnH0JuKTZl7ZJYX+sAMaYx4YTEm33gsNdnP5RuQSCnBiAvisQeErqZWAT31bR89WB6g=="],
|
||||
|
||||
"react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="],
|
||||
|
||||
"react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="],
|
||||
|
||||
"react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="],
|
||||
|
||||
"readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
|
||||
|
||||
"readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
|
||||
@@ -1084,6 +1342,8 @@
|
||||
|
||||
"rollup": ["rollup@4.60.4", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.60.4", "@rollup/rollup-android-arm64": "4.60.4", "@rollup/rollup-darwin-arm64": "4.60.4", "@rollup/rollup-darwin-x64": "4.60.4", "@rollup/rollup-freebsd-arm64": "4.60.4", "@rollup/rollup-freebsd-x64": "4.60.4", "@rollup/rollup-linux-arm-gnueabihf": "4.60.4", "@rollup/rollup-linux-arm-musleabihf": "4.60.4", "@rollup/rollup-linux-arm64-gnu": "4.60.4", "@rollup/rollup-linux-arm64-musl": "4.60.4", "@rollup/rollup-linux-loong64-gnu": "4.60.4", "@rollup/rollup-linux-loong64-musl": "4.60.4", "@rollup/rollup-linux-ppc64-gnu": "4.60.4", "@rollup/rollup-linux-ppc64-musl": "4.60.4", "@rollup/rollup-linux-riscv64-gnu": "4.60.4", "@rollup/rollup-linux-riscv64-musl": "4.60.4", "@rollup/rollup-linux-s390x-gnu": "4.60.4", "@rollup/rollup-linux-x64-gnu": "4.60.4", "@rollup/rollup-linux-x64-musl": "4.60.4", "@rollup/rollup-openbsd-x64": "4.60.4", "@rollup/rollup-openharmony-arm64": "4.60.4", "@rollup/rollup-win32-arm64-msvc": "4.60.4", "@rollup/rollup-win32-ia32-msvc": "4.60.4", "@rollup/rollup-win32-x64-gnu": "4.60.4", "@rollup/rollup-win32-x64-msvc": "4.60.4", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g=="],
|
||||
|
||||
"rope-sequence": ["rope-sequence@1.3.4", "", {}, "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ=="],
|
||||
|
||||
"router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
|
||||
|
||||
"run-applescript": ["run-applescript@7.1.0", "", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="],
|
||||
@@ -1096,6 +1356,8 @@
|
||||
|
||||
"sax": ["sax@1.2.4", "", {}, "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="],
|
||||
|
||||
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
||||
|
||||
"semver": ["semver@7.8.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg=="],
|
||||
|
||||
"send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="],
|
||||
@@ -1106,6 +1368,8 @@
|
||||
|
||||
"setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
|
||||
|
||||
"shadcn-minimal-tiptap": ["shadcn-minimal-tiptap@github:Aslam97/minimal-tiptap#93b5559", { "dependencies": { "@hookform/resolvers": "^3.10.0", "@radix-ui/react-context-menu": "^2.2.16", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-icons": "^1.3.2", "@radix-ui/react-label": "^2.1.8", "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-separator": "^1.1.8", "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-switch": "^1.2.6", "@radix-ui/react-toggle": "^1.1.10", "@radix-ui/react-toggle-group": "^1.1.11", "@radix-ui/react-tooltip": "^1.2.8", "@tiptap/extension-bubble-menu": "^3.14.0", "@tiptap/extension-code-block-lowlight": "^3.14.0", "@tiptap/extension-color": "^3.14.0", "@tiptap/extension-horizontal-rule": "^3.14.0", "@tiptap/extension-image": "^3.14.0", "@tiptap/extension-list": "^3.14.0", "@tiptap/extension-table": "^3.14.0", "@tiptap/extension-text-style": "^3.14.0", "@tiptap/extension-typography": "^3.14.0", "@tiptap/extensions": "^3.14.0", "@tiptap/markdown": "^3.14.0", "@tiptap/pm": "^3.14.0", "@tiptap/react": "^3.14.0", "@tiptap/starter-kit": "^3.14.0", "@vercel/analytics": "^1.6.1", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "glob": "^11.1.0", "lowlight": "^3.3.0", "lucide-react": "^0.486.0", "next-themes": "^0.4.6", "react": "^19.2.1", "react-dom": "^19.2.1", "react-hook-form": "^7.68.0", "react-medium-image-zoom": "^5.4.0", "sonner": "^1.7.4", "tailwind-merge": "^3.4.0", "zod": "^3.25.76" } }, "Aslam97-minimal-tiptap-93b5559", "sha512-n3D4e9DiPrWWGd/8QcSwN1E1FfwPKcyJ5SZTHhJLsZynLGWLER2EvRT0vBdYf+JjHypz2hvV2HRU04JsyxEXDw=="],
|
||||
|
||||
"shadcn-vue": ["shadcn-vue@2.7.3", "", { "dependencies": { "@dotenvx/dotenvx": "^1.51.1", "@modelcontextprotocol/sdk": "^1.24.3", "@unovue/detypes": "^0.8.5", "@vue/compiler-sfc": "^3.5", "c12": "^3.3.2", "commander": "^14.0.2", "consola": "^3.4.2", "dedent": "^1.7.0", "deepmerge": "^4.3.1", "diff": "^8.0.2", "fs-extra": "^11.3.2", "fuzzysort": "^3.1.0", "get-tsconfig": "^4.13.0", "giget": "^3.2.0", "magic-string": "^0.30.21", "nypm": "^0.6.2", "ofetch": "^1.5.1", "open": "^10.2.0", "ora": "^9.0.0", "pathe": "^2.0.3", "postcss": "^8.5.10", "postcss-selector-parser": "^7.1.1", "prompts": "^2.4.2", "reka-ui": "^2.9.2", "semver": "^7.7.3", "stringify-object": "^6.0.0", "tailwindcss": "^4.1.17", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "ts-morph": "^27.0.2", "undici": "^7.16.0", "validate-npm-package-name": "^5.0.1", "vue-metamorph": "3.3.4", "zod": "^3.25.76", "zod-to-json-schema": "^3.25.0" }, "bin": { "shadcn-vue": "dist/index.js" } }, "sha512-bYfn9RbjG98++IjvCEhVhvva64mjDAGrtE4QNSuy6jjtE/XpI3syRJaBYmrvPBI93W27dLGbCTr8p7gnvjkQvQ=="],
|
||||
|
||||
"sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="],
|
||||
@@ -1122,12 +1386,14 @@
|
||||
|
||||
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
|
||||
|
||||
"signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
|
||||
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||
|
||||
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
|
||||
|
||||
"slice-ansi": ["slice-ansi@4.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" } }, "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ=="],
|
||||
|
||||
"sonner": ["sonner@1.7.4", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw=="],
|
||||
|
||||
"source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
|
||||
|
||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||
@@ -1196,6 +1462,8 @@
|
||||
|
||||
"undici": ["undici@7.25.0", "", {}, "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ=="],
|
||||
|
||||
"undici-types": ["undici-types@7.24.6", "", {}, "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg=="],
|
||||
|
||||
"universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="],
|
||||
|
||||
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
|
||||
@@ -1204,6 +1472,12 @@
|
||||
|
||||
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
||||
|
||||
"use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="],
|
||||
|
||||
"use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="],
|
||||
|
||||
"use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="],
|
||||
|
||||
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
||||
|
||||
"validate-npm-package-name": ["validate-npm-package-name@5.0.1", "", {}, "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ=="],
|
||||
@@ -1228,6 +1502,8 @@
|
||||
|
||||
"vue-tsc": ["vue-tsc@2.2.12", "", { "dependencies": { "@volar/typescript": "2.4.15", "@vue/language-core": "2.2.12" }, "peerDependencies": { "typescript": ">=5.0.0" }, "bin": { "vue-tsc": "./bin/vue-tsc.js" } }, "sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw=="],
|
||||
|
||||
"w3c-keyname": ["w3c-keyname@2.2.8", "", {}, "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="],
|
||||
|
||||
"web-worker": ["web-worker@1.5.0", "", {}, "sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw=="],
|
||||
|
||||
"which": ["which@4.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="],
|
||||
@@ -1244,6 +1520,10 @@
|
||||
|
||||
"xlsx": ["xlsx@0.18.5", "", { "dependencies": { "adler-32": "~1.3.0", "cfb": "~1.2.1", "codepage": "~1.15.0", "crc-32": "~1.2.1", "ssf": "~0.11.2", "wmf": "~1.0.1", "word": "~0.3.0" }, "bin": { "xlsx": "bin/xlsx.njs" } }, "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ=="],
|
||||
|
||||
"xml": ["xml@1.0.1", "", {}, "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw=="],
|
||||
|
||||
"xml-js": ["xml-js@1.6.11", "", { "dependencies": { "sax": "^1.2.4" }, "bin": { "xml-js": "./bin/cli.js" } }, "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g=="],
|
||||
|
||||
"xmlbuilder": ["xmlbuilder@10.1.1", "", {}, "sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg=="],
|
||||
|
||||
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
||||
@@ -1260,6 +1540,8 @@
|
||||
|
||||
"@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||
|
||||
"@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"@babel/helper-create-class-features-plugin/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
@@ -1270,6 +1552,22 @@
|
||||
|
||||
"@eslint/config-array/minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
|
||||
|
||||
"@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||
|
||||
"@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||
|
||||
"@radix-ui/react-label/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="],
|
||||
|
||||
"@radix-ui/react-menu/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||
|
||||
"@radix-ui/react-popover/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||
|
||||
"@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||
|
||||
"@radix-ui/react-separator/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="],
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],
|
||||
@@ -1300,13 +1598,13 @@
|
||||
|
||||
"eslint/minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
|
||||
|
||||
"foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||
"execa/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
|
||||
|
||||
"glob/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
|
||||
"glob/minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
|
||||
|
||||
"micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
|
||||
|
||||
"path-scurry/lru-cache": ["lru-cache@11.5.0", "", {}, "sha512-5YgH9UJd7wVb9hIouI2adWpgqrrICkt070Dnj8EUY1+B4B2P9eRLPAkAAo6NICA7CEhOIeBHl46u9zSNpNu7zA=="],
|
||||
"postcss/nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="],
|
||||
|
||||
"recast-x/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||
|
||||
@@ -1314,18 +1612,16 @@
|
||||
|
||||
"restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="],
|
||||
|
||||
"restore-cursor/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||
|
||||
"string-width/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
|
||||
|
||||
"stylus/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
|
||||
|
||||
"table/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"type-is/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="],
|
||||
|
||||
"vue-metamorph/@babel/parser": ["@babel/parser@8.0.0-alpha.12", "", { "bin": "./bin/babel-parser.js" }, "sha512-AzWmrp4uJ+DcXVH0uoUpJVhRqxNirC0BbXsZ82AQuVod41CoaV5G+cwcvtYusrIIxv7BIJb6ce0dQ9L0wAl1iA=="],
|
||||
|
||||
"vue-metamorph/glob": ["glob@11.1.0", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw=="],
|
||||
|
||||
"@eslint/config-array/minimatch/brace-expansion": ["brace-expansion@5.0.6", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g=="],
|
||||
|
||||
"@ts-morph/common/minimatch/brace-expansion": ["brace-expansion@5.0.6", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g=="],
|
||||
@@ -1340,11 +1636,11 @@
|
||||
|
||||
"eslint/minimatch/brace-expansion": ["brace-expansion@5.0.6", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g=="],
|
||||
|
||||
"glob/minimatch/brace-expansion": ["brace-expansion@1.1.14", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g=="],
|
||||
"glob/minimatch/brace-expansion": ["brace-expansion@5.0.6", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g=="],
|
||||
|
||||
"string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
||||
|
||||
"vue-metamorph/glob/minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
|
||||
"stylus/glob/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
|
||||
|
||||
"@eslint/config-array/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
|
||||
|
||||
@@ -1352,8 +1648,8 @@
|
||||
|
||||
"eslint/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
|
||||
|
||||
"vue-metamorph/glob/minimatch/brace-expansion": ["brace-expansion@5.0.6", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g=="],
|
||||
"glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
|
||||
|
||||
"vue-metamorph/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
|
||||
"stylus/glob/minimatch/brace-expansion": ["brace-expansion@1.1.14", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g=="],
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+5884
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,17 @@
|
||||
"@tanstack/vue-table": "^8.21.3",
|
||||
"@tauri-apps/api": "^2.11.0",
|
||||
"@tauri-apps/cli": "^2.11.2",
|
||||
"@tauri-apps/plugin-http": "^2.5.9",
|
||||
"@tiptap/extension-code-block-lowlight": "^3.23.6",
|
||||
"@tiptap/extension-image": "^3.23.6",
|
||||
"@tiptap/extension-link": "^3.23.6",
|
||||
"@tiptap/extension-placeholder": "^3.23.6",
|
||||
"@tiptap/extension-table": "^3.23.6",
|
||||
"@tiptap/extension-table-cell": "^3.23.6",
|
||||
"@tiptap/extension-table-header": "^3.23.6",
|
||||
"@tiptap/extension-table-row": "^3.23.6",
|
||||
"@tiptap/starter-kit": "^3.23.6",
|
||||
"@tiptap/vue-3": "^3.23.6",
|
||||
"@vueuse/core": "^14.3.0",
|
||||
"ag-grid-community": "^35.3.0",
|
||||
"ag-grid-vue3": "^35.3.0",
|
||||
@@ -25,10 +36,13 @@
|
||||
"clsx": "^2.1.1",
|
||||
"dexie": "^4.0.4",
|
||||
"dnd-kit-vue": "^0.0.2",
|
||||
"docx": "^9.7.1",
|
||||
"lowlight": "^3.3.0",
|
||||
"lucide-vue-next": "^1.0.0",
|
||||
"mammoth": "^1.12.0",
|
||||
"pinia": "^2.1.7",
|
||||
"reka-ui": "^2.9.8",
|
||||
"shadcn-minimal-tiptap": "github:Aslam97/minimal-tiptap",
|
||||
"shadcn-vue": "^2.7.3",
|
||||
"sharp": "^0.34.5",
|
||||
"tailwind-merge": "^3.6.0",
|
||||
|
||||
Generated
+405
-13
@@ -71,6 +71,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-plugin-http",
|
||||
"tauri-plugin-opener",
|
||||
]
|
||||
|
||||
@@ -621,6 +622,12 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.44"
|
||||
@@ -690,10 +697,29 @@ version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
"time",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cookie_store"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206"
|
||||
dependencies = [
|
||||
"cookie",
|
||||
"document-features",
|
||||
"idna",
|
||||
"log",
|
||||
"publicsuffix",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"time",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
@@ -860,6 +886,12 @@ dependencies = [
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-url"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376"
|
||||
|
||||
[[package]]
|
||||
name = "dbus"
|
||||
version = "0.9.11"
|
||||
@@ -1000,6 +1032,15 @@ dependencies = [
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "document-features"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
|
||||
dependencies = [
|
||||
"litrs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dom_query"
|
||||
version = "0.27.0"
|
||||
@@ -1092,6 +1133,15 @@ version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "endi"
|
||||
version = "1.1.1"
|
||||
@@ -1497,8 +1547,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1508,9 +1560,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"r-efi 5.3.0",
|
||||
"wasip2",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1693,6 +1747,25 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
"fnv",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"http 1.4.1",
|
||||
"indexmap 2.14.0",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
@@ -1859,7 +1932,7 @@ dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"h2 0.3.27",
|
||||
"http 0.2.12",
|
||||
"http-body 0.4.6",
|
||||
"httparse",
|
||||
@@ -1883,6 +1956,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"h2 0.4.14",
|
||||
"http 1.4.1",
|
||||
"http-body 1.0.1",
|
||||
"httparse",
|
||||
@@ -1903,14 +1977,30 @@ dependencies = [
|
||||
"http 0.2.12",
|
||||
"hyper 0.14.32",
|
||||
"log",
|
||||
"rustls",
|
||||
"rustls 0.22.4",
|
||||
"rustls-native-certs",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tokio-rustls 0.25.0",
|
||||
"webpki-roots 0.26.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.27.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f"
|
||||
dependencies = [
|
||||
"http 1.4.1",
|
||||
"hyper 1.9.0",
|
||||
"hyper-util",
|
||||
"rustls 0.23.40",
|
||||
"tokio",
|
||||
"tokio-rustls 0.26.4",
|
||||
"tower-service",
|
||||
"webpki-roots 1.0.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-timeout"
|
||||
version = "0.4.1"
|
||||
@@ -1941,9 +2031,11 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"socket2 0.6.3",
|
||||
"system-configuration",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"windows-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2394,7 +2486,7 @@ dependencies = [
|
||||
"futures",
|
||||
"http 0.2.12",
|
||||
"hyper 0.14.32",
|
||||
"hyper-rustls",
|
||||
"hyper-rustls 0.25.0",
|
||||
"libsql-hrana",
|
||||
"libsql-sqlite3-parser",
|
||||
"libsql-sys",
|
||||
@@ -2529,6 +2621,12 @@ version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
|
||||
|
||||
[[package]]
|
||||
name = "litrs"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.14"
|
||||
@@ -2544,6 +2642,12 @@ version = "0.4.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5"
|
||||
|
||||
[[package]]
|
||||
name = "lru-slab"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
||||
|
||||
[[package]]
|
||||
name = "markup5ever"
|
||||
version = "0.38.0"
|
||||
@@ -2830,6 +2934,7 @@ checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
|
||||
dependencies = [
|
||||
"bitflags 2.11.1",
|
||||
"block2",
|
||||
"libc",
|
||||
"objc2",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
@@ -3061,7 +3166,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
|
||||
dependencies = [
|
||||
"phf_shared 0.11.3",
|
||||
"rand",
|
||||
"rand 0.8.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3327,6 +3432,22 @@ dependencies = [
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psl-types"
|
||||
version = "2.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
|
||||
|
||||
[[package]]
|
||||
name = "publicsuffix"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf"
|
||||
dependencies = [
|
||||
"idna",
|
||||
"psl-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.39.4"
|
||||
@@ -3336,6 +3457,61 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn"
|
||||
version = "0.11.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"cfg_aliases",
|
||||
"pin-project-lite",
|
||||
"quinn-proto",
|
||||
"quinn-udp",
|
||||
"rustc-hash 2.1.2",
|
||||
"rustls 0.23.40",
|
||||
"socket2 0.6.3",
|
||||
"thiserror 2.0.18",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn-proto"
|
||||
version = "0.11.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"getrandom 0.3.4",
|
||||
"lru-slab",
|
||||
"rand 0.9.4",
|
||||
"ring",
|
||||
"rustc-hash 2.1.2",
|
||||
"rustls 0.23.40",
|
||||
"rustls-pki-types",
|
||||
"slab",
|
||||
"thiserror 2.0.18",
|
||||
"tinyvec",
|
||||
"tracing",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn-udp"
|
||||
version = "0.5.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
|
||||
dependencies = [
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"socket2 0.6.3",
|
||||
"tracing",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.45"
|
||||
@@ -3364,8 +3540,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_chacha 0.3.1",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
|
||||
dependencies = [
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_core 0.9.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3375,7 +3561,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.9.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3387,6 +3583,15 @@ dependencies = [
|
||||
"getrandom 0.2.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "raw-window-handle"
|
||||
version = "0.6.2"
|
||||
@@ -3473,6 +3678,49 @@ version = "0.8.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"cookie",
|
||||
"cookie_store",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"h2 0.4.14",
|
||||
"http 1.4.1",
|
||||
"http-body 1.0.1",
|
||||
"http-body-util",
|
||||
"hyper 1.9.0",
|
||||
"hyper-rustls 0.27.9",
|
||||
"hyper-util",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"quinn",
|
||||
"rustls 0.23.40",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper 1.0.2",
|
||||
"tokio",
|
||||
"tokio-rustls 0.26.4",
|
||||
"tower 0.5.3",
|
||||
"tower-http 0.6.11",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"webpki-roots 1.0.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.13.4"
|
||||
@@ -3577,7 +3825,21 @@ dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki",
|
||||
"rustls-webpki 0.102.8",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki 0.103.13",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
@@ -3610,6 +3872,7 @@ version = "1.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9"
|
||||
dependencies = [
|
||||
"web-time",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
@@ -3624,12 +3887,29 @@ dependencies = [
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
@@ -3852,6 +4132,18 @@ dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_urlencoded"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "3.20.0"
|
||||
@@ -4134,6 +4426,27 @@ dependencies = [
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b"
|
||||
dependencies = [
|
||||
"bitflags 2.11.1",
|
||||
"core-foundation 0.9.4",
|
||||
"system-configuration-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration-sys"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-deps"
|
||||
version = "6.2.2"
|
||||
@@ -4234,7 +4547,7 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"plist",
|
||||
"raw-window-handle",
|
||||
"reqwest",
|
||||
"reqwest 0.13.4",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
@@ -4333,6 +4646,54 @@ dependencies = [
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-fs"
|
||||
version = "2.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7ecc274121aca0c036a2b42d1cbe83d368d348f54e0bb8a735c2b1548e8f371"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dunce",
|
||||
"glob",
|
||||
"log",
|
||||
"objc2-foundation",
|
||||
"percent-encoding",
|
||||
"schemars 0.8.22",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.18",
|
||||
"toml 1.1.2+spec-1.1.0",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-http"
|
||||
version = "2.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5bd512048e1985b7ec78f96d99083e2ddaf7e0d906b2b63c44ce5bb8b894067"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"cookie_store",
|
||||
"data-url",
|
||||
"http 1.4.1",
|
||||
"regex",
|
||||
"reqwest 0.12.28",
|
||||
"schemars 0.8.22",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tauri-plugin-fs",
|
||||
"thiserror 2.0.18",
|
||||
"tokio",
|
||||
"url",
|
||||
"urlpattern",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-opener"
|
||||
version = "2.5.4"
|
||||
@@ -4618,11 +4979,21 @@ version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"rustls 0.22.4",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.26.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
|
||||
dependencies = [
|
||||
"rustls 0.23.40",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-stream"
|
||||
version = "0.1.18"
|
||||
@@ -4778,7 +5149,7 @@ dependencies = [
|
||||
"axum",
|
||||
"base64 0.21.7",
|
||||
"bytes",
|
||||
"h2",
|
||||
"h2 0.3.27",
|
||||
"http 0.2.12",
|
||||
"http-body 0.4.6",
|
||||
"hyper 0.14.32",
|
||||
@@ -4825,7 +5196,7 @@ dependencies = [
|
||||
"indexmap 1.9.3",
|
||||
"pin-project",
|
||||
"pin-project-lite",
|
||||
"rand",
|
||||
"rand 0.8.6",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
@@ -5292,6 +5663,16 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-time"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web_atoms"
|
||||
version = "0.2.4"
|
||||
@@ -5563,6 +5944,17 @@ dependencies = [
|
||||
"windows-link 0.1.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-registry"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720"
|
||||
dependencies = [
|
||||
"windows-link 0.2.1",
|
||||
"windows-result 0.4.1",
|
||||
"windows-strings 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.3.4"
|
||||
|
||||
@@ -6,6 +6,7 @@ edition = "2021"
|
||||
[dependencies]
|
||||
tauri = { version = "2", features = [] }
|
||||
tauri-plugin-opener = "2"
|
||||
tauri-plugin-http = "2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
libsql = "0.9"
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"identifier": "default",
|
||||
"description": "Default capabilities",
|
||||
"windows": ["main"],
|
||||
"permissions": [
|
||||
"core:default",
|
||||
"opener:default",
|
||||
"http:default"
|
||||
]
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
{}
|
||||
{"default":{"identifier":"default","description":"Default capabilities","local":true,"windows":["main"],"permissions":["core:default","opener:default","http:default"]}}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -134,6 +134,144 @@
|
||||
"description": "Reference a permission or permission set by identifier and extends its scope.",
|
||||
"type": "object",
|
||||
"allOf": [
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"identifier": {
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "This permission set configures what kind of\nfetch operations are available from the http plugin.\n\nThis enables all fetch operations but does not\nallow explicitly any origins to be fetched. This needs to\nbe manually configured before usage.\n\n#### Granted Permissions\n\nAll fetch operations are enabled.\n\n\n#### This default permission set includes:\n\n- `allow-fetch`\n- `allow-fetch-cancel`\n- `allow-fetch-send`\n- `allow-fetch-read-body`\n- `allow-fetch-cancel-body`",
|
||||
"type": "string",
|
||||
"const": "http:default",
|
||||
"markdownDescription": "This permission set configures what kind of\nfetch operations are available from the http plugin.\n\nThis enables all fetch operations but does not\nallow explicitly any origins to be fetched. This needs to\nbe manually configured before usage.\n\n#### Granted Permissions\n\nAll fetch operations are enabled.\n\n\n#### This default permission set includes:\n\n- `allow-fetch`\n- `allow-fetch-cancel`\n- `allow-fetch-send`\n- `allow-fetch-read-body`\n- `allow-fetch-cancel-body`"
|
||||
},
|
||||
{
|
||||
"description": "Enables the fetch command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:allow-fetch",
|
||||
"markdownDescription": "Enables the fetch command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the fetch_cancel command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:allow-fetch-cancel",
|
||||
"markdownDescription": "Enables the fetch_cancel command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the fetch_cancel_body command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:allow-fetch-cancel-body",
|
||||
"markdownDescription": "Enables the fetch_cancel_body command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the fetch_read_body command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:allow-fetch-read-body",
|
||||
"markdownDescription": "Enables the fetch_read_body command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the fetch_send command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:allow-fetch-send",
|
||||
"markdownDescription": "Enables the fetch_send command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the fetch command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:deny-fetch",
|
||||
"markdownDescription": "Denies the fetch command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the fetch_cancel command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:deny-fetch-cancel",
|
||||
"markdownDescription": "Denies the fetch_cancel command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the fetch_cancel_body command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:deny-fetch-cancel-body",
|
||||
"markdownDescription": "Denies the fetch_cancel_body command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the fetch_read_body command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:deny-fetch-read-body",
|
||||
"markdownDescription": "Denies the fetch_read_body command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the fetch_send command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:deny-fetch-send",
|
||||
"markdownDescription": "Denies the fetch_send command without any pre-configured scope."
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"properties": {
|
||||
"allow": {
|
||||
"items": {
|
||||
"title": "HttpScopeEntry",
|
||||
"description": "HTTP scope entry.",
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "A URL that can be accessed by the webview when using the HTTP APIs. Wildcards can be used following the URL pattern standard.\n\nSee [the URL Pattern spec](https://urlpattern.spec.whatwg.org/) for more information.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin on port 443\n\n- \"https://*:*\" : allows all HTTPS origin on any port\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"url"
|
||||
],
|
||||
"properties": {
|
||||
"url": {
|
||||
"description": "A URL that can be accessed by the webview when using the HTTP APIs. Wildcards can be used following the URL pattern standard.\n\nSee [the URL Pattern spec](https://urlpattern.spec.whatwg.org/) for more information.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin on port 443\n\n- \"https://*:*\" : allows all HTTPS origin on any port\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"deny": {
|
||||
"items": {
|
||||
"title": "HttpScopeEntry",
|
||||
"description": "HTTP scope entry.",
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "A URL that can be accessed by the webview when using the HTTP APIs. Wildcards can be used following the URL pattern standard.\n\nSee [the URL Pattern spec](https://urlpattern.spec.whatwg.org/) for more information.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin on port 443\n\n- \"https://*:*\" : allows all HTTPS origin on any port\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"url"
|
||||
],
|
||||
"properties": {
|
||||
"url": {
|
||||
"description": "A URL that can be accessed by the webview when using the HTTP APIs. Wildcards can be used following the URL pattern standard.\n\nSee [the URL Pattern spec](https://urlpattern.spec.whatwg.org/) for more information.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin on port 443\n\n- \"https://*:*\" : allows all HTTPS origin on any port\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"identifier": {
|
||||
"description": "Identifier of the permission or permission set.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Identifier"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
@@ -2360,6 +2498,72 @@
|
||||
"const": "core:window:deny-unminimize",
|
||||
"markdownDescription": "Denies the unminimize command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "This permission set configures what kind of\nfetch operations are available from the http plugin.\n\nThis enables all fetch operations but does not\nallow explicitly any origins to be fetched. This needs to\nbe manually configured before usage.\n\n#### Granted Permissions\n\nAll fetch operations are enabled.\n\n\n#### This default permission set includes:\n\n- `allow-fetch`\n- `allow-fetch-cancel`\n- `allow-fetch-send`\n- `allow-fetch-read-body`\n- `allow-fetch-cancel-body`",
|
||||
"type": "string",
|
||||
"const": "http:default",
|
||||
"markdownDescription": "This permission set configures what kind of\nfetch operations are available from the http plugin.\n\nThis enables all fetch operations but does not\nallow explicitly any origins to be fetched. This needs to\nbe manually configured before usage.\n\n#### Granted Permissions\n\nAll fetch operations are enabled.\n\n\n#### This default permission set includes:\n\n- `allow-fetch`\n- `allow-fetch-cancel`\n- `allow-fetch-send`\n- `allow-fetch-read-body`\n- `allow-fetch-cancel-body`"
|
||||
},
|
||||
{
|
||||
"description": "Enables the fetch command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:allow-fetch",
|
||||
"markdownDescription": "Enables the fetch command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the fetch_cancel command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:allow-fetch-cancel",
|
||||
"markdownDescription": "Enables the fetch_cancel command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the fetch_cancel_body command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:allow-fetch-cancel-body",
|
||||
"markdownDescription": "Enables the fetch_cancel_body command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the fetch_read_body command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:allow-fetch-read-body",
|
||||
"markdownDescription": "Enables the fetch_read_body command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the fetch_send command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:allow-fetch-send",
|
||||
"markdownDescription": "Enables the fetch_send command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the fetch command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:deny-fetch",
|
||||
"markdownDescription": "Denies the fetch command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the fetch_cancel command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:deny-fetch-cancel",
|
||||
"markdownDescription": "Denies the fetch_cancel command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the fetch_cancel_body command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:deny-fetch-cancel-body",
|
||||
"markdownDescription": "Denies the fetch_cancel_body command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the fetch_read_body command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:deny-fetch-read-body",
|
||||
"markdownDescription": "Denies the fetch_read_body command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the fetch_send command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:deny-fetch-send",
|
||||
"markdownDescription": "Denies the fetch_send command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`",
|
||||
"type": "string",
|
||||
|
||||
@@ -134,6 +134,144 @@
|
||||
"description": "Reference a permission or permission set by identifier and extends its scope.",
|
||||
"type": "object",
|
||||
"allOf": [
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"identifier": {
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "This permission set configures what kind of\nfetch operations are available from the http plugin.\n\nThis enables all fetch operations but does not\nallow explicitly any origins to be fetched. This needs to\nbe manually configured before usage.\n\n#### Granted Permissions\n\nAll fetch operations are enabled.\n\n\n#### This default permission set includes:\n\n- `allow-fetch`\n- `allow-fetch-cancel`\n- `allow-fetch-send`\n- `allow-fetch-read-body`\n- `allow-fetch-cancel-body`",
|
||||
"type": "string",
|
||||
"const": "http:default",
|
||||
"markdownDescription": "This permission set configures what kind of\nfetch operations are available from the http plugin.\n\nThis enables all fetch operations but does not\nallow explicitly any origins to be fetched. This needs to\nbe manually configured before usage.\n\n#### Granted Permissions\n\nAll fetch operations are enabled.\n\n\n#### This default permission set includes:\n\n- `allow-fetch`\n- `allow-fetch-cancel`\n- `allow-fetch-send`\n- `allow-fetch-read-body`\n- `allow-fetch-cancel-body`"
|
||||
},
|
||||
{
|
||||
"description": "Enables the fetch command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:allow-fetch",
|
||||
"markdownDescription": "Enables the fetch command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the fetch_cancel command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:allow-fetch-cancel",
|
||||
"markdownDescription": "Enables the fetch_cancel command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the fetch_cancel_body command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:allow-fetch-cancel-body",
|
||||
"markdownDescription": "Enables the fetch_cancel_body command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the fetch_read_body command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:allow-fetch-read-body",
|
||||
"markdownDescription": "Enables the fetch_read_body command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the fetch_send command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:allow-fetch-send",
|
||||
"markdownDescription": "Enables the fetch_send command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the fetch command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:deny-fetch",
|
||||
"markdownDescription": "Denies the fetch command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the fetch_cancel command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:deny-fetch-cancel",
|
||||
"markdownDescription": "Denies the fetch_cancel command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the fetch_cancel_body command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:deny-fetch-cancel-body",
|
||||
"markdownDescription": "Denies the fetch_cancel_body command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the fetch_read_body command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:deny-fetch-read-body",
|
||||
"markdownDescription": "Denies the fetch_read_body command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the fetch_send command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:deny-fetch-send",
|
||||
"markdownDescription": "Denies the fetch_send command without any pre-configured scope."
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"properties": {
|
||||
"allow": {
|
||||
"items": {
|
||||
"title": "HttpScopeEntry",
|
||||
"description": "HTTP scope entry.",
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "A URL that can be accessed by the webview when using the HTTP APIs. Wildcards can be used following the URL pattern standard.\n\nSee [the URL Pattern spec](https://urlpattern.spec.whatwg.org/) for more information.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin on port 443\n\n- \"https://*:*\" : allows all HTTPS origin on any port\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"url"
|
||||
],
|
||||
"properties": {
|
||||
"url": {
|
||||
"description": "A URL that can be accessed by the webview when using the HTTP APIs. Wildcards can be used following the URL pattern standard.\n\nSee [the URL Pattern spec](https://urlpattern.spec.whatwg.org/) for more information.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin on port 443\n\n- \"https://*:*\" : allows all HTTPS origin on any port\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"deny": {
|
||||
"items": {
|
||||
"title": "HttpScopeEntry",
|
||||
"description": "HTTP scope entry.",
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "A URL that can be accessed by the webview when using the HTTP APIs. Wildcards can be used following the URL pattern standard.\n\nSee [the URL Pattern spec](https://urlpattern.spec.whatwg.org/) for more information.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin on port 443\n\n- \"https://*:*\" : allows all HTTPS origin on any port\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"url"
|
||||
],
|
||||
"properties": {
|
||||
"url": {
|
||||
"description": "A URL that can be accessed by the webview when using the HTTP APIs. Wildcards can be used following the URL pattern standard.\n\nSee [the URL Pattern spec](https://urlpattern.spec.whatwg.org/) for more information.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin on port 443\n\n- \"https://*:*\" : allows all HTTPS origin on any port\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"identifier": {
|
||||
"description": "Identifier of the permission or permission set.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Identifier"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
@@ -2360,6 +2498,72 @@
|
||||
"const": "core:window:deny-unminimize",
|
||||
"markdownDescription": "Denies the unminimize command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "This permission set configures what kind of\nfetch operations are available from the http plugin.\n\nThis enables all fetch operations but does not\nallow explicitly any origins to be fetched. This needs to\nbe manually configured before usage.\n\n#### Granted Permissions\n\nAll fetch operations are enabled.\n\n\n#### This default permission set includes:\n\n- `allow-fetch`\n- `allow-fetch-cancel`\n- `allow-fetch-send`\n- `allow-fetch-read-body`\n- `allow-fetch-cancel-body`",
|
||||
"type": "string",
|
||||
"const": "http:default",
|
||||
"markdownDescription": "This permission set configures what kind of\nfetch operations are available from the http plugin.\n\nThis enables all fetch operations but does not\nallow explicitly any origins to be fetched. This needs to\nbe manually configured before usage.\n\n#### Granted Permissions\n\nAll fetch operations are enabled.\n\n\n#### This default permission set includes:\n\n- `allow-fetch`\n- `allow-fetch-cancel`\n- `allow-fetch-send`\n- `allow-fetch-read-body`\n- `allow-fetch-cancel-body`"
|
||||
},
|
||||
{
|
||||
"description": "Enables the fetch command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:allow-fetch",
|
||||
"markdownDescription": "Enables the fetch command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the fetch_cancel command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:allow-fetch-cancel",
|
||||
"markdownDescription": "Enables the fetch_cancel command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the fetch_cancel_body command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:allow-fetch-cancel-body",
|
||||
"markdownDescription": "Enables the fetch_cancel_body command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the fetch_read_body command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:allow-fetch-read-body",
|
||||
"markdownDescription": "Enables the fetch_read_body command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the fetch_send command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:allow-fetch-send",
|
||||
"markdownDescription": "Enables the fetch_send command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the fetch command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:deny-fetch",
|
||||
"markdownDescription": "Denies the fetch command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the fetch_cancel command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:deny-fetch-cancel",
|
||||
"markdownDescription": "Denies the fetch_cancel command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the fetch_cancel_body command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:deny-fetch-cancel-body",
|
||||
"markdownDescription": "Denies the fetch_cancel_body command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the fetch_read_body command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:deny-fetch-read-body",
|
||||
"markdownDescription": "Denies the fetch_read_body command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the fetch_send command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "http:deny-fetch-send",
|
||||
"markdownDescription": "Denies the fetch_send command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`",
|
||||
"type": "string",
|
||||
|
||||
@@ -18,6 +18,7 @@ fn main() {
|
||||
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.plugin(tauri_plugin_http::init())
|
||||
.manage(Mutex::new(db_path))
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
commands::get_projects,
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useWorkItemsStore, type EnrichedUserStory } from '@/stores/workitems'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { AlertTriangle, Calendar, Clock, ListChecks } from 'lucide-vue-next'
|
||||
|
||||
const { t } = useI18n()
|
||||
const workItems = useWorkItemsStore()
|
||||
|
||||
const today = new Date()
|
||||
today.setHours(0, 0, 0, 0)
|
||||
|
||||
function daysUntil(dateStr: string | undefined): number | null {
|
||||
if (!dateStr) return null
|
||||
const d = new Date(dateStr)
|
||||
d.setHours(0, 0, 0, 0)
|
||||
return Math.ceil((d.getTime() - today.getTime()) / (1000 * 60 * 60 * 24))
|
||||
}
|
||||
|
||||
function isOverdue(dateStr: string | undefined): boolean {
|
||||
const d = daysUntil(dateStr)
|
||||
return d !== null && d < 0
|
||||
}
|
||||
|
||||
function isToday(dateStr: string | undefined): boolean {
|
||||
const d = daysUntil(dateStr)
|
||||
return d === 0
|
||||
}
|
||||
|
||||
function isThisWeek(dateStr: string | undefined): boolean {
|
||||
const d = daysUntil(dateStr)
|
||||
return d !== null && d > 0 && d <= 7
|
||||
}
|
||||
|
||||
function isNextWeek(dateStr: string | undefined): boolean {
|
||||
const d = daysUntil(dateStr)
|
||||
return d !== null && d > 7 && d <= 14
|
||||
}
|
||||
|
||||
function isBlocked(hu: EnrichedUserStory): boolean {
|
||||
const s = String(hu.status ?? '').toLowerCase()
|
||||
return ['blocked', 'bloqueado'].includes(s)
|
||||
}
|
||||
|
||||
function isInProgress(hu: EnrichedUserStory): boolean {
|
||||
const s = String(hu.status ?? '').toLowerCase()
|
||||
return ['in_progress', 'doing', 'wip', 'active', 'in progress', 'en progreso', 'true', '2'].includes(s)
|
||||
}
|
||||
|
||||
function isDone(hu: EnrichedUserStory): boolean {
|
||||
const s = String(hu.status ?? '').toLowerCase()
|
||||
return ['done', 'completed', 'closed', 'finalizado', '5', '6', '7', 'qa-client', 'ready to deploy'].includes(s)
|
||||
}
|
||||
|
||||
// Agrupar HUs
|
||||
const overdueHUs = computed(() =>
|
||||
workItems.userStories.filter(hu => hu.end_date && isOverdue(hu.end_date))
|
||||
)
|
||||
|
||||
const todayHUs = computed(() =>
|
||||
workItems.userStories.filter(hu => hu.end_date && isToday(hu.end_date))
|
||||
)
|
||||
|
||||
const thisWeekHUs = computed(() =>
|
||||
workItems.userStories.filter(hu => hu.end_date && isThisWeek(hu.end_date))
|
||||
)
|
||||
|
||||
const nextWeekHUs = computed(() =>
|
||||
workItems.userStories.filter(hu => hu.end_date && isNextWeek(hu.end_date))
|
||||
)
|
||||
|
||||
const blockedHUs = computed(() =>
|
||||
workItems.userStories.filter(hu => isBlocked(hu))
|
||||
)
|
||||
|
||||
const inProgressHUs = computed(() =>
|
||||
workItems.userStories.filter(hu => isInProgress(hu))
|
||||
)
|
||||
|
||||
function formatDate(dateStr: string | undefined): string {
|
||||
if (!dateStr) return ''
|
||||
const d = new Date(dateStr)
|
||||
return d.toLocaleDateString('es-CO', { day: 'numeric', month: 'short' })
|
||||
}
|
||||
|
||||
function priorityVariant(p: unknown) {
|
||||
const s = String(p ?? '').toLowerCase().trim()
|
||||
if (['alta', 'high', 'critical', 'urgente', '1'].includes(s)) return 'destructive'
|
||||
if (['media', 'medium', 'normal', '2'].includes(s)) return 'default'
|
||||
if (['baja', 'low', '3'].includes(s)) return 'secondary'
|
||||
return 'outline'
|
||||
}
|
||||
|
||||
const totalHUs = computed(() => workItems.userStories.length)
|
||||
const doneHUs = computed(() =>
|
||||
workItems.userStories.filter(hu => isDone(hu)).length
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<!-- Summary cards -->
|
||||
<div class="grid gap-3 @xl:grid-cols-4">
|
||||
<Card class="bg-gradient-to-t from-primary/5 to-card shadow-xs">
|
||||
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle class="text-xs font-medium">{{ t('dashboard.hus') }}</CardTitle>
|
||||
<ListChecks class="size-3.5 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div class="text-xl font-bold">{{ totalHUs }}</div>
|
||||
<p class="text-[10px] text-muted-foreground">{{ doneHUs }} completadas ({{ totalHUs > 0 ? Math.round(doneHUs / totalHUs * 100) : 0 }}%)</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card class="bg-gradient-to-t from-amber-500/5 to-card shadow-xs">
|
||||
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle class="text-xs font-medium">En progreso</CardTitle>
|
||||
<Clock class="size-3.5 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div class="text-xl font-bold">{{ inProgressHUs.length }}</div>
|
||||
<p class="text-[10px] text-muted-foreground">activas</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card class="bg-gradient-to-t from-red-500/5 to-card shadow-xs">
|
||||
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle class="text-xs font-medium">Vencidas</CardTitle>
|
||||
<AlertTriangle class="size-3.5 text-red-500" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div class="text-xl font-bold text-red-500">{{ overdueHUs.length }}</div>
|
||||
<p class="text-[10px] text-muted-foreground">requieren atención</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card class="bg-gradient-to-t from-purple-500/5 to-card shadow-xs">
|
||||
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle class="text-xs font-medium">Bloqueadas</CardTitle>
|
||||
<AlertTriangle class="size-3.5 text-purple-500" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div class="text-xl font-bold text-purple-500">{{ blockedHUs.length }}</div>
|
||||
<p class="text-[10px] text-muted-foreground">con impedimentos</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<!-- Overdue -->
|
||||
<Card v-if="overdueHUs.length > 0" class="border-l-3 border-l-red-500">
|
||||
<CardHeader class="pb-2">
|
||||
<CardTitle class="text-xs font-medium flex items-center gap-2 text-red-600">
|
||||
<AlertTriangle class="size-3.5" />
|
||||
Vencidas ({{ overdueHUs.length }})
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-1">
|
||||
<div v-for="hu in overdueHUs" :key="hu.id" class="flex items-center gap-2 text-xs py-1 border-b last:border-0">
|
||||
<span class="font-mono text-[10px] text-muted-foreground w-16">{{ hu.code || `#${hu.id}` }}</span>
|
||||
<span class="flex-1 truncate">{{ hu._cleanTitle || hu.title }}</span>
|
||||
<Badge :variant="priorityVariant(hu.priority)" class="text-[10px]">{{ hu.priority }}</Badge>
|
||||
<span class="text-red-500 font-mono text-[10px] w-16 text-right">{{ formatDate(hu.end_date) }}</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- Today -->
|
||||
<Card v-if="todayHUs.length > 0" class="border-l-3 border-l-amber-500">
|
||||
<CardHeader class="pb-2">
|
||||
<CardTitle class="text-xs font-medium flex items-center gap-2">
|
||||
<Calendar class="size-3.5" />
|
||||
Hoy ({{ todayHUs.length }})
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-1">
|
||||
<div v-for="hu in todayHUs" :key="hu.id" class="flex items-center gap-2 text-xs py-1 border-b last:border-0">
|
||||
<span class="font-mono text-[10px] text-muted-foreground w-16">{{ hu.code || `#${hu.id}` }}</span>
|
||||
<span class="flex-1 truncate">{{ hu._cleanTitle || hu.title }}</span>
|
||||
<Badge :variant="priorityVariant(hu.priority)" class="text-[10px]">{{ hu.priority }}</Badge>
|
||||
<Badge variant="outline" class="text-[10px]">Hoy</Badge>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- This week -->
|
||||
<Card v-if="thisWeekHUs.length > 0">
|
||||
<CardHeader class="pb-2">
|
||||
<CardTitle class="text-xs font-medium flex items-center gap-2">
|
||||
<Calendar class="size-3.5" />
|
||||
Esta semana ({{ thisWeekHUs.length }})
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-1">
|
||||
<div v-for="hu in thisWeekHUs" :key="hu.id" class="flex items-center gap-2 text-xs py-1 border-b last:border-0">
|
||||
<span class="font-mono text-[10px] text-muted-foreground w-16">{{ hu.code || `#${hu.id}` }}</span>
|
||||
<span class="flex-1 truncate">{{ hu._cleanTitle || hu.title }}</span>
|
||||
<Badge :variant="priorityVariant(hu.priority)" class="text-[10px]">{{ hu.priority }}</Badge>
|
||||
<span class="font-mono text-[10px] text-muted-foreground w-16 text-right">{{ formatDate(hu.end_date) }}</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- Blocked -->
|
||||
<Card v-if="blockedHUs.length > 0" class="border-l-3 border-l-purple-500">
|
||||
<CardHeader class="pb-2">
|
||||
<CardTitle class="text-xs font-medium flex items-center gap-2 text-purple-600">
|
||||
<AlertTriangle class="size-3.5" />
|
||||
Bloqueadas ({{ blockedHUs.length }})
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-1">
|
||||
<div v-for="hu in blockedHUs" :key="hu.id" class="flex items-center gap-2 text-xs py-1 border-b last:border-0">
|
||||
<span class="font-mono text-[10px] text-muted-foreground w-16">{{ hu.code || `#${hu.id}` }}</span>
|
||||
<span class="flex-1 truncate">{{ hu._cleanTitle || hu.title }}</span>
|
||||
<Badge variant="destructive" class="text-[10px]">Bloqueada</Badge>
|
||||
<Badge v-if="hu.has_impairment" variant="outline" class="text-[10px]">Impedimento</Badge>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,154 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import { useEditor, EditorContent } from '@tiptap/vue-3'
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
import Placeholder from '@tiptap/extension-placeholder'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Bold, Italic, List, ListOrdered, Code, Heading, Quote, Undo, Redo } from 'lucide-vue-next'
|
||||
|
||||
const props = defineProps<{
|
||||
open: boolean
|
||||
title: string
|
||||
content: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:open': [value: boolean]
|
||||
save: [content: string]
|
||||
reset: []
|
||||
}>()
|
||||
|
||||
const editor = useEditor({
|
||||
content: props.content,
|
||||
extensions: [
|
||||
StarterKit.configure({
|
||||
heading: { levels: [1, 2, 3] },
|
||||
}),
|
||||
Placeholder.configure({ placeholder: 'Editá el prompt...' }),
|
||||
],
|
||||
onUpdate: () => {
|
||||
currentContent.value = editor.value?.getHTML() || ''
|
||||
},
|
||||
})
|
||||
|
||||
const currentContent = ref(props.content)
|
||||
|
||||
watch(() => props.content, (val) => {
|
||||
currentContent.value = val
|
||||
editor.value?.commands.setContent(val)
|
||||
})
|
||||
|
||||
watch(() => props.open, (open) => {
|
||||
if (open) {
|
||||
setTimeout(() => editor.value?.commands.setContent(props.content), 50)
|
||||
}
|
||||
})
|
||||
|
||||
function handleSave() {
|
||||
const html = editor.value?.getHTML() || ''
|
||||
// Convert HTML to plain text for storage (los prompts son texto plano)
|
||||
const text = html
|
||||
.replace(/<p>/g, '')
|
||||
.replace(/<\/p>/g, '\n')
|
||||
.replace(/<br\s*\/?>/g, '\n')
|
||||
.replace(/<[^>]*>/g, '')
|
||||
.replace(/ /g, ' ')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/\n{3,}/g, '\n\n')
|
||||
.trim()
|
||||
emit('save', text)
|
||||
emit('update:open', false)
|
||||
}
|
||||
|
||||
function toggleBold() { editor.value?.chain().focus().toggleBold().run() }
|
||||
function toggleItalic() { editor.value?.chain().focus().toggleItalic().run() }
|
||||
function toggleBulletList() { editor.value?.chain().focus().toggleBulletList().run() }
|
||||
function toggleOrderedList() { editor.value?.chain().focus().toggleOrderedList().run() }
|
||||
function toggleCode() { editor.value?.chain().focus().toggleCodeBlock().run() }
|
||||
function toggleHeading(level: 1 | 2 | 3) { editor.value?.chain().focus().toggleHeading({ level }).run() }
|
||||
function toggleBlockquote() { editor.value?.chain().focus().toggleBlockquote().run() }
|
||||
function undo() { editor.value?.chain().focus().undo().run() }
|
||||
function redo() { editor.value?.chain().focus().redo().run() }
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Dialog :open="open" @update:open="emit('update:open', $event)">
|
||||
<DialogContent class="sm:max-w-[800px] max-h-[85vh] flex flex-col">
|
||||
<DialogHeader>
|
||||
<DialogTitle class="text-sm">{{ title }}</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<div class="flex items-center gap-0.5 p-1 rounded-lg bg-muted/50 border mb-2 flex-wrap">
|
||||
<button class="size-7 flex items-center justify-center rounded hover:bg-muted transition-colors" @click="toggleBold" title="Negrita"><Bold class="size-3.5" /></button>
|
||||
<button class="size-7 flex items-center justify-center rounded hover:bg-muted transition-colors" @click="toggleItalic" title="Cursiva"><Italic class="size-3.5" /></button>
|
||||
<span class="w-px h-5 bg-border mx-0.5" />
|
||||
<button class="size-7 flex items-center justify-center rounded hover:bg-muted transition-colors" @click="toggleHeading(1)" title="H1"><Heading class="size-3.5" /><sup class="text-[8px]">1</sup></button>
|
||||
<button class="size-7 flex items-center justify-center rounded hover:bg-muted transition-colors" @click="toggleHeading(2)" title="H2"><Heading class="size-3.5" /><sup class="text-[8px]">2</sup></button>
|
||||
<button class="size-7 flex items-center justify-center rounded hover:bg-muted transition-colors" @click="toggleHeading(3)" title="H3"><Heading class="size-3.5" /><sup class="text-[8px]">3</sup></button>
|
||||
<span class="w-px h-5 bg-border mx-0.5" />
|
||||
<button class="size-7 flex items-center justify-center rounded hover:bg-muted transition-colors" @click="toggleBulletList" title="Lista"><List class="size-3.5" /></button>
|
||||
<button class="size-7 flex items-center justify-center rounded hover:bg-muted transition-colors" @click="toggleOrderedList" title="Lista numerada"><ListOrdered class="size-3.5" /></button>
|
||||
<span class="w-px h-5 bg-border mx-0.5" />
|
||||
<button class="size-7 flex items-center justify-center rounded hover:bg-muted transition-colors" @click="toggleCode" title="Código"><Code class="size-3.5" /></button>
|
||||
<button class="size-7 flex items-center justify-center rounded hover:bg-muted transition-colors" @click="toggleBlockquote" title="Cita"><Quote class="size-3.5" /></button>
|
||||
<span class="w-px h-5 bg-border mx-0.5" />
|
||||
<button class="size-7 flex items-center justify-center rounded hover:bg-muted transition-colors" @click="undo" title="Deshacer"><Undo class="size-3.5" /></button>
|
||||
<button class="size-7 flex items-center justify-center rounded hover:bg-muted transition-colors" @click="redo" title="Rehacer"><Redo class="size-3.5" /></button>
|
||||
</div>
|
||||
|
||||
<!-- Editor -->
|
||||
<div class="flex-1 overflow-y-auto min-h-[300px] rounded-lg border p-4 text-sm">
|
||||
<EditorContent :editor="editor" class="prose prose-sm dark:prose-invert max-w-none focus:outline-none" />
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="flex items-center justify-between pt-3 border-t mt-2">
|
||||
<Button size="sm" variant="ghost" class="text-xs text-muted-foreground" @click="emit('reset')">
|
||||
Restaurar default
|
||||
</Button>
|
||||
<div class="flex gap-2">
|
||||
<Button size="sm" variant="outline" class="text-xs" @click="emit('update:open', false)">Cancelar</Button>
|
||||
<Button size="sm" class="text-xs" @click="handleSave">Guardar</Button>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.tiptap {
|
||||
outline: none;
|
||||
min-height: 250px;
|
||||
}
|
||||
.tiptap p.is-editor-empty:first-child::before {
|
||||
color: hsl(var(--muted-foreground));
|
||||
content: attr(data-placeholder);
|
||||
float: left;
|
||||
height: 0;
|
||||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
.tiptap pre {
|
||||
background: hsl(var(--muted));
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.75rem 1rem;
|
||||
font-size: 0.875rem;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.tiptap blockquote {
|
||||
border-left: 3px solid hsl(var(--border));
|
||||
padding-left: 1rem;
|
||||
margin: 0.5rem 0;
|
||||
color: hsl(var(--muted-foreground));
|
||||
}
|
||||
.tiptap ul, .tiptap ol {
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
.tiptap h1 { font-size: 1.25rem; font-weight: 700; margin: 0.75rem 0 0.5rem; }
|
||||
.tiptap h2 { font-size: 1.1rem; font-weight: 600; margin: 0.5rem 0 0.25rem; }
|
||||
.tiptap h3 { font-size: 1rem; font-weight: 600; margin: 0.5rem 0 0.25rem; }
|
||||
</style>
|
||||
@@ -0,0 +1,130 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, onMounted } from 'vue'
|
||||
import { useWorkItemsStore } from '@/stores/workitems'
|
||||
import { calculateSCurve, type CurvePoint, type SCurveData } from '@/services/s-curve'
|
||||
import { getBlockers } from '@/services/blocker-log'
|
||||
|
||||
const props = defineProps<{ projectId: number; compact?: boolean }>()
|
||||
|
||||
const workItems = useWorkItemsStore()
|
||||
const loading = ref(true)
|
||||
const sCurve = ref<SCurveData | null>(null)
|
||||
|
||||
async function load() {
|
||||
loading.value = true
|
||||
try {
|
||||
const blockers = await getBlockers(props.projectId)
|
||||
sCurve.value = calculateSCurve(workItems.userStories, blockers)
|
||||
} catch (e) {
|
||||
console.error('[Alpha] S-Curve error:', e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(load)
|
||||
watch(() => workItems.userStories.length, load)
|
||||
|
||||
const CHART_W = 400
|
||||
const CHART_H = 150
|
||||
const PAD = { top: 10, right: 10, bottom: 20, left: 35 }
|
||||
|
||||
function buildAreaPath(points: CurvePoint[], maxSp: number, totalDays: number): string {
|
||||
if (points.length === 0) return ''
|
||||
const w = CHART_W - PAD.left - PAD.right
|
||||
const h = CHART_H - PAD.top - PAD.bottom
|
||||
const firstDate = points[0].date
|
||||
const line = points.map((p, i) => {
|
||||
const dx = dayOffset(p.date, firstDate)
|
||||
const x = PAD.left + (dx / totalDays) * w
|
||||
const y = PAD.top + h - (p.cumulative / maxSp) * h
|
||||
return `${i === 0 ? 'M' : 'L'}${x},${y}`
|
||||
}).join(' ')
|
||||
// Close the area for fill
|
||||
const last = points[points.length - 1]
|
||||
const lastDx = dayOffset(last.date, firstDate)
|
||||
const lastX = PAD.left + (lastDx / totalDays) * w
|
||||
const bottomY = PAD.top + h
|
||||
return `${line} L${lastX},${bottomY} L${PAD.left},${bottomY} Z`
|
||||
}
|
||||
|
||||
function dayOffset(date: string, from: string): number {
|
||||
return Math.max(0, Math.ceil((new Date(date).getTime() - new Date(from).getTime()) / (1000 * 60 * 60 * 24)))
|
||||
}
|
||||
|
||||
const planned = computed(() => sCurve.value?.planned || [])
|
||||
const actual = computed(() => sCurve.value?.actual || [])
|
||||
const metrics = computed(() => sCurve.value?.metrics)
|
||||
|
||||
const maxSp = computed(() => {
|
||||
const all = [...planned.value, ...actual.value]
|
||||
return Math.max(...all.map(p => p.cumulative), 1)
|
||||
})
|
||||
|
||||
const totalDays = computed(() => {
|
||||
const all = [...planned.value, ...actual.value]
|
||||
if (all.length < 2) return 30
|
||||
const first = new Date(all[0].date)
|
||||
const last = new Date(all[all.length - 1].date)
|
||||
return Math.max(1, Math.ceil((last.getTime() - first.getTime()) / (1000 * 60 * 60 * 24)))
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="loading" class="text-[10px] text-muted-foreground text-center py-4">Cargando...</div>
|
||||
<div v-else-if="planned.length === 0" class="text-[10px] text-muted-foreground text-center py-4">Sin datos</div>
|
||||
<div v-else class="space-y-2">
|
||||
<!-- Mini metrics row for compact -->
|
||||
<div v-if="metrics && compact" class="flex items-center gap-2 text-[10px] text-muted-foreground px-1">
|
||||
<span class="font-mono font-bold text-foreground">{{ metrics.totalSpPlanned }} SP</span>
|
||||
<span class="text-muted-foreground/50">|</span>
|
||||
<span :class="metrics.spi >= 1 ? 'text-green-500' : 'text-red-500'">SPI {{ metrics.spi }}</span>
|
||||
</div>
|
||||
|
||||
<!-- SVG Chart -->
|
||||
<svg :viewBox="`0 0 ${CHART_W} ${CHART_H}`" class="w-full h-auto">
|
||||
<defs>
|
||||
<linearGradient id="planGrad" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#3b82f6" stop-opacity="0.3" />
|
||||
<stop offset="100%" stop-color="#3b82f6" stop-opacity="0.02" />
|
||||
</linearGradient>
|
||||
<linearGradient id="actualGrad" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#22c55e" stop-opacity="0.25" />
|
||||
<stop offset="100%" stop-color="#22c55e" stop-opacity="0.02" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Grid -->
|
||||
<line v-for="i in 3" :key="i"
|
||||
:x1="PAD.left" :y1="PAD.top + ((i-1)/2) * (CHART_H - PAD.top - PAD.bottom)"
|
||||
:x2="CHART_W - PAD.right" :y2="PAD.top + ((i-1)/2) * (CHART_H - PAD.top - PAD.bottom)"
|
||||
stroke="currentColor" stroke-opacity="0.08" stroke-dasharray="3,3"
|
||||
/>
|
||||
|
||||
<!-- Area fills -->
|
||||
<path :d="buildAreaPath(planned, maxSp, totalDays)" fill="url(#planGrad)" />
|
||||
<path :d="buildAreaPath(actual, maxSp, totalDays)" fill="url(#actualGrad)" />
|
||||
|
||||
<!-- Lines -->
|
||||
<path :d="buildAreaPath(planned, maxSp, totalDays).replace(/Z$/, '')" fill="none" stroke="#3b82f6" stroke-width="1.5" />
|
||||
<path :d="buildAreaPath(actual, maxSp, totalDays).replace(/Z$/, '')" fill="none" stroke="#22c55e" stroke-width="1.5" />
|
||||
|
||||
<!-- Y labels -->
|
||||
<text v-for="i in 3" :key="'y'+i"
|
||||
:x="PAD.left - 4"
|
||||
:y="PAD.top + ((i-1)/2) * (CHART_H - PAD.top - PAD.bottom) + 3"
|
||||
text-anchor="end" class="fill-muted-foreground text-[8px]"
|
||||
>{{ Math.round(maxSp * (i-1) / 2) }}</text>
|
||||
|
||||
<!-- X labels -->
|
||||
<text :x="PAD.left" :y="CHART_H - 4" text-anchor="middle" class="fill-muted-foreground text-[8px]">{{ planned[0]?.date?.slice(5) || '' }}</text>
|
||||
<text :x="CHART_W - PAD.right" :y="CHART_H - 4" text-anchor="middle" class="fill-muted-foreground text-[8px]">{{ planned[planned.length - 1]?.date?.slice(5) || '' }}</text>
|
||||
|
||||
<!-- Mini legend -->
|
||||
<circle cx="20" cy="8" r="3" fill="#3b82f6" />
|
||||
<text x="27" y="12" class="fill-muted-foreground text-[8px]">Plan</text>
|
||||
<circle cx="65" cy="8" r="3" fill="#22c55e" />
|
||||
<text x="72" y="12" class="fill-muted-foreground text-[8px]">Real</text>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
@@ -3,7 +3,6 @@ import { ref, computed } from "vue"
|
||||
import type { ChartConfig } from "@/components/ui/chart"
|
||||
|
||||
// import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts"
|
||||
import { VisArea, VisAxis, VisLine, VisXYContainer } from "@unovis/vue"
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
import db from '@/services/db'
|
||||
|
||||
export interface BlockerRecord {
|
||||
id?: number
|
||||
projectId: number
|
||||
huId?: number
|
||||
huTitle?: string
|
||||
date: string
|
||||
category: string
|
||||
description: string
|
||||
spAffected: number
|
||||
resolvedAt?: string
|
||||
resolution?: string
|
||||
timeLostHours: number
|
||||
createdBy?: string
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export type BlockerCategory = 'client' | 'pm' | 'tech' | 'external' | 'other'
|
||||
|
||||
const CATEGORY_LABELS: Record<BlockerCategory, string> = {
|
||||
client: 'Cliente',
|
||||
pm: 'Lambda',
|
||||
tech: 'Técnico',
|
||||
external: 'Terceros',
|
||||
other: 'Otro',
|
||||
}
|
||||
|
||||
const CATEGORY_COLORS: Record<BlockerCategory, string> = {
|
||||
client: 'text-orange-600 bg-orange-100 dark:bg-orange-900/30 dark:text-orange-400',
|
||||
pm: 'text-blue-600 bg-blue-100 dark:bg-blue-900/30 dark:text-blue-400',
|
||||
tech: 'text-red-600 bg-red-100 dark:bg-red-900/30 dark:text-red-400',
|
||||
external: 'text-purple-600 bg-purple-100 dark:bg-purple-900/30 dark:text-purple-400',
|
||||
other: 'text-gray-600 bg-gray-100 dark:bg-gray-900/30 dark:text-gray-400',
|
||||
}
|
||||
|
||||
export function getBlockerCategoryLabel(cat: BlockerCategory): string {
|
||||
return CATEGORY_LABELS[cat] || cat
|
||||
}
|
||||
|
||||
export function getBlockerCategoryColor(cat: BlockerCategory): string {
|
||||
return CATEGORY_COLORS[cat] || ''
|
||||
}
|
||||
|
||||
export const BLOCKER_CATEGORIES: BlockerCategory[] = ['client', 'pm', 'tech', 'external', 'other']
|
||||
|
||||
export async function saveBlocker(blocker: BlockerRecord): Promise<number> {
|
||||
if (blocker.id) {
|
||||
await (db as any).table('blockers').put(blocker)
|
||||
return blocker.id
|
||||
}
|
||||
return (db as any).table('blockers').add(blocker)
|
||||
}
|
||||
|
||||
export async function getBlockers(projectId: number): Promise<BlockerRecord[]> {
|
||||
return (db as any).table('blockers').where('projectId').equals(projectId).toArray()
|
||||
}
|
||||
|
||||
export async function getActiveBlockers(projectId: number): Promise<BlockerRecord[]> {
|
||||
const all = await getBlockers(projectId)
|
||||
return all.filter(b => !b.resolvedAt)
|
||||
}
|
||||
|
||||
export async function getBlockersByCategory(projectId: number): Promise<Record<BlockerCategory, BlockerRecord[]>> {
|
||||
const all = await getBlockers(projectId)
|
||||
const grouped: Record<string, BlockerRecord[]> = {}
|
||||
for (const b of all) {
|
||||
const cat = b.category || 'other'
|
||||
if (!grouped[cat]) grouped[cat] = []
|
||||
grouped[cat].push(b)
|
||||
}
|
||||
return grouped as Record<BlockerCategory, BlockerRecord[]>
|
||||
}
|
||||
|
||||
export async function resolveBlocker(id: number, resolution: string, resolvedAt: string): Promise<void> {
|
||||
const blocker = await (db as any).table('blockers').get(id)
|
||||
if (blocker) {
|
||||
blocker.resolvedAt = resolvedAt
|
||||
blocker.resolution = resolution
|
||||
await (db as any).table('blockers').put(blocker)
|
||||
}
|
||||
}
|
||||
|
||||
export async function getBlockerStats(projectId: number): Promise<{
|
||||
totalBlocks: number
|
||||
activeBlocks: number
|
||||
totalHoursLost: number
|
||||
byCategory: Record<string, { count: number; hours: number }>
|
||||
}> {
|
||||
const all = await getBlockers(projectId)
|
||||
const active = all.filter(b => !b.resolvedAt)
|
||||
const byCategory: Record<string, { count: number; hours: number }> = {}
|
||||
|
||||
for (const b of all) {
|
||||
const cat = b.category || 'other'
|
||||
if (!byCategory[cat]) byCategory[cat] = { count: 0, hours: 0 }
|
||||
byCategory[cat].count++
|
||||
byCategory[cat].hours += b.timeLostHours || 0
|
||||
}
|
||||
|
||||
return {
|
||||
totalBlocks: all.length,
|
||||
activeBlocks: active.length,
|
||||
totalHoursLost: all.reduce((s, b) => s + (b.timeLostHours || 0), 0),
|
||||
byCategory,
|
||||
}
|
||||
}
|
||||
+19
-1
@@ -135,6 +135,22 @@ export interface PromptRecord {
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface DexieBlocker {
|
||||
id?: number
|
||||
projectId: number
|
||||
huId?: number
|
||||
huTitle?: string
|
||||
date: string
|
||||
category: string
|
||||
description: string
|
||||
spAffected: number
|
||||
resolvedAt?: string
|
||||
resolution?: string
|
||||
timeLostHours: number
|
||||
createdBy?: string
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
const db = new Dexie('alpha-core') as Dexie & {
|
||||
settings: Dexie.Table<SettingEntry, string>
|
||||
project_docs: Dexie.Table<ProjectDocRecord, number>
|
||||
@@ -149,9 +165,10 @@ const db = new Dexie('alpha-core') as Dexie & {
|
||||
cell_members: Dexie.Table<CellMemberRecord, string>
|
||||
user_stories: Dexie.Table<DexieUserStory, number>
|
||||
prompts: Dexie.Table<PromptRecord, string>
|
||||
blockers: Dexie.Table<DexieBlocker, number>
|
||||
}
|
||||
|
||||
db.version(9).stores({
|
||||
db.version(10).stores({
|
||||
settings: '&key',
|
||||
project_docs: '&projectId, projectName, updatedAt',
|
||||
sessions: '++id, projectId, date',
|
||||
@@ -165,6 +182,7 @@ db.version(9).stores({
|
||||
cell_members: '[cellId+userId], cellId, userId',
|
||||
user_stories: '&id, initiative_id',
|
||||
prompts: '&key',
|
||||
blockers: '++id, projectId, category',
|
||||
})
|
||||
|
||||
export default db
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
import { parseHierarchy, buildFullTitle, buildHierarchyPath, type ItemType } from '@/services/hierarchy'
|
||||
import type { AnalysisEpic, AnalysisHU } from '@/services/project-analyzer'
|
||||
import type { EnrichedEpic, EnrichedUserStory } from '@/stores/workitems'
|
||||
|
||||
export interface MaxCounters {
|
||||
epicMax: number
|
||||
byEpic: Map<string, Record<string, number>>
|
||||
}
|
||||
|
||||
const ITEM_TYPE_MAP: Record<string, ItemType> = {
|
||||
feature: 'F',
|
||||
task: 'T',
|
||||
us: 'U',
|
||||
bug: 'B',
|
||||
spike: 'S',
|
||||
}
|
||||
|
||||
/**
|
||||
* Analiza épicas e ítems existentes para determinar el próximo número disponible
|
||||
* para cada nivel jerárquico.
|
||||
*/
|
||||
export function analyzeExisting(
|
||||
epics: EnrichedEpic[],
|
||||
items: EnrichedUserStory[],
|
||||
): MaxCounters {
|
||||
let epicMax = 0
|
||||
const byEpic = new Map<string, Record<string, number>>()
|
||||
|
||||
function updateCounter(epicCode: string, type: string, num: number) {
|
||||
if (!byEpic.has(epicCode)) byEpic.set(epicCode, {})
|
||||
const map = byEpic.get(epicCode)!
|
||||
const current = map[type] || 0
|
||||
if (num > current) map[type] = num
|
||||
}
|
||||
|
||||
for (const epic of epics) {
|
||||
const h = parseHierarchy(epic._cleanName || epic.name || epic.title || '')
|
||||
if (!h) {
|
||||
// Si no tiene código jerárquico, asumimos que usa el id numérico de KAPPA
|
||||
const fakeH = parseHierarchy(buildFullTitle('', Math.max(epicMax, 1)))
|
||||
if (fakeH) {
|
||||
const code = fakeH.epicCode
|
||||
if (code) {
|
||||
const num = parseInt(code.slice(1), 10)
|
||||
if (num > epicMax) epicMax = num
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
const epicItem = h.items.find(i => i.type === 'E')
|
||||
if (epicItem) {
|
||||
const code = `E${String(epicItem.number).padStart(2, '0')}`
|
||||
if (epicItem.number > epicMax) epicMax = epicItem.number
|
||||
}
|
||||
}
|
||||
|
||||
for (const item of items) {
|
||||
const h = parseHierarchy(item._cleanTitle || item.title || '')
|
||||
if (!h) continue
|
||||
const epicItem = h.items.find(i => i.type === 'E')
|
||||
if (!epicItem) continue
|
||||
const epicCode = `E${String(epicItem.number).padStart(2, '0')}`
|
||||
|
||||
for (const seg of h.items) {
|
||||
if (seg.type === 'E') continue
|
||||
updateCounter(epicCode, seg.type, seg.number)
|
||||
}
|
||||
}
|
||||
|
||||
return { epicMax, byEpic }
|
||||
}
|
||||
|
||||
/**
|
||||
* Asigna códigos jerárquicos a épicas propuestas y devuelve el array
|
||||
* enriquecido con epicCode en metadata.
|
||||
*/
|
||||
export function assignEpicCodes(
|
||||
proposedEpics: AnalysisEpic[],
|
||||
counters: MaxCounters,
|
||||
): (AnalysisEpic & { epicCode: string })[] {
|
||||
let nextEpicNum = counters.epicMax + 1
|
||||
|
||||
return proposedEpics.map(epic => {
|
||||
const epicCode = `E${String(nextEpicNum).padStart(2, '0')}`
|
||||
nextEpicNum++
|
||||
return { ...epic, epicCode }
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Asigna códigos jerárquicos a ítems propuestos dentro de épicas confirmadas.
|
||||
* Requiere que cada HU tenga epicName (nombre de la épica) y type.
|
||||
* Devuelve HUs con title y description enriquecidos con el código.
|
||||
*/
|
||||
export function assignItemCodes(
|
||||
proposedHUs: AnalysisHU[],
|
||||
confirmedEpics: { name: string; epicCode: string }[],
|
||||
counters: MaxCounters,
|
||||
): AnalysisHU[] {
|
||||
const epicNameToCode = new Map<string, string>()
|
||||
for (const e of confirmedEpics) {
|
||||
epicNameToCode.set(e.name.toLowerCase().trim(), e.epicCode)
|
||||
}
|
||||
|
||||
// Clonar counters para no mutar el original
|
||||
const localCounters = new Map<string, Record<string, number>>()
|
||||
for (const [key, val] of counters.byEpic) {
|
||||
localCounters.set(key, { ...val })
|
||||
}
|
||||
|
||||
return proposedHUs.map(hu => {
|
||||
const epicName = (hu.epicName || '').toLowerCase().trim()
|
||||
const epicCode = epicNameToCode.get(epicName) || ''
|
||||
const epicNum = epicCode ? parseInt(epicCode.slice(1), 10) : 0
|
||||
|
||||
const itemType = ITEM_TYPE_MAP[(hu.type || 'feature').toLowerCase()] || 'F'
|
||||
|
||||
// Obtener próximo número para este tipo dentro de la épica
|
||||
const epicMap = localCounters.get(epicCode) || {}
|
||||
const nextNum = (epicMap[itemType] || 0) + 1
|
||||
if (!localCounters.has(epicCode)) localCounters.set(epicCode, {})
|
||||
localCounters.get(epicCode)![itemType] = nextNum
|
||||
|
||||
// Construir título y descripción con código
|
||||
const fullTitle = buildFullTitle(hu.title, epicNum || undefined, itemType, nextNum)
|
||||
const codeTag = buildHierarchyPath(epicNum || undefined, itemType, nextNum)
|
||||
|
||||
const typeLabel: Record<string, string> = {
|
||||
feature: 'Feature', task: 'Tarea', us: 'HU', bug: 'Bug', spike: 'Spike',
|
||||
}
|
||||
|
||||
const huType = (hu.type || 'feature').toLowerCase()
|
||||
const descPrefix = `[Tipo: ${typeLabel[huType] || 'HU'} | Épica: ${epicCode} | Código: ${codeTag}]<br>`
|
||||
const enrichedDesc = hu.description
|
||||
? `${descPrefix}${hu.description}`
|
||||
: descPrefix
|
||||
|
||||
return {
|
||||
...hu,
|
||||
title: fullTitle,
|
||||
description: enrichedDesc,
|
||||
}
|
||||
})
|
||||
}
|
||||
+13
-15
@@ -1,10 +1,9 @@
|
||||
export type ItemType = 'E' | 'F' | 'U' | 'T' | 'B'
|
||||
export type ItemType = 'E' | 'F' | 'U' | 'T' | 'B' | 'S'
|
||||
|
||||
export interface HierarchyInfo {
|
||||
fullPath: string
|
||||
items: { type: ItemType; number: number }[]
|
||||
epicCode: string | null
|
||||
featureCode: string | null
|
||||
itemCode: string | null
|
||||
}
|
||||
|
||||
@@ -14,6 +13,7 @@ const TYPE_LABELS: Record<ItemType, string> = {
|
||||
U: 'HU',
|
||||
T: 'Tarea',
|
||||
B: 'Bug',
|
||||
S: 'Spike',
|
||||
}
|
||||
|
||||
const TYPE_COLORS: Record<ItemType, string> = {
|
||||
@@ -22,6 +22,7 @@ const TYPE_COLORS: Record<ItemType, string> = {
|
||||
U: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400',
|
||||
T: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400',
|
||||
B: 'bg-rose-100 text-rose-700 dark:bg-rose-900/30 dark:text-rose-400',
|
||||
S: 'bg-cyan-100 text-cyan-700 dark:bg-cyan-900/30 dark:text-cyan-400',
|
||||
}
|
||||
|
||||
const TYPE_ICONS: Record<ItemType, string> = {
|
||||
@@ -30,6 +31,7 @@ const TYPE_ICONS: Record<ItemType, string> = {
|
||||
U: '📋',
|
||||
T: '⚙️',
|
||||
B: '🐛',
|
||||
S: '🔬',
|
||||
}
|
||||
|
||||
export function getTypeLabel(type: ItemType): string {
|
||||
@@ -44,7 +46,7 @@ export function getTypeIcon(type: ItemType): string {
|
||||
return TYPE_ICONS[type] || '📄'
|
||||
}
|
||||
|
||||
const HIERARCHY_REGEX = /\[(([EFUTB]\d+)(?:-([EFUTB]\d+))?(?:-([EFUTB]\d+))?)\]/i
|
||||
const HIERARCHY_REGEX = /\[(([EFUTBS]\d+)(?:-([EFUTBS]\d+))?(?:-([EFUTBS]\d+))?)\]/i
|
||||
|
||||
export function parseHierarchy(title: string): HierarchyInfo | null {
|
||||
const match = title.match(HIERARCHY_REGEX)
|
||||
@@ -61,15 +63,12 @@ export function parseHierarchy(title: string): HierarchyInfo | null {
|
||||
}))
|
||||
|
||||
const epic = items.find(i => i.type === 'E')
|
||||
const feature = items.find(i => i.type === 'F')
|
||||
const item = items[items.length - 1]
|
||||
|
||||
return {
|
||||
fullPath,
|
||||
items,
|
||||
epicCode: epic ? `E${String(epic.number).padStart(2, '0')}` : null,
|
||||
featureCode: feature ? `F${String(feature.number).padStart(2, '0')}` : null,
|
||||
itemCode: item ? `${item.type}${String(item.number).padStart(2, '0')}` : null,
|
||||
itemCode: items.length > 1 ? `${items[1].type}${String(items[1].number).padStart(2, '0')}` : null,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,25 +84,24 @@ export function getItemType(title: string): ItemType {
|
||||
|
||||
export function buildHierarchyPath(
|
||||
epicNum?: number,
|
||||
featureNum?: number,
|
||||
itemType?: ItemType,
|
||||
itemNum?: number,
|
||||
): string {
|
||||
const parts: string[] = []
|
||||
if (epicNum !== undefined) parts.push(`E${String(epicNum).padStart(2, '0')}`)
|
||||
if (featureNum !== undefined) parts.push(`F${String(featureNum).padStart(2, '0')}`)
|
||||
if (itemType && itemNum !== undefined) parts.push(`${itemType}${String(itemNum).padStart(2, '0')}`)
|
||||
return parts.length ? `[${parts.join('-')}]` : ''
|
||||
if (epicNum === undefined) return ''
|
||||
const parts: string[] = [`E${String(epicNum).padStart(2, '0')}`]
|
||||
if (itemType && itemNum !== undefined) {
|
||||
parts.push(`${itemType}${String(itemNum).padStart(2, '0')}`)
|
||||
}
|
||||
return `[${parts.join('-')}]`
|
||||
}
|
||||
|
||||
export function buildFullTitle(
|
||||
name: string,
|
||||
epicNum?: number,
|
||||
featureNum?: number,
|
||||
itemType?: ItemType,
|
||||
itemNum?: number,
|
||||
): string {
|
||||
const path = buildHierarchyPath(epicNum, featureNum, itemType, itemNum)
|
||||
const path = buildHierarchyPath(epicNum, itemType, itemNum)
|
||||
return path ? `${path} ${name}` : name
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,19 @@ import { callAI } from '@/services/ai'
|
||||
import { getPrompt } from '@/services/prompts-db'
|
||||
import { getSessionsByProject, getSessionSummary, getProjectState } from '@/services/transcriptions-db'
|
||||
import { saveDraft, createDraftId } from '@/services/hu-drafts-db'
|
||||
import { generateAndSavePlan } from '@/services/qa-analyzer'
|
||||
import type { EnrichedUserStory } from '@/stores/workitems'
|
||||
import { analyzeExisting, assignEpicCodes, assignItemCodes } from '@/services/hierarchy-generator'
|
||||
import type { EnrichedEpic, EnrichedUserStory } from '@/stores/workitems'
|
||||
|
||||
export interface AnalysisHU {
|
||||
title: string
|
||||
description: string
|
||||
acceptance_criteria: string[]
|
||||
priority: string
|
||||
story_points?: number
|
||||
type?: string
|
||||
feature?: string
|
||||
sprint?: number
|
||||
epicName?: string // nombre de la épica a la que pertenece
|
||||
}
|
||||
|
||||
export interface AnalysisEpic {
|
||||
@@ -20,18 +25,18 @@ export interface AnalysisEpic {
|
||||
estimatedEnd?: string
|
||||
}
|
||||
|
||||
interface AnalysisResult {
|
||||
hus: AnalysisHU[]
|
||||
interface AnalysisEpicsResult {
|
||||
epics: AnalysisEpic[]
|
||||
summary: string
|
||||
rationale: string // explicación de por qué estas épicas
|
||||
}
|
||||
|
||||
interface AnalysisHUsResult {
|
||||
hus: AnalysisHU[]
|
||||
summary: string
|
||||
}
|
||||
|
||||
export async function analyzeProject(
|
||||
projectId: number,
|
||||
projectName: string,
|
||||
existingHUs: EnrichedUserStory[],
|
||||
signal?: AbortSignal,
|
||||
): Promise<AnalysisResult> {
|
||||
async function buildProjectContext(projectId: number, projectName: string, existingHUs: EnrichedUserStory[]) {
|
||||
const sessions = await getSessionsByProject(projectId)
|
||||
const state = await getProjectState(projectId)
|
||||
|
||||
@@ -48,9 +53,9 @@ export async function analyzeProject(
|
||||
})
|
||||
}
|
||||
|
||||
const context = {
|
||||
return {
|
||||
projectName,
|
||||
existingHUs: existingHUs.map(h => ({ t: h._cleanTitle || h.title, s: h.status, p: h.priority })),
|
||||
existingHUs: existingHUs.map(h => ({ t: h._cleanTitle || h.title, s: h.status, p: h.priority, e: h._assignedName || '' })),
|
||||
sessions: sessionsWithSummaries.slice(-10).reverse(),
|
||||
projectState: state ? {
|
||||
summary: state.summary?.slice(0, 500),
|
||||
@@ -58,39 +63,172 @@ export async function analyzeProject(
|
||||
tasks: (safeParse<{ status: string }[]>(state.tasks, [])).filter(t => t.status !== 'completada').slice(0, 30),
|
||||
} : null,
|
||||
}
|
||||
}
|
||||
|
||||
const userContent = `Contexto completo del proyecto en JSON:\n${JSON.stringify(context, null, 0)}`
|
||||
// ─── FASE 1: Generar solo Épicas ──────────────────────
|
||||
|
||||
console.log(`[Alpha] Project analysis — ${projectId}, ${existingHUs.length} HUs existentes, ${sessions.length} sesiones`)
|
||||
export async function analyzeProjectEpics(
|
||||
projectId: number,
|
||||
projectName: string,
|
||||
existingHUs: EnrichedUserStory[],
|
||||
existingEpics: string[], // nombres de épicas que ya están en KAPPA
|
||||
signal?: AbortSignal,
|
||||
existingEpicItems?: EnrichedEpic[], // épicas enriquecidas para contar códigos
|
||||
): Promise<AnalysisEpicsResult> {
|
||||
const context = await buildProjectContext(projectId, projectName, existingHUs)
|
||||
|
||||
const userContent = `Contexto del proyecto:\n${JSON.stringify(context, null, 0)}\n\nÉpicas ya existentes en KAPPA: ${existingEpics.join(', ') || 'Ninguna'}`
|
||||
|
||||
console.log(`[Alpha] Phase 1 — Analyzing epics for ${projectName}, ${existingEpics.length} existentes`)
|
||||
|
||||
const systemPrompt = await getPrompt('project_gap')
|
||||
// Pasamos una instrucción adicional para que solo genere épicas en esta fase
|
||||
const phasePrompt = `[FASE 1: SOLO ÉPICAS]
|
||||
Analizá TODO el contexto del proyecto.
|
||||
Identificá las épicas necesarias. Una épica agrupa funcionalidades de un mismo tema (ej: "Módulo de Pagos", "Scrapers", "Dashboard").
|
||||
Revisá si las épicas ya existentes en KAPPA cubren las necesidades.
|
||||
Si una épica ya existe en KAPPA, NO la generes de nuevo.
|
||||
Si el acta de inicio, sesiones o documentación mencionan funcionalidades que no están cubiertas por ninguna épica existente, proponé nuevas épicas.
|
||||
Para cada épica, listá los títulos tentativos de las HUs que pertenecerían a ella (linkedHuTitles).
|
||||
|
||||
${systemPrompt}
|
||||
|
||||
IMPORTANTE: Respondé SOLO con épicas en esta fase. NO generes HUs aún.`
|
||||
|
||||
const content = await callAI(
|
||||
[{ role: 'system', content: systemPrompt }, { role: 'user', content: userContent }],
|
||||
[{ role: 'system', content: phasePrompt }, { role: 'user', content: userContent }],
|
||||
0.3, 8192, signal,
|
||||
)
|
||||
|
||||
try {
|
||||
const jsonStr = extractJSON(content)
|
||||
const result: AnalysisResult = JSON.parse(jsonStr)
|
||||
console.log(`[Alpha] Analysis result: ${result.hus.length} HUs, ${result.epics?.length || 0} épicas`)
|
||||
const parsed = JSON.parse(jsonStr)
|
||||
const rawEpics: AnalysisEpic[] = parsed.epics || parsed.epic || []
|
||||
|
||||
// Asignar códigos jerárquicos (E06, E07...) a las épicas propuestas
|
||||
const counters = analyzeExisting(existingEpicItems || [], existingHUs)
|
||||
const epicsWithCodes = assignEpicCodes(rawEpics, counters)
|
||||
|
||||
const result: AnalysisEpicsResult = {
|
||||
epics: epicsWithCodes,
|
||||
summary: parsed.summary || '',
|
||||
rationale: parsed.rationale || parsed.summary || '',
|
||||
}
|
||||
console.log(`[Alpha] Phase 1 complete: ${result.epics.length} épicas propuestas`)
|
||||
return result
|
||||
} catch (e) {
|
||||
console.error('[Alpha] Failed to parse analysis. Raw:', content)
|
||||
throw new Error('No se pudo procesar el análisis del proyecto')
|
||||
console.error('[Alpha] Failed to parse epics analysis. Raw:', content)
|
||||
throw new Error('No se pudieron generar las épicas')
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveAsDrafts(
|
||||
// ─── FASE 2: Generar HUs dentro de las épicas confirmadas ──
|
||||
|
||||
export async function analyzeProjectHUs(
|
||||
projectId: number,
|
||||
analysis: AnalysisResult,
|
||||
projectName: string,
|
||||
existingHUs: EnrichedUserStory[],
|
||||
confirmedEpics: AnalysisEpic[], // épicas que el usuario aceptó y ya están o serán enviadas a KAPPA
|
||||
signal?: AbortSignal,
|
||||
): Promise<AnalysisHUsResult> {
|
||||
const context = await buildProjectContext(projectId, projectName, existingHUs)
|
||||
|
||||
const epicsDetail = confirmedEpics.map(e =>
|
||||
`- ${e.name}: ${e.description?.slice(0, 200)} (HUs sugeridas: ${e.linkedHuTitles.join(', ')})`
|
||||
).join('\n')
|
||||
|
||||
const userContent = `Contexto del proyecto:\n${JSON.stringify(context, null, 0)}\n\nÉpicas confirmadas:\n${epicsDetail}`
|
||||
|
||||
console.log(`[Alpha] Phase 2 — Generating HUs for ${confirmedEpics.length} epics in ${projectName}`)
|
||||
|
||||
const systemPrompt = await getPrompt('project_gap')
|
||||
const phasePrompt = `[FASE 2: GENERAR HUs]
|
||||
Las siguientes épicas ya están definidas. Tu tarea es generar las HUs (feature, task, US, bug, spike, etc.) que pertenecen a cada épica.
|
||||
Cada HU debe estar vinculada a UNA épica existente.
|
||||
Usá el campo "epicName" para indicar a qué épica pertenece cada HU.
|
||||
No generes HUs duplicadas con las que ya existen en KAPPA.
|
||||
Incluí para cada HU: título, descripción, criterios de aceptación, prioridad, story points, tipo, feature, sprint estimado.
|
||||
|
||||
${systemPrompt}
|
||||
|
||||
IMPORTANTE: Respondé SOLO con HUs en esta fase. NO generes épicas nuevas.`
|
||||
|
||||
const content = await callAI(
|
||||
[{ role: 'system', content: phasePrompt }, { role: 'user', content: userContent }],
|
||||
0.3, 8192, signal,
|
||||
)
|
||||
|
||||
try {
|
||||
const jsonStr = extractJSON(content)
|
||||
const parsed = JSON.parse(jsonStr)
|
||||
const rawHUs: AnalysisHU[] = parsed.hus || []
|
||||
|
||||
// Asignar códigos jerárquicos (E01-F04, E01-T01...) a las HUs propuestas
|
||||
const counters = analyzeExisting([], existingHUs)
|
||||
const epicsWithCodes = confirmedEpics.map(e => ({
|
||||
name: e.name,
|
||||
epicCode: (e as any).epicCode || '',
|
||||
}))
|
||||
const husWithCodes = assignItemCodes(rawHUs, epicsWithCodes, counters)
|
||||
|
||||
const result: AnalysisHUsResult = {
|
||||
hus: husWithCodes,
|
||||
summary: parsed.summary || '',
|
||||
}
|
||||
console.log(`[Alpha] Phase 2 complete: ${result.hus.length} HUs generadas con códigos`)
|
||||
return result
|
||||
} catch (e) {
|
||||
console.error('[Alpha] Failed to parse HUs analysis. Raw:', content)
|
||||
throw new Error('No se pudieron generar las HUs')
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Guardar drafts ───────────────────────────────────
|
||||
|
||||
export async function saveEpicDrafts(
|
||||
projectId: number,
|
||||
epics: AnalysisEpic[],
|
||||
existingHUs: EnrichedUserStory[],
|
||||
sourceSessionId?: number,
|
||||
): Promise<{ saved: number; skipped: number }> {
|
||||
let saved = 0
|
||||
let skipped = 0
|
||||
|
||||
// Guardar HUs
|
||||
for (const hu of analysis.hus || []) {
|
||||
for (const epic of epics) {
|
||||
const epicWithCode = epic as any
|
||||
const epicCode = epicWithCode.epicCode || ''
|
||||
const fullTitle = epicCode ? `[${epicCode}] ${epic.name}` : epic.name
|
||||
|
||||
const normalizedName = epic.name.toLowerCase().trim()
|
||||
const isDuplicate = existingHUs.some(ex => (ex._cleanTitle || ex.title).toLowerCase().trim() === normalizedName)
|
||||
if (isDuplicate) { skipped++; continue }
|
||||
|
||||
await saveDraft({
|
||||
id: createDraftId(), projectId, title: fullTitle,
|
||||
description: epic.description, acceptanceCriteria: '',
|
||||
priority: 'Media', type: 'E',
|
||||
metadata: JSON.stringify({
|
||||
linkedHuTitles: epic.linkedHuTitles,
|
||||
estimatedStart: epic.estimatedStart,
|
||||
estimatedEnd: epic.estimatedEnd,
|
||||
epicCode,
|
||||
}),
|
||||
syncStatus: 'draft', createdAt: new Date().toISOString(),
|
||||
})
|
||||
saved++
|
||||
}
|
||||
|
||||
return { saved, skipped }
|
||||
}
|
||||
|
||||
export async function saveHUDrafts(
|
||||
projectId: number,
|
||||
hus: AnalysisHU[],
|
||||
existingHUs: EnrichedUserStory[],
|
||||
): Promise<{ saved: number; skipped: number }> {
|
||||
let saved = 0
|
||||
let skipped = 0
|
||||
|
||||
for (const hu of hus) {
|
||||
const normalizedTitle = hu.title.toLowerCase().trim()
|
||||
const isDuplicate = existingHUs.some(ex => {
|
||||
const et = (ex._cleanTitle || ex.title).toLowerCase().trim()
|
||||
@@ -102,27 +240,14 @@ export async function saveAsDrafts(
|
||||
await saveDraft({
|
||||
id: draftId, projectId, title: hu.title,
|
||||
description: hu.description, acceptanceCriteria: hu.acceptance_criteria.join('\n'),
|
||||
priority: hu.priority, type: 'U', metadata: '{}',
|
||||
sourceSessionId, syncStatus: 'draft', createdAt: new Date().toISOString(),
|
||||
})
|
||||
// QA plan: fire-and-forget para no bloquear el guardado
|
||||
generateAndSavePlan(projectId, draftId, hu.title, hu.description, hu.acceptance_criteria.join('\n'))
|
||||
.catch(e => console.error(`[Alpha] QA auto-gen failed for ${hu.title}:`, e))
|
||||
saved++
|
||||
}
|
||||
|
||||
// Guardar épicas
|
||||
for (const epic of analysis.epics || []) {
|
||||
const normalizedName = epic.name.toLowerCase().trim()
|
||||
const isDuplicate = existingHUs.some(ex => (ex._cleanTitle || ex.title).toLowerCase().trim() === normalizedName)
|
||||
if (isDuplicate) { skipped++; continue }
|
||||
|
||||
await saveDraft({
|
||||
id: createDraftId(), projectId, title: epic.name,
|
||||
description: epic.description, acceptanceCriteria: '',
|
||||
priority: 'Media', type: 'E',
|
||||
metadata: JSON.stringify({ linkedHuTitles: epic.linkedHuTitles, estimatedStart: epic.estimatedStart, estimatedEnd: epic.estimatedEnd }),
|
||||
sourceSessionId, syncStatus: 'draft', createdAt: new Date().toISOString(),
|
||||
priority: hu.priority, type: hu.type === 'task' ? 'T' : hu.type === 'bug' ? 'B' : hu.type === 'feature' ? 'F' : 'U',
|
||||
metadata: JSON.stringify({
|
||||
epicName: hu.epicName,
|
||||
feature: hu.feature,
|
||||
sprint: hu.sprint,
|
||||
storyPoints: hu.story_points,
|
||||
}),
|
||||
syncStatus: 'draft', createdAt: new Date().toISOString(),
|
||||
})
|
||||
saved++
|
||||
}
|
||||
|
||||
+46
-15
@@ -20,8 +20,9 @@ Reglas:
|
||||
3. Los criterios de aceptación deben ser verificables (condiciones específicas)
|
||||
4. Usa el formato "Como [rol] quiero [funcionalidad] para [beneficio]" cuando sea posible
|
||||
5. Asigna prioridad (Alta/Media/Baja) basada en urgencia implícita
|
||||
6. No inventes información que no esté en la transcripción
|
||||
7. Si el texto no contiene información relevante para HUs, devuelve un arreglo vacío
|
||||
6. Asigna story points donde 1 SP = 1 hora de trabajo estimado
|
||||
7. No inventes información que no esté en la transcripción
|
||||
8. Si el texto no contiene información relevante para HUs, devuelve un arreglo vacío
|
||||
|
||||
Responde SOLO con JSON válido en este formato:
|
||||
{
|
||||
@@ -40,38 +41,68 @@ Responde SOLO con JSON válido en este formato:
|
||||
},
|
||||
project_gap: {
|
||||
label: 'Análisis de brechas del proyecto',
|
||||
content: `Eres un analista funcional experto. Tu tarea es analizar TODO el contexto de un proyecto y generar las Épicas e Historias de Usuario (HUs) que faltan.
|
||||
content: `Eres un analista funcional experto en metodologías ágiles.
|
||||
Trabajás en DOS FASES separadas. Cada fase tiene sus propias instrucciones.
|
||||
|
||||
Reglas:
|
||||
Reglas generales:
|
||||
1. Analizá TODA la información disponible: sesiones, resúmenes, estado del proyecto, HUs existentes
|
||||
2. Identificá requisitos, funcionalidades, mejoras o bugs que NO estén cubiertos
|
||||
3. Agrupá HUs relacionadas en Épicas. Cada épica agrupa funcionalidades de un mismo tema
|
||||
4. Cada HU debe tener: título claro, descripción, criterios de aceptación verificables
|
||||
5. No generes duplicados. Compará con la lista existente
|
||||
6. Priorizá según urgencia implícita (Alta/Media/Baja)
|
||||
7. Si todo ya está cubierto, devolvé arreglos vacíos
|
||||
8. Respondé SOLO con JSON válido
|
||||
3. No generes duplicados. Compará con TODO lo que ya existe
|
||||
4. Respondé SOLO con JSON válido
|
||||
5. Si todo ya está cubierto, devolvé arreglos vacíos
|
||||
|
||||
Formato de respuesta:
|
||||
--- FASE 1: Generar Épicas ---
|
||||
Instrucciones específicas:
|
||||
- Identificá las épicas necesarias agrupando funcionalidades por tema
|
||||
- Revisá si las épicas ya existentes cubren las necesidades
|
||||
- Si una épica ya existe en KAPPA, NO la generes de nuevo
|
||||
- Para cada épica, listá los títulos tentativos de las HUs que pertenecerían a ella (linkedHuTitles)
|
||||
- Incluí un campo "rationale" explicando por qué proponés cada épica
|
||||
- NO generes HUs en esta fase
|
||||
|
||||
Formato de respuesta FASE 1:
|
||||
{
|
||||
"epics": [
|
||||
{
|
||||
"name": "Nombre de la Épica",
|
||||
"name": "Nombre de la Épica (ej: Módulo de Pagos)",
|
||||
"description": "Descripción de la épica",
|
||||
"linkedHuTitles": ["Título HU 1", "Título HU 2"],
|
||||
"estimatedStart": "YYYY-MM-DD",
|
||||
"estimatedEnd": "YYYY-MM-DD"
|
||||
}
|
||||
],
|
||||
"summary": "Resumen del análisis de épicas",
|
||||
"rationale": "Explicación de por qué estas épicas y no otras"
|
||||
}
|
||||
|
||||
--- FASE 2: Generar HUs dentro de Épicas ---
|
||||
Instrucciones específicas:
|
||||
- Las épicas ya están definidas. Generá las HUs que pertenecen a cada una.
|
||||
- NO generes códigos jerárquicos en los títulos. El sistema los asigna después.
|
||||
- Proponé solo el nombre del ítem sin prefijos como [E01-F04].
|
||||
- Cada HU debe tener un campo "epicName" con el NOMBRE de la épica a la que pertenece.
|
||||
- Tipos de HU: feature, task, US (historia de usuario), bug, spike
|
||||
- Incluí: título, descripción, criterios de aceptación, prioridad, story points, tipo, feature, sprint estimado
|
||||
- Story points: 1 SP = 1 hora de trabajo estimado. Una jornada laboral = 8 SP
|
||||
- No generes HUs duplicadas con las existentes
|
||||
- NO generes épicas en esta fase
|
||||
|
||||
Formato de respuesta FASE 2:
|
||||
{
|
||||
"hus": [
|
||||
{
|
||||
"title": "Título de la HU",
|
||||
"title": "Nombre del ítem (sin código jerárquico)",
|
||||
"description": "Descripción detallada",
|
||||
"acceptance_criteria": ["Criterio 1", "Criterio 2"],
|
||||
"priority": "Alta|Media|Baja"
|
||||
"priority": "Alta|Media|Baja",
|
||||
"story_points": 3,
|
||||
"type": "feature|task|bug|spike",
|
||||
"feature": "Nombre de la feature",
|
||||
"sprint": 12,
|
||||
"epicName": "Nombre exacto de la épica a la que pertenece"
|
||||
}
|
||||
],
|
||||
"summary": "Resumen del análisis"
|
||||
"summary": "Resumen de las HUs generadas"
|
||||
}`,
|
||||
},
|
||||
session: {
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
import {
|
||||
Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell,
|
||||
HeadingLevel, AlignmentType, WidthType, ShadingType,
|
||||
} from 'docx'
|
||||
import type { EnrichedUserStory, EnrichedEpic } from '@/stores/workitems'
|
||||
import { getSessionsByProject, getSessionSummary, getProjectState } from '@/services/transcriptions-db'
|
||||
|
||||
export interface ReportData {
|
||||
projectId: number
|
||||
projectName: string
|
||||
epicCount: number
|
||||
huCount: number
|
||||
inProgressCount: number
|
||||
doneCount: number
|
||||
blockedCount: number
|
||||
epics: EnrichedEpic[]
|
||||
hus: EnrichedUserStory[]
|
||||
totalSessions: number
|
||||
metrics: {
|
||||
totalSpPlanned: number
|
||||
totalSpCompleted: number
|
||||
velocityPerWeek: number
|
||||
spi: number
|
||||
estimatedEndDate: string
|
||||
clientBlockedHours: number
|
||||
totalBlockedHours: number
|
||||
}
|
||||
generatedAt: string
|
||||
}
|
||||
|
||||
function cell(text: string, bold = false, shading?: string): TableCell {
|
||||
return new TableCell({
|
||||
children: [new Paragraph({
|
||||
children: [new TextRun({ text, bold, size: '20pt' })],
|
||||
spacing: { before: 40, after: 40 },
|
||||
})],
|
||||
shading: shading ? { type: ShadingType.CLEAR, fill: shading } : undefined,
|
||||
})
|
||||
}
|
||||
|
||||
function headerRow(...headers: string[]): TableRow {
|
||||
return new TableRow({ children: headers.map(h => cell(h, true, 'F2F2F2')) })
|
||||
}
|
||||
|
||||
function dataRow(...values: string[]): TableRow {
|
||||
return new TableRow({ children: values.map(v => cell(v)) })
|
||||
}
|
||||
|
||||
export async function generateReportDocx(data: ReportData): Promise<Blob> {
|
||||
const sessions = await getSessionsByProject(data.projectId)
|
||||
const state = await getProjectState(data.projectId)
|
||||
|
||||
const summary = state?.summary || 'Sin resumen disponible'
|
||||
|
||||
const doc = new Document({
|
||||
sections: [{
|
||||
children: [
|
||||
// ═══════════ TITLE ═══════════
|
||||
new Paragraph({
|
||||
text: `${data.projectName} — Informe de Avance del Proyecto`,
|
||||
heading: HeadingLevel.TITLE,
|
||||
alignment: AlignmentType.CENTER,
|
||||
spacing: { after: 100 },
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { after: 400 },
|
||||
children: [
|
||||
new TextRun({ text: `Fecha de corte: ${data.generatedAt}`, size: '20pt' }),
|
||||
],
|
||||
}),
|
||||
|
||||
// ═══════════ 1. ESTADO GENERAL ═══════════
|
||||
new Paragraph({ text: '1. Estado General', heading: HeadingLevel.HEADING_1, spacing: { before: 400, after: 200 } }),
|
||||
|
||||
new Table({
|
||||
rows: [
|
||||
headerRow('Métrica', 'Valor'),
|
||||
dataRow('Total Épicas', String(data.epicCount)),
|
||||
dataRow('Total HUs', String(data.huCount)),
|
||||
dataRow('En Progreso', String(data.inProgressCount)),
|
||||
dataRow('Completadas', String(data.doneCount)),
|
||||
dataRow('Bloqueadas', String(data.blockedCount)),
|
||||
dataRow('SP Planificados', String(data.metrics.totalSpPlanned)),
|
||||
dataRow('SP Completados', String(data.metrics.totalSpCompleted)),
|
||||
],
|
||||
width: { size: 100, type: WidthType.PERCENTAGE },
|
||||
}),
|
||||
|
||||
// ═══════════ 2. RESUMEN EJECUTIVO ═══════════
|
||||
new Paragraph({ text: '2. Resumen Ejecutivo', heading: HeadingLevel.HEADING_1, spacing: { before: 400, after: 200 } }),
|
||||
new Paragraph({
|
||||
spacing: { after: 200 },
|
||||
children: [new TextRun({ text: summary, size: '20pt' })],
|
||||
}),
|
||||
|
||||
...(data.metrics.totalBlockedHours > 0 ? [
|
||||
new Paragraph({
|
||||
spacing: { before: 100 },
|
||||
children: [new TextRun({
|
||||
text: `⏱ Horas bloqueadas: ${data.metrics.totalBlockedHours}h totales (${data.metrics.clientBlockedHours}h imputables al cliente)`,
|
||||
size: '20pt',
|
||||
bold: true,
|
||||
})],
|
||||
}),
|
||||
] : []),
|
||||
|
||||
// ═══════════ 3. ÉPICAS ═══════════
|
||||
new Paragraph({ text: '3. Épicas', heading: HeadingLevel.HEADING_1, spacing: { before: 400, after: 200 } }),
|
||||
...data.epics.map(epic => new Paragraph({
|
||||
spacing: { before: 80, after: 40 },
|
||||
children: [
|
||||
new TextRun({ text: `📦 ${(epic as any)._epicCode || `EP-${epic.id}`}`, bold: true, size: '20pt' }),
|
||||
new TextRun({ text: ` ${(epic as any)._cleanName || epic.name || epic.title || ''}`, size: '20pt' }),
|
||||
],
|
||||
})),
|
||||
|
||||
// ═══════════ 4. HUs ═══════════
|
||||
new Paragraph({ text: '4. HUs — Resumen', heading: HeadingLevel.HEADING_1, spacing: { before: 400, after: 200 } }),
|
||||
new Table({
|
||||
rows: [
|
||||
headerRow('Código', 'Título', 'Estado', 'Prioridad', 'SP'),
|
||||
...data.hus.slice(0, 50).map(hu => dataRow(
|
||||
hu.code || `#${hu.id}`,
|
||||
(hu._cleanTitle || hu.title).slice(0, 60),
|
||||
hu._statusName || String(hu.status || '—'),
|
||||
String(hu.priority || '—'),
|
||||
String(hu.story_points ?? '—'),
|
||||
)),
|
||||
],
|
||||
width: { size: 100, type: WidthType.PERCENTAGE },
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { before: 100 },
|
||||
children: [new TextRun({ text: `Mostrando 50 de ${data.huCount} HUs`, size: '20pt', italics: true, color: '888888' })],
|
||||
}),
|
||||
|
||||
// ═══════════ 5. HUs BLOQUEADAS ═══════════
|
||||
...(data.blockedCount > 0 ? [
|
||||
new Paragraph({ text: '5. HUs Bloqueadas', heading: HeadingLevel.HEADING_1, spacing: { before: 400, after: 200 } }),
|
||||
...data.hus.filter(h => {
|
||||
const s = String(h.status ?? '').toLowerCase()
|
||||
return ['blocked', 'bloqueado'].includes(s)
|
||||
}).map(hu => new Paragraph({
|
||||
spacing: { before: 60, after: 60 },
|
||||
children: [new TextRun({ text: `⚠ ${hu._cleanTitle || hu.title}`, size: '20pt' })],
|
||||
})),
|
||||
] : []),
|
||||
|
||||
// ═══════════ 6. CURVA S ═══════════
|
||||
new Paragraph({ text: '6. Curva S del Proyecto', heading: HeadingLevel.HEADING_1, spacing: { before: 400, after: 200 } }),
|
||||
new Paragraph({
|
||||
children: [new TextRun({
|
||||
text: `La curva S muestra el avance planificado vs real. SPI actual: ${data.metrics.spi}. Velocidad: ${data.metrics.velocityPerWeek} SP/semana. Proyección de finalización: ${data.metrics.estimatedEndDate || 'N/A'}.`,
|
||||
size: '20pt',
|
||||
})],
|
||||
spacing: { after: 200 },
|
||||
}),
|
||||
|
||||
// ═══════════ 7. CURVA S ═══════════
|
||||
new Paragraph({ text: '7. Carga de Trabajo Actual', heading: HeadingLevel.HEADING_1, spacing: { before: 400, after: 200 } }),
|
||||
new Paragraph({
|
||||
children: [new TextRun({
|
||||
text: `${data.inProgressCount} HUs en progreso · ${data.blockedCount} bloqueadas · ${data.doneCount} completadas de ${data.huCount} totales.`,
|
||||
size: '20pt',
|
||||
})],
|
||||
}),
|
||||
|
||||
// ═══════════ 8. AVANCES DE LA SEMANA ═══════════
|
||||
...(sessions.length > 0 ? [
|
||||
new Paragraph({ text: '8. Avances Registrados', heading: HeadingLevel.HEADING_1, spacing: { before: 400, after: 200 } }),
|
||||
...(await Promise.all(sessions.slice(-10).reverse().map(async (s) => {
|
||||
const sum = await getSessionSummary(s.id!)
|
||||
return new Paragraph({
|
||||
spacing: { before: 60, after: 60 },
|
||||
children: [
|
||||
new TextRun({ text: `📅 ${s.date}`, bold: true, size: '20pt' }),
|
||||
new TextRun({ text: ` — ${s.title}`, size: '20pt' }),
|
||||
...(sum?.summary ? [new TextRun({ text: `\n${sum.summary.slice(0, 300)}`, size: '20pt', italics: true, color: '555555' })] : []),
|
||||
],
|
||||
})
|
||||
}))),
|
||||
] : []),
|
||||
|
||||
// ═══════════ FOOTER ═══════════
|
||||
new Paragraph({
|
||||
spacing: { before: 600 },
|
||||
alignment: AlignmentType.CENTER,
|
||||
children: [new TextRun({
|
||||
text: `Documento generado automáticamente por Alpha — ${data.generatedAt}`,
|
||||
size: '20pt', italics: true, color: '888888',
|
||||
})],
|
||||
}),
|
||||
],
|
||||
}],
|
||||
})
|
||||
|
||||
return await Packer.toBlob(doc)
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
import type { EnrichedUserStory } from '@/stores/workitems'
|
||||
import type { BlockerRecord } from '@/services/blocker-log'
|
||||
|
||||
export interface CurvePoint {
|
||||
date: string // ISO date YYYY-MM-DD
|
||||
cumulative: number // cumulative SP
|
||||
}
|
||||
|
||||
export interface SCurveData {
|
||||
planned: CurvePoint[]
|
||||
actual: CurvePoint[]
|
||||
projection: CurvePoint[]
|
||||
metrics: SCurveMetrics
|
||||
}
|
||||
|
||||
export interface SCurveMetrics {
|
||||
totalSpPlanned: number
|
||||
totalSpCompleted: number
|
||||
velocityPerWeek: number // average SP completed per week
|
||||
weeksElapsed: number
|
||||
weeksRemaining: number // estimated weeks to completion
|
||||
estimatedEndDate: string // projected completion date
|
||||
spi: number // Schedule Performance Index
|
||||
clientBlockedHours: number
|
||||
totalBlockedHours: number
|
||||
}
|
||||
|
||||
const WORKING_HOURS_PER_DAY = 8
|
||||
const WORKING_DAYS_PER_WEEK = 5
|
||||
const SP_PER_WEEK = WORKING_HOURS_PER_DAY * WORKING_DAYS_PER_WEEK // 40
|
||||
|
||||
/**
|
||||
* Calcula la curva planificada: distribuye los SP de cada HU
|
||||
* linealmente entre su start_date y end_date.
|
||||
*/
|
||||
export function calculatePlannedCurve(hus: EnrichedUserStory[]): CurvePoint[] {
|
||||
const points: { date: string; sp: number }[] = []
|
||||
|
||||
for (const hu of hus) {
|
||||
const sp = hu.story_points || 0
|
||||
if (sp <= 0) continue
|
||||
|
||||
// Si no tiene fechas, asumimos que empieza hoy y dura 1 día por SP
|
||||
const start = hu.initial_date || new Date().toISOString().split('T')[0]
|
||||
const end = hu.end_date || calcEndDate(start, sp)
|
||||
|
||||
// Distribuir SP linealmente entre start y end
|
||||
const startD = new Date(start)
|
||||
const endD = new Date(end)
|
||||
const totalDays = Math.max(1, Math.ceil((endD.getTime() - startD.getTime()) / (1000 * 60 * 60 * 24)) + 1)
|
||||
const spPerDay = sp / totalDays
|
||||
|
||||
for (let i = 0; i < totalDays; i++) {
|
||||
const d = new Date(startD)
|
||||
d.setDate(d.getDate() + i)
|
||||
// Skip weekends
|
||||
if (d.getDay() === 0 || d.getDay() === 6) continue
|
||||
const dateStr = d.toISOString().split('T')[0]
|
||||
points.push({ date: dateStr, sp: spPerDay })
|
||||
}
|
||||
}
|
||||
|
||||
// Acumular por fecha
|
||||
return accumulatePoints(points)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcula la curva real desde daily logs (datos de progreso real).
|
||||
* Por ahora, usa el end_date + status para estimar progreso.
|
||||
*/
|
||||
export function calculateActualCurve(hus: EnrichedUserStory[]): CurvePoint[] {
|
||||
const points: { date: string; sp: number }[] = []
|
||||
|
||||
for (const hu of hus) {
|
||||
const sp = hu.story_points || 0
|
||||
if (sp <= 0) continue
|
||||
|
||||
const s = String(hu.status || '').toLowerCase()
|
||||
const isDone = ['done', 'completed', 'closed', 'finalizado', '5', '6', '7', 'qa-client', 'ready to deploy'].includes(s)
|
||||
const isInProgress = ['in_progress', 'doing', 'wip', 'active', 'in progress', 'en progreso', 'true', '2'].includes(s)
|
||||
|
||||
if (isDone && hu.end_date) {
|
||||
points.push({ date: hu.end_date, sp })
|
||||
} else if (isInProgress && hu.end_date) {
|
||||
// 50% completado (estimado)
|
||||
const halfDate = hu.initial_date
|
||||
? midpointDate(hu.initial_date, hu.end_date)
|
||||
: hu.end_date
|
||||
points.push({ date: halfDate, sp: Math.round(sp * 0.5) })
|
||||
}
|
||||
}
|
||||
|
||||
return accumulatePoints(points)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcula la proyección: extiende la curva real usando la velocidad actual.
|
||||
*/
|
||||
export function calculateProjection(
|
||||
actual: CurvePoint[],
|
||||
planned: CurvePoint[],
|
||||
totalSpPlanned: number,
|
||||
totalSpCompleted: number,
|
||||
): { projection: CurvePoint[]; estimatedEndDate: string; weeksRemaining: number } {
|
||||
if (actual.length === 0) {
|
||||
return {
|
||||
projection: [],
|
||||
estimatedEndDate: planned[planned.length - 1]?.date || '',
|
||||
weeksRemaining: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Calcular velocidad semanal
|
||||
const firstActual = new Date(actual[0].date)
|
||||
const lastActual = new Date(actual[actual.length - 1].date)
|
||||
const daysElapsed = Math.max(1, Math.ceil((lastActual.getTime() - firstActual.getTime()) / (1000 * 60 * 60 * 24)))
|
||||
const weeksElapsed = Math.max(1, daysElapsed / WORKING_DAYS_PER_WEEK)
|
||||
const velocityPerWeek = totalSpCompleted / weeksElapsed
|
||||
|
||||
if (velocityPerWeek <= 0) {
|
||||
return { projection: [], estimatedEndDate: '', weeksRemaining: 0 }
|
||||
}
|
||||
|
||||
const remainingSp = totalSpPlanned - totalSpCompleted
|
||||
const weeksRemaining = Math.ceil(remainingSp / velocityPerWeek)
|
||||
const estimatedEnd = new Date(lastActual)
|
||||
estimatedEnd.setDate(estimatedEnd.getDate() + weeksRemaining * WORKING_DAYS_PER_WEEK)
|
||||
|
||||
// Generar puntos de proyección
|
||||
const projection: CurvePoint[] = []
|
||||
const lastCumulative = actual[actual.length - 1]?.cumulative || 0
|
||||
let projectedCumulative = lastCumulative
|
||||
const projDate = new Date(lastActual)
|
||||
|
||||
for (let w = 0; w <= weeksRemaining; w++) {
|
||||
projectedCumulative += velocityPerWeek * WORKING_DAYS_PER_WEEK / 7
|
||||
if (projectedCumulative > totalSpPlanned) projectedCumulative = totalSpPlanned
|
||||
projDate.setDate(projDate.getDate() + 7)
|
||||
projection.push({
|
||||
date: projDate.toISOString().split('T')[0],
|
||||
cumulative: Math.round(projectedCumulative),
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
projection,
|
||||
estimatedEndDate: estimatedEnd.toISOString().split('T')[0],
|
||||
weeksRemaining,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcula todas las métricas de la curva S.
|
||||
*/
|
||||
export function calculateSCurve(hus: EnrichedUserStory[], blockers: BlockerRecord[]): SCurveData {
|
||||
const planned = calculatePlannedCurve(hus)
|
||||
const actual = calculateActualCurve(hus)
|
||||
|
||||
const totalSpPlanned = hus.reduce((s, h) => s + (h.story_points || 0), 0)
|
||||
const totalSpCompleted = actual.length > 0 ? actual[actual.length - 1].cumulative : 0
|
||||
|
||||
const firstActual = actual.length > 0 ? new Date(actual[0].date) : new Date()
|
||||
const lastActual = actual.length > 0 ? new Date(actual[actual.length - 1].date) : new Date()
|
||||
const daysElapsed = Math.max(1, Math.ceil((lastActual.getTime() - firstActual.getTime()) / (1000 * 60 * 60 * 24)))
|
||||
const weeksElapsed = Math.max(0.5, daysElapsed / WORKING_DAYS_PER_WEEK)
|
||||
const velocityPerWeek = weeksElapsed > 0 ? +(totalSpCompleted / weeksElapsed).toFixed(1) : 0
|
||||
|
||||
// SPI = SP completados / SP planeados en el mismo periodo
|
||||
const plannedAtEnd = planned.filter(p => new Date(p.date) <= lastActual)
|
||||
const plannedInPeriod = plannedAtEnd.length > 0 ? plannedAtEnd[plannedAtEnd.length - 1].cumulative : 0
|
||||
const spi = plannedInPeriod > 0 ? +(totalSpCompleted / plannedInPeriod).toFixed(2) : 1
|
||||
|
||||
const { estimatedEndDate, weeksRemaining } = calculateProjection(actual, planned, totalSpPlanned, totalSpCompleted)
|
||||
|
||||
// Estadísticas de bloqueos
|
||||
const clientBlockedHours = blockers
|
||||
.filter(b => b.category === 'client')
|
||||
.reduce((s, b) => s + (b.timeLostHours || 0), 0)
|
||||
const totalBlockedHours = blockers.reduce((s, b) => s + (b.timeLostHours || 0), 0)
|
||||
|
||||
return {
|
||||
planned,
|
||||
actual,
|
||||
projection: [],
|
||||
metrics: {
|
||||
totalSpPlanned,
|
||||
totalSpCompleted,
|
||||
velocityPerWeek,
|
||||
weeksElapsed,
|
||||
weeksRemaining,
|
||||
estimatedEndDate,
|
||||
spi,
|
||||
clientBlockedHours,
|
||||
totalBlockedHours,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Helpers ────────────────────────────────────────
|
||||
|
||||
function accumulatePoints(points: { date: string; sp: number }[]): CurvePoint[] {
|
||||
if (points.length === 0) return []
|
||||
|
||||
const sorted = [...points].sort((a, b) => a.date.localeCompare(b.date))
|
||||
const result: CurvePoint[] = []
|
||||
let cumulative = 0
|
||||
let currentDate = sorted[0].date
|
||||
let daySum = 0
|
||||
|
||||
for (const p of sorted) {
|
||||
if (p.date !== currentDate) {
|
||||
cumulative += Math.round(daySum)
|
||||
result.push({ date: currentDate, cumulative })
|
||||
currentDate = p.date
|
||||
daySum = 0
|
||||
}
|
||||
daySum += p.sp
|
||||
}
|
||||
// Último día
|
||||
cumulative += Math.round(daySum)
|
||||
result.push({ date: currentDate, cumulative })
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
function calcEndDate(start: string, sp: number): string {
|
||||
const d = new Date(start)
|
||||
d.setDate(d.getDate() + sp)
|
||||
return d.toISOString().split('T')[0]
|
||||
}
|
||||
|
||||
function midpointDate(start: string, end: string): string {
|
||||
const s = new Date(start)
|
||||
const e = new Date(end)
|
||||
const mid = new Date((s.getTime() + e.getTime()) / 2)
|
||||
return mid.toISOString().split('T')[0]
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
import { storage } from '@/services/storage'
|
||||
|
||||
const STORAGE_KEY = 'teams_webhook_url'
|
||||
|
||||
export function getWebhookUrl(): string {
|
||||
return storage.get(STORAGE_KEY) || ''
|
||||
}
|
||||
|
||||
export function setWebhookUrl(url: string): void {
|
||||
storage.set(STORAGE_KEY, url)
|
||||
}
|
||||
|
||||
export function hasWebhook(): boolean {
|
||||
return !!getWebhookUrl()
|
||||
}
|
||||
|
||||
interface TeamsMessage {
|
||||
title?: string
|
||||
text: string
|
||||
themeColor?: string
|
||||
sections?: {
|
||||
activityTitle?: string
|
||||
activitySubtitle?: string
|
||||
facts?: { name: string; value: string }[]
|
||||
text?: string
|
||||
}[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Envía un mensaje a Teams via webhook.
|
||||
* Formato: https://learn.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook
|
||||
* Soporta tanto Tauri (plugin-http) como browser (fetch).
|
||||
*/
|
||||
export async function sendToTeams(message: TeamsMessage): Promise<boolean> {
|
||||
const webhookUrl = getWebhookUrl()
|
||||
if (!webhookUrl) {
|
||||
console.warn('[Teams] No hay webhook configurado')
|
||||
return false
|
||||
}
|
||||
|
||||
const payload = {
|
||||
'@type': 'MessageCard',
|
||||
'@context': 'http://schema.org/extensions',
|
||||
summary: message.title || message.text.slice(0, 50),
|
||||
title: message.title,
|
||||
text: message.text,
|
||||
themeColor: message.themeColor || '0078D4',
|
||||
sections: message.sections || [],
|
||||
}
|
||||
|
||||
try {
|
||||
// Intentar usar Tauri plugin HTTP primero
|
||||
if (typeof window !== 'undefined' && (window as any).__TAURI_INTERNALS__) {
|
||||
const { fetch } = await import('@tauri-apps/plugin-http')
|
||||
const res = await fetch(webhookUrl, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
if (!res.ok) {
|
||||
console.error(`[Teams] Error HTTP ${res.status}`)
|
||||
return false
|
||||
}
|
||||
console.log('[Teams] Mensaje enviado via Tauri plugin')
|
||||
return true
|
||||
}
|
||||
|
||||
// Fallback browser fetch
|
||||
const res = await fetch(webhookUrl, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
if (!res.ok) {
|
||||
console.error(`[Teams] Error HTTP ${res.status}`)
|
||||
return false
|
||||
}
|
||||
console.log('[Teams] Mensaje enviado via browser fetch')
|
||||
return true
|
||||
} catch (e: any) {
|
||||
console.error('[Teams] Error al enviar mensaje:', e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Envía una notificación de HU creada.
|
||||
*/
|
||||
export async function notifyHUCreated(projectName: string, huTitle: string, huUrl?: string): Promise<boolean> {
|
||||
return sendToTeams({
|
||||
title: '🆕 Nueva HU creada',
|
||||
text: `**Proyecto:** ${projectName}`,
|
||||
themeColor: '0078D4',
|
||||
sections: [{
|
||||
facts: [
|
||||
{ name: 'HU', value: huTitle },
|
||||
{ name: 'Proyecto', value: projectName },
|
||||
{ name: 'URL', value: huUrl || window.location.href },
|
||||
],
|
||||
}],
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Envía una notificación de bloqueo/impedimento.
|
||||
*/
|
||||
export async function notifyBlockerLogged(
|
||||
projectName: string,
|
||||
huTitle: string,
|
||||
category: string,
|
||||
description: string,
|
||||
hoursLost: number,
|
||||
): Promise<boolean> {
|
||||
return sendToTeams({
|
||||
title: '⛔ Bloqueo registrado',
|
||||
text: `**Proyecto:** ${projectName}`,
|
||||
themeColor: 'FF4444',
|
||||
sections: [{
|
||||
facts: [
|
||||
{ name: 'HU', value: huTitle },
|
||||
{ name: 'Categoría', value: category },
|
||||
{ name: 'Descripción', value: description },
|
||||
{ name: 'Horas perdidas', value: `${hoursLost}h` },
|
||||
],
|
||||
}],
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Envía un mensaje de prueba.
|
||||
*/
|
||||
export async function sendTestMessage(): Promise<boolean> {
|
||||
return sendToTeams({
|
||||
title: '🔔 Prueba de integración',
|
||||
text: 'Este es un mensaje de prueba desde **Alpha**.\n\nSi ves esto, la integración con Teams funciona correctamente.',
|
||||
themeColor: '00FF00',
|
||||
})
|
||||
}
|
||||
@@ -19,12 +19,15 @@ export interface EnrichedUserStory extends KappaUserStory {
|
||||
_assignedName: string
|
||||
_statusName: string
|
||||
_assignedEmployeeId: number | null
|
||||
_epicCode: string | null
|
||||
_itemCode: string | null
|
||||
}
|
||||
|
||||
export interface EnrichedEpic extends KappaEpicDevelopment {
|
||||
_itemType: ItemType
|
||||
_hierarchyPath: string | null
|
||||
_cleanName: string
|
||||
_epicCode: string | null
|
||||
}
|
||||
|
||||
export const useWorkItemsStore = defineStore('workitems', () => {
|
||||
@@ -61,6 +64,7 @@ export const useWorkItemsStore = defineStore('workitems', () => {
|
||||
_itemType: h?.items[h.items.length - 1]?.type || 'E',
|
||||
_hierarchyPath: h?.fullPath ?? null,
|
||||
_cleanName: h ? stripHierarchy(title) : title,
|
||||
_epicCode: h?.epicCode ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +93,7 @@ export const useWorkItemsStore = defineStore('workitems', () => {
|
||||
return { id: null, name: '', employeeId: null }
|
||||
}
|
||||
|
||||
function enrichHU(hu: { title?: string; id?: number; description?: string | null; status?: string | number | null; status_name?: string | null; priority?: string | number | null; story_points?: number | null; acceptance_criteria?: string | null; criterios_aceptacion?: string | null; assigned_to?: number | null; asignado_a?: number[] | string[] | null; asignado_a_names?: string[] | string | null; assigned_name?: string }, initiativeId: number): EnrichedUserStory {
|
||||
function enrichHU(hu: { title?: string; id?: number; description?: string | null; status?: string | number | null; status_name?: string | null; priority?: string | number | null; story_points?: number | null; end_date?: string | null; sprint?: number | string | null; acceptance_criteria?: string | null; criterios_aceptacion?: string | null; assigned_to?: number | null; asignado_a?: number[] | string[] | null; asignado_a_names?: string[] | string | null; assigned_name?: string }, initiativeId: number): EnrichedUserStory {
|
||||
const h = parseHierarchy(hu.title || '')
|
||||
const rawCriteria = hu.acceptance_criteria || hu.criterios_aceptacion || ''
|
||||
const criteriaList = rawCriteria ? parseQuillList(rawCriteria) : []
|
||||
@@ -100,6 +104,8 @@ export const useWorkItemsStore = defineStore('workitems', () => {
|
||||
status: String(hu.status ?? ''),
|
||||
priority: String(hu.priority ?? ''),
|
||||
story_points: hu.story_points ?? undefined,
|
||||
end_date: hu.end_date || undefined,
|
||||
sprint: hu.sprint != null ? String(hu.sprint) : undefined,
|
||||
description: hu.description || '',
|
||||
acceptance_criteria: rawCriteria,
|
||||
criterios_aceptacion: rawCriteria,
|
||||
@@ -113,6 +119,8 @@ export const useWorkItemsStore = defineStore('workitems', () => {
|
||||
_assignedName: assignedName,
|
||||
_statusName: hu.status_name || resolveStatusName(hu.status),
|
||||
_assignedEmployeeId: employeeId,
|
||||
_epicCode: h?.epicCode ?? null,
|
||||
_itemCode: h?.itemCode ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -48,7 +48,9 @@ export interface KappaUserStory {
|
||||
priority?: string | number
|
||||
initiative: number | string
|
||||
story_points?: number
|
||||
sprint?: string
|
||||
sprint?: number | string
|
||||
end_date?: string
|
||||
initial_date?: string | null
|
||||
created_at?: string
|
||||
assigned_to?: number | null
|
||||
asignado_a?: number[] | string[] | null
|
||||
|
||||
+241
-61
@@ -6,12 +6,15 @@ import { useWorkItemsStore } from '@/stores/workitems'
|
||||
import { useUsersStore } from '@/stores/users'
|
||||
import { storage } from '@/services/storage'
|
||||
import { getTypeLabel, getTypeColor, getTypeIcon } from '@/services/hierarchy'
|
||||
import { Activity, FileText, Layers, Clock, Info, AlertTriangle, Plus, Brain, Sparkles, Loader2, CheckCircle2, XCircle, Send, ChevronDown, Eye } from 'lucide-vue-next'
|
||||
import { Activity, FileText, Layers, Clock, Info, AlertTriangle, Plus, Brain, Sparkles, Loader2, CheckCircle2, XCircle, Send, ChevronDown, Eye, TrendingUp } from 'lucide-vue-next'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'
|
||||
import HuDrafts from '@/components/HuDrafts.vue'
|
||||
import AiProjectChat from '@/components/AiProjectChat.vue'
|
||||
import { analyzeProject, saveAsDrafts } from '@/services/project-analyzer'
|
||||
import PrioritizerCard from '@/components/PrioritizerCard.vue'
|
||||
import SCurveChart from '@/components/SCurveChart.vue'
|
||||
// import { generateReportDocx } from '@/services/report-export' // Sidecar Python pending
|
||||
import { analyzeProjectEpics, analyzeProjectHUs, saveEpicDrafts, saveHUDrafts } from '@/services/project-analyzer'
|
||||
import { getDrafts, deleteDraft, type HuDraftRecord } from '@/services/hu-drafts-db'
|
||||
import { kappa } from '@/services/kappa-api'
|
||||
import { generateAndSavePlan, getQAPlans, type HUQAPlan } from '@/services/qa-analyzer'
|
||||
@@ -40,10 +43,10 @@ const usersStore = useUsersStore()
|
||||
const project = computed(() => projects.selected)
|
||||
|
||||
// ─── Inline notification (funciona en Tauri) ─────────
|
||||
const notification = ref<{ type: 'success' | 'error'; message: string } | null>(null)
|
||||
const notification = ref<{ type: 'success' | 'error' | 'info'; message: string } | null>(null)
|
||||
let notifTimeout: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
function showNotif(type: 'success' | 'error', message: string) {
|
||||
function showNotif(type: 'success' | 'error' | 'info', message: string) {
|
||||
if (notifTimeout) clearTimeout(notifTimeout)
|
||||
notification.value = { type, message }
|
||||
notifTimeout = setTimeout(() => { notification.value = null }, 5000)
|
||||
@@ -154,9 +157,9 @@ function assignedName(hu: { _assignedName?: string; _assignedEmployeeId?: number
|
||||
|
||||
// ─── Priority helpers ─────────────────────────────────
|
||||
const PRIORITY_MAP: Record<string, { label: string; variant: string }> = {
|
||||
'1': { label: 'Alta', variant: 'destructive' },
|
||||
'1': { label: 'Baja', variant: 'secondary' },
|
||||
'2': { label: 'Media', variant: 'default' },
|
||||
'3': { label: 'Baja', variant: 'secondary' },
|
||||
'3': { label: 'Alta', variant: 'destructive' },
|
||||
'alta': { label: 'Alta', variant: 'destructive' },
|
||||
'high': { label: 'Alta', variant: 'destructive' },
|
||||
'critical': { label: 'Crítica', variant: 'destructive' },
|
||||
@@ -270,6 +273,22 @@ function getEpicLinkedHUs(d: HuDraftRecord): string[] {
|
||||
return meta.linkedHuTitles || []
|
||||
} catch { return [] }
|
||||
}
|
||||
|
||||
function getEpicCode(d: HuDraftRecord): string {
|
||||
try {
|
||||
const meta = JSON.parse(d.metadata || '{}')
|
||||
return meta.epicCode || ''
|
||||
} catch { return '' }
|
||||
}
|
||||
|
||||
function getItemCode(d: HuDraftRecord): string {
|
||||
const match = d.title.match(/\[([^\]]+)\]/)
|
||||
return match ? match[1] : ''
|
||||
}
|
||||
|
||||
function stripEpicTitle(title: string): string {
|
||||
return title.replace(/^\[[^\]]+\]\s*/, '')
|
||||
}
|
||||
const drafts = ref<HuDraftRecord[]>([])
|
||||
const pushingDraftId = ref<string | null>(null)
|
||||
|
||||
@@ -289,7 +308,7 @@ async function pushDraft(d: HuDraftRecord) {
|
||||
let endpoint: string, body: string
|
||||
|
||||
if (d.type === 'E') {
|
||||
// Push épica
|
||||
// Push épica primero — KAPPA asigna el ID
|
||||
const meta = JSON.parse(d.metadata || '{}')
|
||||
const linkedHuIds: number[] = []
|
||||
for (const huTitle of meta.linkedHuTitles || []) {
|
||||
@@ -308,24 +327,51 @@ async function pushDraft(d: HuDraftRecord) {
|
||||
status: false,
|
||||
})
|
||||
} else {
|
||||
// Push HU
|
||||
// Push HU con epic_development si está vinculada a una épica
|
||||
const meta = JSON.parse(d.metadata || '{}')
|
||||
const epicDevId = meta.epicDevelopment || null
|
||||
endpoint = '/api/userstorys/create/'
|
||||
body = JSON.stringify({
|
||||
initiative: String(d.projectId), title: d.title,
|
||||
initiative: String(d.projectId),
|
||||
title: d.title,
|
||||
description: d.description ? `<p>${d.description.replace(/\n/g, '</p><p>')}</p>` : '',
|
||||
criterios_aceptacion: d.acceptanceCriteria ? `<p>${d.acceptanceCriteria.replace(/\n/g, '</p><p>')}</p>` : '',
|
||||
story_points: String(d.story_points ?? ''),
|
||||
priority: d.priority === 'Alta' ? '1' : d.priority === 'Baja' ? '3' : '2',
|
||||
sprint: '', asignado_a: [], client_taker: null,
|
||||
characterization_hu: '', has_impairment: false,
|
||||
epic_development: null, feature: '',
|
||||
initial_date: null, end_date: null,
|
||||
story_points: d.story_points ?? null,
|
||||
priority: d.priority === 'Alta' ? '3' : d.priority === 'Baja' ? '1' : '2',
|
||||
sprint: meta.sprint ?? '',
|
||||
asignado_a: meta.asignado_a ?? [],
|
||||
client_taker: meta.clientTaker ?? null,
|
||||
characterization_hu: meta.characterizationHu ?? '',
|
||||
has_impairment: false,
|
||||
epic_development: epicDevId,
|
||||
feature: meta.feature ?? '',
|
||||
initial_date: null,
|
||||
end_date: meta.endDate ?? null,
|
||||
status: meta.status ?? 1,
|
||||
is_planned: false,
|
||||
})
|
||||
}
|
||||
|
||||
const res = await fetch(endpoint, { method: 'POST', headers, body })
|
||||
if (res.ok) {
|
||||
if (d.type !== 'E') {
|
||||
if (d.type === 'E') {
|
||||
// Épica creada → capturar ID y vincular HUs pendientes
|
||||
const created = await res.json()
|
||||
const epicKappaId = created.id
|
||||
const meta = JSON.parse(d.metadata || '{}')
|
||||
for (const huTitle of meta.linkedHuTitles || []) {
|
||||
const linkedDraft = drafts.value.find(x =>
|
||||
x.type !== 'E' && x.syncStatus === 'draft' &&
|
||||
x.title.toLowerCase().trim() === huTitle.toLowerCase().trim()
|
||||
)
|
||||
if (linkedDraft) {
|
||||
const linkedMeta = JSON.parse(linkedDraft.metadata || '{}')
|
||||
linkedMeta.epicDevelopment = String(epicKappaId)
|
||||
linkedDraft.metadata = JSON.stringify(linkedMeta)
|
||||
await dbSaveDraft(linkedDraft)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const created = await res.json()
|
||||
d.kappaId = created.id || undefined
|
||||
}
|
||||
@@ -365,44 +411,121 @@ async function discardDraft(id: string) {
|
||||
await loadDrafts()
|
||||
}
|
||||
|
||||
// ─── Project analysis ────────────────────────────────────
|
||||
// ─── Export report (pending Python sidecar) ────────
|
||||
|
||||
// ─── Project analysis — Two-phase ─────────────────────────
|
||||
const phase = ref<'idle' | 'epics' | 'hus' | 'done'>('idle')
|
||||
const analyzing = ref(false)
|
||||
const analysisAbort = ref<AbortController | null>(null)
|
||||
const analysisMessage = ref('')
|
||||
const analysisResult = ref<{ saved: number; skipped: number } | null>(null)
|
||||
const analysisSummary = ref('')
|
||||
|
||||
function cancelAnalysis() {
|
||||
analysisAbort.value?.abort()
|
||||
analyzing.value = false
|
||||
analysisAbort.value = null
|
||||
analysisSummary.value = 'Análisis cancelado'
|
||||
analysisMessage.value = ''
|
||||
phase.value = 'idle'
|
||||
}
|
||||
|
||||
async function runAnalysis() {
|
||||
// Obtener épicas que ya existen en KAPPA
|
||||
function getExistingEpicNames(): string[] {
|
||||
return workItems.epics.map(e => (e._cleanName || e.name || e.title || '').toLowerCase().trim()).filter(Boolean)
|
||||
}
|
||||
|
||||
// FASE 1: Generar épicas
|
||||
async function runPhaseEpics() {
|
||||
if (!project.value) return
|
||||
phase.value = 'epics'
|
||||
analyzing.value = true
|
||||
analysisAbort.value = new AbortController()
|
||||
analysisResult.value = null
|
||||
analysisSummary.value = ''
|
||||
analysisMessage.value = 'Analizando contexto y generando épicas...'
|
||||
|
||||
try {
|
||||
const result = await analyzeProject(project.value.id, project.value.name || '', workItems.userStories, analysisAbort.value?.signal)
|
||||
analysisSummary.value = result.summary
|
||||
const existingEpics = getExistingEpicNames()
|
||||
const result = await analyzeProjectEpics(
|
||||
project.value.id, project.value.name || '',
|
||||
workItems.userStories, existingEpics,
|
||||
analysisAbort.value?.signal,
|
||||
workItems.epics,
|
||||
)
|
||||
analysisMessage.value = result.rationale
|
||||
|
||||
if (result.hus.length > 0) {
|
||||
const outcome = await saveAsDrafts(project.value.id, result, workItems.userStories)
|
||||
if (result.epics.length > 0) {
|
||||
const outcome = await saveEpicDrafts(project.value.id, result.epics, workItems.userStories)
|
||||
analysisResult.value = outcome
|
||||
await loadDrafts()
|
||||
} else {
|
||||
analysisResult.value = { saved: 0, skipped: 0 }
|
||||
analysisMessage.value = 'No se identificaron nuevas épicas necesarias.'
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (e.name === 'AbortError' || e.message?.includes('aborted')) {
|
||||
analysisSummary.value = 'Análisis cancelado'
|
||||
analysisMessage.value = 'Análisis cancelado'
|
||||
} else {
|
||||
console.error('[Alpha] Analysis error:', e)
|
||||
analysisSummary.value = `Error: ${e.message}`
|
||||
console.error('[Alpha] Phase 1 error:', e)
|
||||
analysisMessage.value = `Error: ${e.message}`
|
||||
}
|
||||
analysisResult.value = { saved: 0, skipped: 0 }
|
||||
phase.value = 'idle'
|
||||
} finally {
|
||||
analyzing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// FASE 2: Generar HUs dentro de las épicas
|
||||
async function runPhaseHUs() {
|
||||
if (!project.value) return
|
||||
phase.value = 'hus'
|
||||
analyzing.value = true
|
||||
analysisAbort.value = new AbortController()
|
||||
analysisResult.value = null
|
||||
analysisMessage.value = 'Generando HUs dentro de las épicas...'
|
||||
|
||||
try {
|
||||
// Obtener las épicas confirmadas desde los drafts de tipo épica
|
||||
const epicDrafts = drafts.value.filter(d => d.type === 'E')
|
||||
const confirmedEpics = epicDrafts.map(d => ({
|
||||
name: d.title,
|
||||
description: d.description,
|
||||
linkedHuTitles: (() => { try { return JSON.parse(d.metadata || '{}').linkedHuTitles || [] } catch { return [] } })(),
|
||||
}))
|
||||
|
||||
if (confirmedEpics.length === 0) {
|
||||
analysisMessage.value = 'No hay épicas en borrador. Primero generá y enviá las épicas a KAPPA.'
|
||||
analysisResult.value = { saved: 0, skipped: 0 }
|
||||
phase.value = 'idle'
|
||||
analyzing.value = false
|
||||
return
|
||||
}
|
||||
|
||||
const result = await analyzeProjectHUs(
|
||||
project.value.id, project.value.name || '',
|
||||
workItems.userStories, confirmedEpics,
|
||||
analysisAbort.value?.signal,
|
||||
)
|
||||
analysisMessage.value = result.summary
|
||||
|
||||
if (result.hus.length > 0) {
|
||||
const outcome = await saveHUDrafts(project.value.id, result.hus, workItems.userStories)
|
||||
analysisResult.value = outcome
|
||||
await loadDrafts()
|
||||
phase.value = 'done'
|
||||
} else {
|
||||
analysisResult.value = { saved: 0, skipped: 0 }
|
||||
analysisMessage.value = 'No se identificaron HUs nuevas. Todo parece cubierto.'
|
||||
phase.value = 'done'
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (e.name === 'AbortError' || e.message?.includes('aborted')) {
|
||||
analysisMessage.value = 'Análisis cancelado'
|
||||
} else {
|
||||
console.error('[Alpha] Phase 2 error:', e)
|
||||
analysisMessage.value = `Error: ${e.message}`
|
||||
}
|
||||
analysisResult.value = { saved: 0, skipped: 0 }
|
||||
phase.value = 'idle'
|
||||
} finally {
|
||||
analyzing.value = false
|
||||
}
|
||||
@@ -421,6 +544,32 @@ watch(
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
// ─── Epic progress bar ───────────────────────────────
|
||||
const epicProgress = computed(() => {
|
||||
const map = new Map<string, { total: number; done: number }>()
|
||||
for (const hu of workItems.userStories) {
|
||||
const epicCode = hu._epicCode || ''
|
||||
if (!epicCode) continue
|
||||
if (!map.has(epicCode)) map.set(epicCode, { total: 0, done: 0 })
|
||||
const entry = map.get(epicCode)!
|
||||
entry.total++
|
||||
const s = String(hu.status ?? '').toLowerCase()
|
||||
if (['done', 'completed', 'closed', 'finalizado', '5', '6', '7', 'qa-client', 'ready to deploy'].includes(s)) {
|
||||
entry.done++
|
||||
}
|
||||
}
|
||||
const result: Record<string, number> = {}
|
||||
for (const [code, { total, done }] of map) {
|
||||
result[code] = total > 0 ? Math.round((done / total) * 100) : 0
|
||||
}
|
||||
return result
|
||||
})
|
||||
|
||||
function getEpicProgress(epic: any): number {
|
||||
const code = epic._epicCode
|
||||
return code ? epicProgress.value[code] ?? 0 : 0
|
||||
}
|
||||
|
||||
const statusVariant = (status: unknown) => {
|
||||
const s = String(status ?? '').toLowerCase()
|
||||
if (['done', 'completed', 'closed', 'finalizado'].includes(s)) return 'secondary'
|
||||
@@ -453,6 +602,10 @@ const statusLabel = (status: unknown) => {
|
||||
</h1>
|
||||
<div class="flex items-center gap-2">
|
||||
<Badge v-if="project.key" variant="outline" class="text-xs">{{ project.key }}</Badge>
|
||||
<!-- Export button pending Python sidecar
|
||||
<button class="ml-auto text-[11px] text-muted-foreground hover:text-foreground transition-colors flex items-center gap-1" title="Exportar informe DOCX (próximamente)">
|
||||
<FileText class="size-3.5" /> Exportar
|
||||
</button> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -519,6 +672,9 @@ const statusLabel = (status: unknown) => {
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<!-- Priorizador Diario -->
|
||||
<PrioritizerCard />
|
||||
|
||||
<!-- AI Chat -->
|
||||
<AiProjectChat
|
||||
:project-id="project.id"
|
||||
@@ -529,45 +685,59 @@ const statusLabel = (status: unknown) => {
|
||||
@navigate-settings="emit('navigate-settings')"
|
||||
/>
|
||||
|
||||
<!-- Project Analysis -->
|
||||
<!-- Project Analysis — Two-phase -->
|
||||
<Card id="dashboard-analysis" class="border-dashed">
|
||||
<CardHeader class="pb-3 flex flex-row items-center justify-between">
|
||||
<CardTitle class="text-sm font-medium flex items-center gap-2">
|
||||
<Sparkles class="size-4" />
|
||||
Análisis completo del proyecto
|
||||
Análisis del proyecto
|
||||
</CardTitle>
|
||||
<div class="flex gap-2">
|
||||
<div v-if="!analyzing" class="flex gap-2">
|
||||
<Button
|
||||
v-if="analyzing"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
@click="cancelAnalysis()"
|
||||
:disabled="analyzing || phase === 'done'"
|
||||
@click="runPhaseEpics()"
|
||||
>
|
||||
Cancelar
|
||||
<Sparkles class="size-3 mr-1" />
|
||||
1. Generar Épicas
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
:disabled="analyzing"
|
||||
@click="runAnalysis()"
|
||||
:disabled="analyzing || phase === 'idle'"
|
||||
@click="runPhaseHUs()"
|
||||
>
|
||||
<Loader2 v-if="analyzing" class="size-4 mr-1 animate-spin" />
|
||||
<Sparkles v-else class="size-4 mr-1" />
|
||||
{{ analyzing ? 'Analizando...' : 'Generar HUs faltantes' }}
|
||||
<Sparkles class="size-3 mr-1" />
|
||||
2. Generar HUs
|
||||
</Button>
|
||||
</div>
|
||||
<Button
|
||||
v-if="analyzing"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
@click="cancelAnalysis()"
|
||||
>
|
||||
Cancelar
|
||||
</Button>
|
||||
</CardHeader>
|
||||
<CardContent v-if="analysisResult" class="space-y-2 text-sm">
|
||||
<p class="text-muted-foreground">{{ analysisSummary }}</p>
|
||||
<div class="flex items-center gap-3 text-xs">
|
||||
<span v-if="analysisResult.saved > 0" class="text-green-600 dark:text-green-400 flex items-center gap-1">
|
||||
<CheckCircle2 class="size-3" /> {{ analysisResult.saved }} borradores guardados
|
||||
</span>
|
||||
<span v-if="analysisResult.skipped > 0" class="text-amber-600 dark:text-amber-400 flex items-center gap-1">
|
||||
{{ analysisResult.skipped }} duplicadas saltadas
|
||||
</span>
|
||||
<span v-if="analysisResult.saved === 0 && analysisResult.skipped === 0" class="text-muted-foreground">
|
||||
Todo ya está cubierto. No se requieren nuevas HUs.
|
||||
</span>
|
||||
<CardContent class="space-y-3">
|
||||
<div v-if="analyzing" class="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<Loader2 class="size-4 animate-spin" />
|
||||
{{ analysisMessage }}
|
||||
</div>
|
||||
<div v-if="analysisResult" class="space-y-2 text-sm">
|
||||
<p class="text-muted-foreground">{{ analysisMessage }}</p>
|
||||
<div class="flex items-center gap-3 text-xs">
|
||||
<span v-if="analysisResult.saved > 0" class="text-green-600 dark:text-green-400 flex items-center gap-1">
|
||||
<CheckCircle2 class="size-3" /> {{ analysisResult.saved }} borradores guardados
|
||||
</span>
|
||||
<span v-if="analysisResult.skipped > 0" class="text-amber-600 dark:text-amber-400 flex items-center gap-1">
|
||||
{{ analysisResult.skipped }} duplicadas saltadas
|
||||
</span>
|
||||
<span v-if="analysisResult.saved === 0 && analysisResult.skipped === 0" class="text-muted-foreground">
|
||||
{{ phase === 'epics' ? 'No se requieren nuevas épicas.' : phase === 'hus' ? 'No se requieren nuevas HUs.' : '' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -584,7 +754,9 @@ const statusLabel = (status: unknown) => {
|
||||
<Badge variant="outline" class="text-[10px]" :class="d.type === 'E' ? 'border-purple-300 text-purple-600' : ''">
|
||||
{{ d.type === 'E' ? 'Épica' : 'HU' }}
|
||||
</Badge>
|
||||
<p class="font-medium">{{ d.title }}</p>
|
||||
<span v-if="d.type === 'E'" class="font-mono text-[10px] text-muted-foreground shrink-0">[{{ getEpicCode(d) }}]</span>
|
||||
<span v-else class="font-mono text-[10px] text-muted-foreground shrink-0">{{ getItemCode(d) }}</span>
|
||||
<p class="font-medium">{{ d.type === 'E' ? stripEpicTitle(d.title) : d.title }}</p>
|
||||
</div>
|
||||
<div v-if="d.type === 'E' && d.metadata" class="text-xs text-muted-foreground mt-0.5">
|
||||
<span v-if="getEpicLinkedHUs(d).length > 0">
|
||||
@@ -637,13 +809,12 @@ const statusLabel = (status: unknown) => {
|
||||
<TableHead class="w-[60px]">{{ t('users.role') }}</TableHead>
|
||||
<TableHead>{{ t('dashboard.title') }}</TableHead>
|
||||
<TableHead class="w-[60px] text-center">Desc</TableHead>
|
||||
<TableHead class="w-[90px] text-center">{{ t('dashboard.assignedTo') }}</TableHead>
|
||||
<TableHead class="w-[100px]">{{ t('dashboard.status') }}</TableHead>
|
||||
<TableHead class="w-[140px]">Progreso</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<TableRow v-for="epic in workItems.epics" :key="epic.id">
|
||||
<TableCell class="font-mono text-xs text-muted-foreground">{{ epic.code || `EP-${epic.id}` }}</TableCell>
|
||||
<TableCell class="font-mono text-xs text-muted-foreground">{{ epic._epicCode || epic.code || `EP-${epic.id}` }}</TableCell>
|
||||
<TableCell>
|
||||
<span class="inline-flex items-center rounded px-1 py-0.5 text-[10px] font-bold" :class="getTypeColor(epic._itemType)">{{ getTypeLabel(epic._itemType) }}</span>
|
||||
</TableCell>
|
||||
@@ -658,9 +829,17 @@ const statusLabel = (status: unknown) => {
|
||||
</Dialog>
|
||||
<span v-else class="text-xs text-muted-foreground/40">—</span>
|
||||
</TableCell>
|
||||
<TableCell class="text-center text-xs text-muted-foreground">—</TableCell>
|
||||
<TableCell>
|
||||
<Badge :variant="statusVariant(epic.status || '')" class="text-xs">{{ statusLabel(epic.status || '') }}</Badge>
|
||||
<TableCell class="text-xs">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex-1 h-2 rounded-full bg-muted overflow-hidden">
|
||||
<div
|
||||
class="h-full rounded-full transition-all duration-300"
|
||||
:class="getEpicProgress(epic) >= 100 ? 'bg-green-500' : getEpicProgress(epic) > 0 ? 'bg-primary' : ''"
|
||||
:style="{ width: getEpicProgress(epic) + '%' }"
|
||||
/>
|
||||
</div>
|
||||
<span class="font-mono text-[10px] text-muted-foreground w-8 text-right">{{ getEpicProgress(epic) }}%</span>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
@@ -734,9 +913,9 @@ const statusLabel = (status: unknown) => {
|
||||
<SelectItem value="baja" class="text-xs">Baja</SelectItem>
|
||||
<SelectItem value="critical" class="text-xs">Crítica</SelectItem>
|
||||
<SelectItem value="urgente" class="text-xs">Urgente</SelectItem>
|
||||
<SelectItem value="1" class="text-xs">Alta (1)</SelectItem>
|
||||
<SelectItem value="3" class="text-xs">Alta (3)</SelectItem>
|
||||
<SelectItem value="2" class="text-xs">Media (2)</SelectItem>
|
||||
<SelectItem value="3" class="text-xs">Baja (3)</SelectItem>
|
||||
<SelectItem value="1" class="text-xs">Baja (1)</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Select v-model="filterAssigned">
|
||||
@@ -813,9 +992,10 @@ const statusLabel = (status: unknown) => {
|
||||
<div
|
||||
v-if="notification"
|
||||
class="fixed bottom-4 right-4 z-50 flex items-start gap-3 p-3 rounded-lg border bg-card border-l-[3px] text-xs shadow-lg max-w-sm"
|
||||
:class="notification.type === 'success' ? 'border-l-green-500' : 'border-l-red-500'"
|
||||
:class="notification.type === 'success' ? 'border-l-green-500' : notification.type === 'error' ? 'border-l-red-500' : 'border-l-blue-500'"
|
||||
>
|
||||
<CheckCircle2 v-if="notification.type === 'success'" class="size-4 text-green-500 shrink-0 mt-0.5" />
|
||||
<svg v-else-if="notification.type === 'info'" class="size-4 text-blue-500 shrink-0 mt-0.5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>
|
||||
<XCircle v-else class="size-4 text-red-500 shrink-0 mt-0.5" />
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="font-medium text-foreground">{{ notification.type === 'success' ? 'Completado' : 'Error' }}</p>
|
||||
|
||||
+134
-30
@@ -3,7 +3,8 @@ import { ref, onMounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useSettingsStore, PROVIDER_CONFIG, type AIProvider } from '@/stores/settings'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { getAllPromptKeys, getPrompt, savePrompt, resetPrompt, type PromptKey } from '@/services/prompts-db'
|
||||
import { getAllPromptKeys, getPrompt, savePrompt, resetPrompt, getDefaultPrompt, type PromptKey } from '@/services/prompts-db'
|
||||
import PromptEditorModal from '@/components/PromptEditorModal.vue'
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
@@ -16,6 +17,7 @@ import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { getWebhookUrl, setWebhookUrl, sendTestMessage } from '@/services/teams'
|
||||
import {
|
||||
Brain,
|
||||
Key,
|
||||
@@ -26,6 +28,7 @@ import {
|
||||
LogOut,
|
||||
Sparkles,
|
||||
ChevronRight,
|
||||
MessageSquare,
|
||||
} from 'lucide-vue-next'
|
||||
|
||||
const { t } = useI18n()
|
||||
@@ -66,8 +69,61 @@ function markDirty(key: string) {
|
||||
promptDirty.value[key] = true
|
||||
}
|
||||
|
||||
// ─── Prompt editor modal ──────────────────────────
|
||||
const editingPrompt = ref<{ key: PromptKey; label: string } | null>(null)
|
||||
const editingContent = ref('')
|
||||
const promptModalOpen = ref(false)
|
||||
|
||||
function openPromptEditor(key: PromptKey, label: string) {
|
||||
editingPrompt.value = { key, label }
|
||||
editingContent.value = promptContents.value[key] || ''
|
||||
promptModalOpen.value = true
|
||||
}
|
||||
|
||||
function savePromptFromEditor(content: string) {
|
||||
if (!editingPrompt.value) return
|
||||
const key = editingPrompt.value.key
|
||||
promptContents.value[key] = content
|
||||
promptDirty.value[key] = true
|
||||
savePromptHandler(key)
|
||||
}
|
||||
|
||||
function resetPromptFromEditor() {
|
||||
if (!editingPrompt.value) return
|
||||
const key = editingPrompt.value.key
|
||||
resetPromptHandler(key)
|
||||
editingContent.value = promptContents.value[key] || ''
|
||||
}
|
||||
|
||||
onMounted(loadPrompts)
|
||||
|
||||
// ─── Teams ───────────────────────────────────────────
|
||||
const teamsUrl = ref(getWebhookUrl())
|
||||
const teamsSending = ref(false)
|
||||
const teamsStatus = ref<'idle' | 'ok' | 'error'>('idle')
|
||||
const teamsStatusMsg = ref('')
|
||||
|
||||
function teamsUrlChanged() {
|
||||
setWebhookUrl(teamsUrl.value)
|
||||
teamsStatus.value = 'idle'
|
||||
}
|
||||
|
||||
async function testTeams() {
|
||||
teamsSending.value = true
|
||||
teamsStatus.value = 'idle'
|
||||
teamsStatusMsg.value = ''
|
||||
try {
|
||||
const ok = await sendTestMessage()
|
||||
teamsStatus.value = ok ? 'ok' : 'error'
|
||||
teamsStatusMsg.value = ok ? 'Mensaje enviado a Teams' : 'Error al enviar. Verifica la URL del webhook.'
|
||||
} catch (e: any) {
|
||||
teamsStatus.value = 'error'
|
||||
teamsStatusMsg.value = e.message
|
||||
} finally {
|
||||
teamsSending.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const newKey = ref('')
|
||||
const keySaved = ref(false)
|
||||
|
||||
@@ -283,6 +339,44 @@ const tierColors: Record<string, string> = {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- Teams -->
|
||||
<Card id="settings-teams">
|
||||
<CardHeader>
|
||||
<CardTitle class="text-sm font-medium flex items-center gap-2">
|
||||
<MessageSquare class="size-4" />
|
||||
Microsoft Teams
|
||||
</CardTitle>
|
||||
<CardDescription class="text-xs">
|
||||
Configurá un webhook para recibir notificaciones de Alpha en Teams.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-3">
|
||||
<div class="space-y-1.5">
|
||||
<Label class="text-xs font-medium">URL del Webhook</Label>
|
||||
<Input
|
||||
v-model="teamsUrl"
|
||||
placeholder="https://...office.com/webhookb2/..."
|
||||
class="text-xs font-mono"
|
||||
@input="teamsUrlChanged"
|
||||
/>
|
||||
<p class="text-[10px] text-muted-foreground">
|
||||
Creá un webhook en Teams: Canal → ... → Conectores → Webhook entrante
|
||||
</p>
|
||||
</div>
|
||||
<div v-if="teamsUrl" class="flex items-center gap-2">
|
||||
<Button size="sm" variant="outline" class="text-xs" :disabled="teamsSending" @click="testTeams">
|
||||
{{ teamsSending ? 'Enviando...' : 'Enviar prueba' }}
|
||||
</Button>
|
||||
<span v-if="teamsStatus === 'ok'" class="text-xs text-green-600 flex items-center gap-1">
|
||||
<CheckCircle2 class="size-3" /> {{ teamsStatusMsg }}
|
||||
</span>
|
||||
<span v-if="teamsStatus === 'error'" class="text-xs text-red-600 flex items-center gap-1">
|
||||
<XCircle class="size-3" /> {{ teamsStatusMsg }}
|
||||
</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- Prompts -->
|
||||
<Card id="settings-prompts" class="border-dashed">
|
||||
<CardHeader>
|
||||
@@ -294,40 +388,50 @@ const tierColors: Record<string, string> = {
|
||||
Editá los prompts que usa la IA para cada función. Los cambios se aplican inmediatamente.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-4">
|
||||
<div v-for="{ key, label } in promptKeys" :key="key" class="space-y-1.5">
|
||||
<div class="flex items-center justify-between">
|
||||
<Label class="text-xs font-medium">{{ label }}</Label>
|
||||
<div class="flex gap-1">
|
||||
<Button
|
||||
v-if="promptDirty[key]"
|
||||
size="sm"
|
||||
variant="default"
|
||||
class="text-xs h-6 px-2"
|
||||
:disabled="promptSaving[key]"
|
||||
@click="savePromptHandler(key as PromptKey)"
|
||||
>
|
||||
{{ promptSaving[key] ? 'Guardando...' : 'Guardar' }}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
class="text-xs h-6 px-2 text-muted-foreground"
|
||||
@click="resetPromptHandler(key as PromptKey)"
|
||||
>
|
||||
Restaurar
|
||||
</Button>
|
||||
</div>
|
||||
<CardContent class="space-y-2">
|
||||
<div
|
||||
v-for="{ key, label } in promptKeys"
|
||||
:key="key"
|
||||
class="flex items-center gap-3 p-3 rounded-lg border hover:border-primary/50 transition-colors cursor-pointer"
|
||||
@click="openPromptEditor(key as PromptKey, label)"
|
||||
>
|
||||
<div class="size-8 rounded-lg bg-muted flex items-center justify-center shrink-0">
|
||||
<span class="text-xs font-bold text-muted-foreground">{{ label.slice(0, 2).toUpperCase() }}</span>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="text-sm font-medium">{{ label }}</p>
|
||||
<p class="text-xs text-muted-foreground truncate mt-0.5">{{ (promptContents[key] || '').slice(0, 80) }}...</p>
|
||||
</div>
|
||||
<div class="flex gap-1 shrink-0">
|
||||
<Button
|
||||
v-if="promptDirty[key]"
|
||||
size="sm"
|
||||
variant="default"
|
||||
class="text-xs h-7 px-2"
|
||||
:disabled="promptSaving[key]"
|
||||
@click.stop="savePromptHandler(key as PromptKey)"
|
||||
>
|
||||
{{ promptSaving[key] ? '...' : 'Guardar' }}
|
||||
</Button>
|
||||
<Button size="sm" variant="ghost" class="text-xs h-7 px-2 text-muted-foreground" @click.stop="openPromptEditor(key as PromptKey, label)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-3.5"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/></svg>
|
||||
</Button>
|
||||
</div>
|
||||
<textarea
|
||||
:value="promptContents[key]"
|
||||
@input="(e: any) => { promptContents[key] = e.target.value; markDirty(key) }"
|
||||
class="w-full h-32 rounded-md border border-input bg-background p-2 text-xs font-mono outline-none focus:border-ring focus:ring-1 focus:ring-ring resize-y"
|
||||
/>
|
||||
</div>
|
||||
<p class="text-[10px] text-muted-foreground text-center pt-1">Haz clic en un prompt para editarlo con el editor enriquecido</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- Prompt Editor Modal -->
|
||||
<PromptEditorModal
|
||||
:open="promptModalOpen"
|
||||
:title="editingPrompt?.label || ''"
|
||||
:content="editingContent"
|
||||
@update:open="promptModalOpen = $event"
|
||||
@save="savePromptFromEditor"
|
||||
@reset="resetPromptFromEditor"
|
||||
/>
|
||||
|
||||
<!-- Account -->
|
||||
<Card id="settings-account">
|
||||
<CardHeader>
|
||||
|
||||
Reference in New Issue
Block a user