From ead8219f3bf98f079cb54c08fd9d193520057b02 Mon Sep 17 00:00:00 2001
From: domrichardson <100129001+domrichardson@users.noreply.github.com>
Date: Wed, 17 Jun 2026 12:08:20 +0100
Subject: [PATCH] new frontend
---
.gitignore | 4 +
backend/cmd/server/main.go | 141 +-
devops/docker/Dockerfile | 8 +-
frontend_new/next-env.d.ts | 6 +
frontend_new/next.config.ts | 12 +
frontend_new/package-lock.json | 2281 +++++++++++++++++
frontend_new/package.json | 45 +
frontend_new/src/app/admin/layout.tsx | 51 +
frontend_new/src/app/admin/page.tsx | 887 +++++++
frontend_new/src/app/dashboard/layout.tsx | 167 ++
frontend_new/src/app/dashboard/page.tsx | 315 +++
.../notes/[noteId]/NotePageClient.tsx | 572 +++++
.../spaces/[spaceId]/notes/[noteId]/page.tsx | 9 +
.../[spaceId]/notes/new/NewNotePageClient.tsx | 346 +++
.../spaces/[spaceId]/notes/new/page.tsx | 9 +
.../[taskListId]/TaskListPageClient.tsx | 1025 ++++++++
.../[spaceId]/tasklists/[taskListId]/page.tsx | 9 +
.../tasklists/new/NewTaskListPageClient.tsx | 132 +
.../spaces/[spaceId]/tasklists/new/page.tsx | 9 +
frontend_new/src/app/layout.tsx | 18 +
frontend_new/src/app/login/page.tsx | 166 ++
frontend_new/src/app/page.tsx | 30 +
frontend_new/src/app/register/page.tsx | 167 ++
.../src/components/AdminGroupModal.tsx | 106 +
.../src/components/AdminProviderModal.tsx | 212 ++
.../src/components/AdminSpaceModal.tsx | 294 +++
.../src/components/AdminUserModal.tsx | 112 +
.../src/components/ConfirmActionModal.tsx | 66 +
frontend_new/src/components/Navbar.tsx | 160 ++
.../src/components/RichTextEditor.tsx | 618 +++++
frontend_new/src/components/Sidebar.tsx | 125 +
.../src/components/SpaceSettingsModal.tsx | 320 +++
frontend_new/src/lib/apiClient.ts | 59 +
frontend_new/src/lib/markdown.ts | 35 +
frontend_new/src/stores/authStore.ts | 127 +
frontend_new/src/stores/settingsStore.ts | 42 +
frontend_new/src/stores/spaceStore.ts | 213 ++
frontend_new/src/styles/globals.css | 1260 +++++++++
frontend_new/tsconfig.json | 27 +
frontend_new/tsconfig.tsbuildinfo | 1 +
40 files changed, 10149 insertions(+), 37 deletions(-)
create mode 100644 frontend_new/next-env.d.ts
create mode 100644 frontend_new/next.config.ts
create mode 100644 frontend_new/package-lock.json
create mode 100644 frontend_new/package.json
create mode 100644 frontend_new/src/app/admin/layout.tsx
create mode 100644 frontend_new/src/app/admin/page.tsx
create mode 100644 frontend_new/src/app/dashboard/layout.tsx
create mode 100644 frontend_new/src/app/dashboard/page.tsx
create mode 100644 frontend_new/src/app/dashboard/spaces/[spaceId]/notes/[noteId]/NotePageClient.tsx
create mode 100644 frontend_new/src/app/dashboard/spaces/[spaceId]/notes/[noteId]/page.tsx
create mode 100644 frontend_new/src/app/dashboard/spaces/[spaceId]/notes/new/NewNotePageClient.tsx
create mode 100644 frontend_new/src/app/dashboard/spaces/[spaceId]/notes/new/page.tsx
create mode 100644 frontend_new/src/app/dashboard/spaces/[spaceId]/tasklists/[taskListId]/TaskListPageClient.tsx
create mode 100644 frontend_new/src/app/dashboard/spaces/[spaceId]/tasklists/[taskListId]/page.tsx
create mode 100644 frontend_new/src/app/dashboard/spaces/[spaceId]/tasklists/new/NewTaskListPageClient.tsx
create mode 100644 frontend_new/src/app/dashboard/spaces/[spaceId]/tasklists/new/page.tsx
create mode 100644 frontend_new/src/app/layout.tsx
create mode 100644 frontend_new/src/app/login/page.tsx
create mode 100644 frontend_new/src/app/page.tsx
create mode 100644 frontend_new/src/app/register/page.tsx
create mode 100644 frontend_new/src/components/AdminGroupModal.tsx
create mode 100644 frontend_new/src/components/AdminProviderModal.tsx
create mode 100644 frontend_new/src/components/AdminSpaceModal.tsx
create mode 100644 frontend_new/src/components/AdminUserModal.tsx
create mode 100644 frontend_new/src/components/ConfirmActionModal.tsx
create mode 100644 frontend_new/src/components/Navbar.tsx
create mode 100644 frontend_new/src/components/RichTextEditor.tsx
create mode 100644 frontend_new/src/components/Sidebar.tsx
create mode 100644 frontend_new/src/components/SpaceSettingsModal.tsx
create mode 100644 frontend_new/src/lib/apiClient.ts
create mode 100644 frontend_new/src/lib/markdown.ts
create mode 100644 frontend_new/src/stores/authStore.ts
create mode 100644 frontend_new/src/stores/settingsStore.ts
create mode 100644 frontend_new/src/stores/spaceStore.ts
create mode 100644 frontend_new/src/styles/globals.css
create mode 100644 frontend_new/tsconfig.json
create mode 100644 frontend_new/tsconfig.tsbuildinfo
diff --git a/.gitignore b/.gitignore
index 46aceb2..bb0e75d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,3 +43,7 @@ secret*
*.a
*.so
.go/
+
+frontend_new/out
+backend/public
+frontend_new/.next
\ No newline at end of file
diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go
index e79a539..dbb50cc 100644
--- a/backend/cmd/server/main.go
+++ b/backend/cmd/server/main.go
@@ -6,6 +6,7 @@ import (
"log"
"net/http"
"os"
+ "path/filepath"
"strconv"
"strings"
"time"
@@ -358,39 +359,9 @@ func main() {
admin.HandleFunc("/auth/providers/{providerId}", authHandler.UpdateProvider).Methods("PUT")
admin.HandleFunc("/auth/providers/{providerId}", adminHandler.DeleteProvider).Methods("DELETE")
- // Serve static files (frontend) for all other routes
- // This must be after all API route handlers to allow API routes to take precedence
- router.PathPrefix("/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- // List of static file extensions to serve directly
- staticExts := map[string]bool{
- ".js": true, ".css": true, ".svg": true, ".png": true,
- ".jpg": true, ".jpeg": true, ".gif": true, ".ico": true,
- ".woff": true, ".woff2": true, ".ttf": true, ".eot": true,
- }
-
- filePath := "./public" + r.URL.Path
- if r.URL.Path == "/" {
- filePath = "./public/index.html"
- }
-
- // Check if it's a static file (has an extension in staticExts)
- isStatic := false
- for ext := range staticExts {
- if len(r.URL.Path) > len(ext) {
- if r.URL.Path[len(r.URL.Path)-len(ext):] == ext {
- isStatic = true
- break
- }
- }
- }
-
- // If it doesn't look like a static file, serve index.html (SPA routing)
- if !isStatic {
- filePath = "./public/index.html"
- }
-
- http.ServeFile(w, r, filePath)
- })
+ // Serve static files (NextJS frontend) for all other routes.
+ // Must come after all API route handlers.
+ router.PathPrefix("/").HandlerFunc(serveNextJS)
// Start server
server := &http.Server{
@@ -529,3 +500,107 @@ func ensureDefaultAdminUser(
log.Printf("default admin user synchronized from environment: %s", adminEmail)
return nil
}
+
+// serveNextJS serves the NextJS static export from ./public.
+// It handles /_next/ assets directly, tries {path}/index.html for page routes,
+// and falls back to dynamic route pattern matching (NextJS [param] folders).
+func serveNextJS(w http.ResponseWriter, r *http.Request) {
+ const publicDir = "./public"
+ urlPath := r.URL.Path
+
+ // ── /_next/ and other static assets ─────────────────────────────────────
+ if strings.HasPrefix(urlPath, "/_next/") {
+ http.ServeFile(w, r, filepath.Join(publicDir, filepath.FromSlash(urlPath)))
+ return
+ }
+
+ staticExts := []string{
+ ".js", ".css", ".svg", ".png", ".jpg", ".jpeg", ".gif", ".ico",
+ ".woff", ".woff2", ".ttf", ".eot", ".json", ".map", ".txt", ".xml",
+ }
+ for _, ext := range staticExts {
+ if strings.HasSuffix(urlPath, ext) {
+ http.ServeFile(w, r, filepath.Join(publicDir, filepath.FromSlash(urlPath)))
+ return
+ }
+ }
+
+ // ── HTML page resolution ─────────────────────────────────────────────────
+ // NextJS static export with trailingSlash:true produces {path}/index.html
+ cleanPath := strings.TrimRight(urlPath, "/")
+ if cleanPath == "" {
+ cleanPath = "/"
+ }
+
+ // 1. Try exact {path}/index.html
+ candidate := filepath.Join(publicDir, filepath.FromSlash(cleanPath), "index.html")
+ if _, err := os.Stat(candidate); err == nil {
+ http.ServeFile(w, r, candidate)
+ return
+ }
+
+ // 2. Try {path}.html
+ candidate = filepath.Join(publicDir, filepath.FromSlash(cleanPath)+".html")
+ if _, err := os.Stat(candidate); err == nil {
+ http.ServeFile(w, r, candidate)
+ return
+ }
+
+ // 3. Walk path segments and try NextJS dynamic [param] folders
+ segments := strings.Split(strings.Trim(cleanPath, "/"), "/")
+ if result := findNextJSPage(publicDir, segments); result != "" {
+ http.ServeFile(w, r, result)
+ return
+ }
+
+ // 4. Fallback: root index.html
+ http.ServeFile(w, r, filepath.Join(publicDir, "index.html"))
+}
+
+// findNextJSPage recursively searches the public directory for an HTML file
+// that matches the given URL segments, replacing unknown segments with any
+// NextJS dynamic directory ([param] named folders).
+func findNextJSPage(publicDir string, segments []string) string {
+ return searchNextJSSegments(publicDir, segments, 0, publicDir)
+}
+
+func searchNextJSSegments(publicDir string, segments []string, idx int, currentDir string) string {
+ if idx == len(segments) {
+ candidate := filepath.Join(currentDir, "index.html")
+ if _, err := os.Stat(candidate); err == nil {
+ return candidate
+ }
+ return ""
+ }
+
+ seg := segments[idx]
+
+ // Try the exact segment directory
+ exactDir := filepath.Join(currentDir, seg)
+ if result := searchNextJSSegments(publicDir, segments, idx+1, exactDir); result != "" {
+ return result
+ }
+
+ // Try NextJS dynamic segment directories.
+ // Matches both [param] (source convention) and __param__ (static export convention).
+ entries, err := os.ReadDir(currentDir)
+ if err != nil {
+ return ""
+ }
+ for _, entry := range entries {
+ name := entry.Name()
+ if !entry.IsDir() {
+ continue
+ }
+ isBracket := len(name) > 2 && name[0] == '[' && name[len(name)-1] == ']'
+ isPlaceholder := len(name) >= 4 && strings.HasPrefix(name, "__") && strings.HasSuffix(name, "__")
+ if isBracket || isPlaceholder {
+ dynamicDir := filepath.Join(currentDir, name)
+ if result := searchNextJSSegments(publicDir, segments, idx+1, dynamicDir); result != "" {
+ return result
+ }
+ }
+ }
+
+ return ""
+}
diff --git a/devops/docker/Dockerfile b/devops/docker/Dockerfile
index 115f58b..43819f8 100644
--- a/devops/docker/Dockerfile
+++ b/devops/docker/Dockerfile
@@ -1,12 +1,12 @@
# Frontend build stage
FROM node:25-alpine AS frontend-builder
-WORKDIR /frontend
+WORKDIR /frontend_new
-COPY frontend/package*.json ./
+COPY frontend_new/package*.json ./
RUN npm install
-COPY frontend/ .
+COPY frontend_new/ .
RUN npm run build
# Backend build stage
@@ -32,7 +32,7 @@ RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=backend-builder /app/server .
-COPY --from=frontend-builder /frontend/dist ./public
+COPY --from=frontend-builder /frontend_new/out ./public
EXPOSE 8080
diff --git a/frontend_new/next-env.d.ts b/frontend_new/next-env.d.ts
new file mode 100644
index 0000000..9edff1c
--- /dev/null
+++ b/frontend_new/next-env.d.ts
@@ -0,0 +1,6 @@
+///
+///
+import "./.next/types/routes.d.ts";
+
+// NOTE: This file should not be edited
+// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
diff --git a/frontend_new/next.config.ts b/frontend_new/next.config.ts
new file mode 100644
index 0000000..0d972fe
--- /dev/null
+++ b/frontend_new/next.config.ts
@@ -0,0 +1,12 @@
+import type { NextConfig } from "next";
+
+const nextConfig: NextConfig = {
+ output: "export",
+ trailingSlash: true,
+ distDir: "out",
+ images: {
+ unoptimized: true,
+ },
+};
+
+export default nextConfig;
diff --git a/frontend_new/package-lock.json b/frontend_new/package-lock.json
new file mode 100644
index 0000000..ebf05db
--- /dev/null
+++ b/frontend_new/package-lock.json
@@ -0,0 +1,2281 @@
+{
+ "name": "noteapp-frontend-next",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "noteapp-frontend-next",
+ "version": "0.1.0",
+ "dependencies": {
+ "@mdi/font": "^7.4.47",
+ "@tiptap/extension-link": "^3.24.0",
+ "@tiptap/extension-mention": "^3.24.0",
+ "@tiptap/extension-placeholder": "^3.24.0",
+ "@tiptap/extension-table": "^3.24.0",
+ "@tiptap/extension-table-cell": "^3.24.0",
+ "@tiptap/extension-table-header": "^3.24.0",
+ "@tiptap/extension-table-row": "^3.24.0",
+ "@tiptap/extension-task-item": "^3.24.0",
+ "@tiptap/extension-task-list": "^3.24.0",
+ "@tiptap/pm": "^3.24.0",
+ "@tiptap/react": "^3.24.0",
+ "@tiptap/starter-kit": "^3.24.0",
+ "@tiptap/suggestion": "^3.24.0",
+ "axios": "^1.7.0",
+ "bootstrap": "^5.3.0",
+ "dompurify": "^3.0.0",
+ "highlight.js": "^11.11.1",
+ "marked": "^18.0.4",
+ "marked-highlight": "^2.2.4",
+ "next": "16.2.6",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0",
+ "tippy.js": "^6.3.7",
+ "zustand": "^5.0.0"
+ },
+ "devDependencies": {
+ "@types/dompurify": "^3.0.0",
+ "@types/node": "^22.0.0",
+ "@types/react": "^19.0.0",
+ "@types/react-dom": "^19.0.0",
+ "typescript": "^5.0.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
+ "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@floating-ui/core": {
+ "version": "1.7.5",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz",
+ "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@floating-ui/utils": "^0.2.11"
+ }
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.7.6",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz",
+ "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@floating-ui/core": "^1.7.5",
+ "@floating-ui/utils": "^0.2.11"
+ }
+ },
+ "node_modules/@floating-ui/utils": {
+ "version": "0.2.11",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz",
+ "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@img/colour": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
+ "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
+ "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
+ "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
+ "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
+ "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
+ "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
+ "cpu": [
+ "arm"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
+ "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-ppc64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
+ "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-riscv64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
+ "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
+ "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
+ "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
+ "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
+ "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
+ "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
+ "cpu": [
+ "arm"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
+ "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-ppc64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
+ "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-ppc64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-riscv64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
+ "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-riscv64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-s390x": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
+ "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
+ "cpu": [
+ "s390x"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
+ "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
+ "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
+ "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
+ "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.7.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
+ "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
+ "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
+ "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@mdi/font": {
+ "version": "7.4.47",
+ "resolved": "https://registry.npmjs.org/@mdi/font/-/font-7.4.47.tgz",
+ "integrity": "sha512-43MtGpd585SNzHZPcYowu/84Vz2a2g31TvPMTm9uTiCSWzaheQySUcSyUH/46fPnuPQWof2yd0pGBtzee/IQWw==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@next/env": {
+ "version": "16.2.6",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.6.tgz",
+ "integrity": "sha512-gd8HoHN4ufj73WmR3JmVolrpJR47ILK6LouP5xElPglaVxir6e1a7VzvTvDWkOoPXT9rkkTzyCxBu4yeZfZwcw==",
+ "license": "MIT"
+ },
+ "node_modules/@next/swc-darwin-arm64": {
+ "version": "16.2.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.6.tgz",
+ "integrity": "sha512-ZJGkkcNfYgrrMkqOdZ7zoLa1TOy0qpcMfk/z4Mh/FKUz40gVO+HNQWqmLxf67Z5WB64DRp0dhEbyHfel+6sJUg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-darwin-x64": {
+ "version": "16.2.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.6.tgz",
+ "integrity": "sha512-v/YLBHIY132Ced3puBJ7YJKw1lqsCrgcNo2aRJlCEyQrrCeRJlvGlnmxhPxNQI3KE3N1DN5r9TPNPvka3nq5RQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-gnu": {
+ "version": "16.2.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.6.tgz",
+ "integrity": "sha512-RPOvqlYBbcQjkz9VQQDZ2T2bARIjXZV1KFlt+V2Mr6SW/e4I9fcKsaA0hdyf2FHoTlsV2xnBd5Y912rP/1Ce6w==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-musl": {
+ "version": "16.2.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.6.tgz",
+ "integrity": "sha512-URUTu1+dMkxJsPFgm+OeEvq9wf5sujw0EvgYy80TDGHTSLTnIHeqb0Eu8A3sC95IRgjejQL+kC4mw+4yPxiAXA==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-gnu": {
+ "version": "16.2.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.6.tgz",
+ "integrity": "sha512-DOj182mPV8G3UkrayLoREM5YEYI+Dk5wv7Ox9xl1fFibAELEsFD0lDPfHIeILlutMMfdyhlzYPELG3peuKaurw==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-musl": {
+ "version": "16.2.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.6.tgz",
+ "integrity": "sha512-HKQ5SP/V/ub73UvF7n/zeJlxk2kLmtL7Wzrg4WfmkjmNos5onJ2tKu7yZOPdL18A6Svfn3max29ym+ry7NkK4g==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-arm64-msvc": {
+ "version": "16.2.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.6.tgz",
+ "integrity": "sha512-LZXpTlPyS5v7HhSmnvsLGP3iIYgYOBnc8r8ArlT55sGHV89bR2HlDdBjWQ+PY6SJMmk8TuVGFuxalnP3k/0Dwg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-x64-msvc": {
+ "version": "16.2.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.6.tgz",
+ "integrity": "sha512-F0+4i0h9J6C4eE3EAPWsoCk7UW/dbzOjyzxY0qnDUOYFu6FFmdZ6l97/XdV3/Nz3VYyO7UWjyEJUXkGqcoXfMA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@popperjs/core": {
+ "version": "2.11.8",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/popperjs"
+ }
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.5.15",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
+ "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/@tiptap/core": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.24.0.tgz",
+ "integrity": "sha512-GTAsXAI32p4hEZgPzvUv2RPrObxamy9AFhmhG10fXSvN/cDUs8naEYVIqDV3Sh99jMwQEbTFKW1E1mcspsY6ow==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/pm": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-blockquote": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-3.24.0.tgz",
+ "integrity": "sha512-DgwEEJ1GbDQcT054ynxoaZGmB9apGeUklPrinq9o6xdLHpdg+bO9HCQzggdB8n21VLLglb8jfAEWsVNwh3eASQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-bold": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-3.24.0.tgz",
+ "integrity": "sha512-CujogYaynasklFKHADUseuvj8X2FnWktTCCo3Hl+nlyRvBTmm5TK2aqiamg3v2P4dBh3O6a70mo8BfRJPuiR1g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-bubble-menu": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.24.0.tgz",
+ "integrity": "sha512-jRXD+JPu9ayvq78g8hsCxx4q/qUFtrdfIYirRSf5YUseuuUbtfrq83AsGabcygpUTefjJkMQoXNITkh6294Ggw==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@floating-ui/dom": "^1.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "3.24.0",
+ "@tiptap/pm": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-bullet-list": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-3.24.0.tgz",
+ "integrity": "sha512-IOpAm5c4XVVVvkOef+V9XYMVpea+3MgBpCQgn83UQRlwO9eIMwmcyxOznu7gQPQVShTEpkt4T6uK+ZN9o8meIA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/extension-list": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-code": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-3.24.0.tgz",
+ "integrity": "sha512-MAQtrPRQ+HRmcGotWbksdIGeH1gqayFAdvi4lNGeFT7taHXP1o1XD7CQp7iYIKmg8IU4/MQ+RdetSfuC1A9edQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-code-block": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-3.24.0.tgz",
+ "integrity": "sha512-NZglw4oHoH6oJ5+HvxxQCYk+wODJmsxzUpRQdsOmje08sekQH+Zt9i4UKimBhg4urpd5r+dKXTslab9a5eQ86w==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "3.24.0",
+ "@tiptap/pm": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-document": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-3.24.0.tgz",
+ "integrity": "sha512-yxgM3+yXy2XZzEwH43y2Kp8D1BkblxEWLXqo0YCoAKtxyKCcEaT8kdlf70kS7D0+VSzYU4D0iN7VdQIYHcL2mA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-dropcursor": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-3.24.0.tgz",
+ "integrity": "sha512-Dbv1c5LnvG3PT+yEbCNroyOeeUkHq9wcir2pbC7wri7g7d2sCi0+HvKH0MAxLwY3j5NJJSiSyG2ypMaXOAs4sg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/extensions": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-floating-menu": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-3.24.0.tgz",
+ "integrity": "sha512-7QEbf3mUzFAkejjQGX9f0L507oMtnOBRwHt2skUTR+9yXgudsN8zaDBSSRHLeMWGk9b7L293ZMA6zCRrZaHrfA==",
+ "license": "MIT",
+ "optional": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@floating-ui/dom": "^1.0.0",
+ "@tiptap/core": "3.24.0",
+ "@tiptap/pm": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-gapcursor": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-3.24.0.tgz",
+ "integrity": "sha512-CzCP5/jni5RFwW9jCfBO6auh83GbaioMTpSk6tyR3sd+CbwlBcUdsJFGJkbaRdiSS9dgIyi+6hRbhjpYdHcp+w==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/extensions": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-hard-break": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-3.24.0.tgz",
+ "integrity": "sha512-T/ZEBiHQPMyTqDvXG0tiqBToNeuSemIPmNtdoGSgBN/degVl7VJZqQIrLIvOUHfjf3QkRs7TE/mcqTJsIboO/g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-heading": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-3.24.0.tgz",
+ "integrity": "sha512-GCSgapIzQPqEGNcVGE0/Pcjg5wITMLYJlrS3GGVw7BPmECJwgexcoOsEwkxtzJnXT/HpFXbvOFW43sM0KeHSjg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-horizontal-rule": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.24.0.tgz",
+ "integrity": "sha512-DFzWJTrb23x+qssLLs85vEyho8ItUGp3RY9XUsVTIAGZn5IsoUw8wMsvIBlH1ux4Ch7gLchtcD6kpTdMdrL9kw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "3.24.0",
+ "@tiptap/pm": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-italic": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.24.0.tgz",
+ "integrity": "sha512-mf3cbNlbMPUNj3IyUkIke+o3ZpOUrtVeY5Yqs5IM/VhkUUh/PdIzqw74VuqEAJ0Z4oZ6nNDHeYLrl3Be1j99lQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-link": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-3.24.0.tgz",
+ "integrity": "sha512-MwMoNGG2mL5XGFV1tEGunBRglwsIbW+ZOB2QnKiv+Mcbi2JCWMrorndJZBqpVPR5nM+Bef2KnpchEJmYlQLvKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "linkifyjs": "^4.3.3"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "3.24.0",
+ "@tiptap/pm": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-list": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.24.0.tgz",
+ "integrity": "sha512-GcxDVMMmDGj7OFTBrV7JpVgr5wxlr2vmjwH7U8QxZX7OJI5vrsMYl/U6KRTvUpG8wP+Zmo5jRlLM+BbL+a/W3g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "3.24.0",
+ "@tiptap/pm": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-list-item": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-3.24.0.tgz",
+ "integrity": "sha512-zl/U3viJiV9OzkKM37AHIUN1af1TSLrcbHUUoNLkfJ33Nq+NlpaXpCVK0rKRqiLFJf7zk/a5KWG5CrOy9TxjKA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/extension-list": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-list-keymap": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-list-keymap/-/extension-list-keymap-3.24.0.tgz",
+ "integrity": "sha512-69fKcrngYGEKWNn4R5oLwl0YuV3FY4kufEValVcjnihUmqJTE1vx+fwctYoTsOGnIuNGpUIQ7f9YDD/0w34qBw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/extension-list": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-mention": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-mention/-/extension-mention-3.24.0.tgz",
+ "integrity": "sha512-c68AYrEoHJ4vlBvt5stBUTveKXiNwt5BxaQxgq2R4OXjc3VMoh+XJqo1bBbMNHEJfuGMNpcdfZ2zf09jnBf8/A==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "3.24.0",
+ "@tiptap/pm": "3.24.0",
+ "@tiptap/suggestion": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-ordered-list": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-3.24.0.tgz",
+ "integrity": "sha512-buRa6bmBDw0TztH+rAcusIye14DiLDS+yGheo6GiNCTD7kKJnksXagBdxvip3jhW5sx7gyAKvoBmvGSg1BbsGA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/extension-list": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-paragraph": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-3.24.0.tgz",
+ "integrity": "sha512-wD06aB6hO7LgcrlhGiw7I64k2tus9kNoICX5R+UecBSB1DVJdzKvXoXL2kPNv4DqYvljHdkIeK/OpuOTQd6MJA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-placeholder": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-3.24.0.tgz",
+ "integrity": "sha512-3jfYYCIuwMADhvZ92vB6c80YiTmgTSFR23JqyLps8qkQtV59Va5CBYpwJtSs1+VrbCVnNxhZBHhVXusZO3uRkA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/extensions": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-strike": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-3.24.0.tgz",
+ "integrity": "sha512-sfN1iQs6Fdlorrfe8wipDkTPwu/Egx3s2fkY7TAWusTGFHwlovuRUGFKqCL9dI4N3u6uqUMpEuWmQNgv+aQGjQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-table": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-3.24.0.tgz",
+ "integrity": "sha512-lr5elob3uJnB+ltgqPDEeVQmIPRx6JoS0I6z93tOgKsI2mIsaw5ErghteeiCTpExdyax7aWR0fn5pZzLVDQL8A==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "3.24.0",
+ "@tiptap/pm": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-table-cell": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-table-cell/-/extension-table-cell-3.24.0.tgz",
+ "integrity": "sha512-jg62iVc1L8tOycSuwZ5i82EGKGuvelCIkpFX7wvHMl9EiPX9fv70enmb4ua9DyLgRtYg/MNrGOdUATRpxWsFRA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/extension-table": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-table-header": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-table-header/-/extension-table-header-3.24.0.tgz",
+ "integrity": "sha512-z1TGVoBD5lVX48hpKcrQY5vETB8aCR61Enk5wGl45zNO/sMEMNxYfDKTkMydyOFUWfGqvOPnjDHJqJuT36/19w==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/extension-table": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-table-row": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-table-row/-/extension-table-row-3.24.0.tgz",
+ "integrity": "sha512-kASEYMWZF9ZqO6DRGz0LiD11c3LPfzOlFiJQVDrOyLP7xkJpHzIAKH0K+8u4aohOziDLn8nz+8I+3xjRS46oGg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/extension-table": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-task-item": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-task-item/-/extension-task-item-3.24.0.tgz",
+ "integrity": "sha512-2VtX6VOp4SJk5E/VHeSC2Acfbge6h69TUBhF7hFebbiCDyB73xCaPKcuClIyzOlWJgpWz2ZPXwVB+Y3meXw+yw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/extension-list": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-task-list": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-task-list/-/extension-task-list-3.24.0.tgz",
+ "integrity": "sha512-EIbB/WQ7L92WfaY5AF9upH93nIgBHjGt5zwPYoX+ZTpBncebr1AEDF7rc+LHNuSa1g1ToOKbFB88ep02IIXOjA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/extension-list": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-text": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.24.0.tgz",
+ "integrity": "sha512-Im7keLPEihxm3+LyF+drYCoaOY5hlq35lvHAp/el6M8pJ/scts88HrYpdR1Yc4BtpZBIhfHSyWgPaupI4qwdeg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extension-underline": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-3.24.0.tgz",
+ "integrity": "sha512-D4W4X3UMq9dLVIOfPB9+UodQ4eAJ8yDcm8qFWAwq0a15YWH6bnwulCuIdV+U5dEG+yaRxN8haB9GrrID9jmrSA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/extensions": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.24.0.tgz",
+ "integrity": "sha512-z6gRYzy2ucJp07OQ0F2W07NxyhMTxPYH1ia2eGiQkWax1i56oExpjMsDHP8THWlg8Tb7NnbfKpkfh881EsmofA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "3.24.0",
+ "@tiptap/pm": "3.24.0"
+ }
+ },
+ "node_modules/@tiptap/pm": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.24.0.tgz",
+ "integrity": "sha512-QQP/78ryOZDN99gNBV7dgh69/8AYaOYQYFklq/iR+ZRFaaL3+qqHFvPVJapGkzPdymBgNJ34xjFM8n5pJ4QmMg==",
+ "license": "MIT",
+ "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-inputrules": "^1.4.0",
+ "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"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ }
+ },
+ "node_modules/@tiptap/react": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-3.24.0.tgz",
+ "integrity": "sha512-KxnrlQbzOgA02EMsfuGGHtNhfkJQGqVlQttmQctI9DOl/F3gcaRqg+wNTBY1Fof8yDaZ8Z1LL1F0C05W0o3vUw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/use-sync-external-store": "^0.0.6",
+ "fast-equals": "^5.3.3",
+ "use-sync-external-store": "^1.4.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "optionalDependencies": {
+ "@tiptap/extension-bubble-menu": "^3.24.0",
+ "@tiptap/extension-floating-menu": "^3.24.0"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "3.24.0",
+ "@tiptap/pm": "3.24.0",
+ "@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"
+ }
+ },
+ "node_modules/@tiptap/starter-kit": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-3.24.0.tgz",
+ "integrity": "sha512-Ef4PCP96vcY2GonXN9J0M8iC6zvxPTmQlL/QZiCwuYqqnH/hNpYIjNSQdTndiDpxRKofa32Sr2HWktgEnL32Bg==",
+ "license": "MIT",
+ "dependencies": {
+ "@tiptap/core": "^3.24.0",
+ "@tiptap/extension-blockquote": "^3.24.0",
+ "@tiptap/extension-bold": "^3.24.0",
+ "@tiptap/extension-bullet-list": "^3.24.0",
+ "@tiptap/extension-code": "^3.24.0",
+ "@tiptap/extension-code-block": "^3.24.0",
+ "@tiptap/extension-document": "^3.24.0",
+ "@tiptap/extension-dropcursor": "^3.24.0",
+ "@tiptap/extension-gapcursor": "^3.24.0",
+ "@tiptap/extension-hard-break": "^3.24.0",
+ "@tiptap/extension-heading": "^3.24.0",
+ "@tiptap/extension-horizontal-rule": "^3.24.0",
+ "@tiptap/extension-italic": "^3.24.0",
+ "@tiptap/extension-link": "^3.24.0",
+ "@tiptap/extension-list": "^3.24.0",
+ "@tiptap/extension-list-item": "^3.24.0",
+ "@tiptap/extension-list-keymap": "^3.24.0",
+ "@tiptap/extension-ordered-list": "^3.24.0",
+ "@tiptap/extension-paragraph": "^3.24.0",
+ "@tiptap/extension-strike": "^3.24.0",
+ "@tiptap/extension-text": "^3.24.0",
+ "@tiptap/extension-underline": "^3.24.0",
+ "@tiptap/extensions": "^3.24.0",
+ "@tiptap/pm": "^3.24.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ }
+ },
+ "node_modules/@tiptap/suggestion": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/suggestion/-/suggestion-3.24.0.tgz",
+ "integrity": "sha512-UlLIij1fxFy7tbCmqUoInWRijzsi8hsbaXKCx6L3KvLXtxHb4hMnDhd6W++rOk9Q1hDpmNf8qNIX498q/ZNstw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "3.24.0",
+ "@tiptap/pm": "3.24.0"
+ }
+ },
+ "node_modules/@types/dompurify": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz",
+ "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/trusted-types": "*"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "22.19.19",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.19.tgz",
+ "integrity": "sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.15",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.15.tgz",
+ "integrity": "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==",
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.2.0"
+ }
+ },
+ "node_modules/@types/trusted-types": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
+ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
+ "devOptional": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/use-sync-external-store": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
+ "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
+ "license": "MIT"
+ },
+ "node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "license": "MIT"
+ },
+ "node_modules/axios": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz",
+ "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==",
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.16.0",
+ "form-data": "^4.0.5",
+ "https-proxy-agent": "^5.0.1",
+ "proxy-from-env": "^2.1.0"
+ }
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.10.33",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.33.tgz",
+ "integrity": "sha512-bA6+tcSLpz2tIEdDXZPpPTIuxBcC4+w6SieaYyfigIa4h8GlFxbA17v22Vx3JUtuZQj9SgOsnbK+aTBzyDyEuw==",
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/bootstrap": {
+ "version": "5.3.8",
+ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz",
+ "integrity": "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/twbs"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/bootstrap"
+ }
+ ],
+ "license": "MIT",
+ "peerDependencies": {
+ "@popperjs/core": "^2.11.8"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001793",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz",
+ "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/client-only": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
+ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
+ "license": "MIT"
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dompurify": {
+ "version": "3.4.7",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.7.tgz",
+ "integrity": "sha512-2jBxDJY4RR06tQNy4w5FlFH7kfxsQZlufd0sbv+chfHCxeJwrFw2baUDsSwvBISD4K4RDbd0PTfy3uNXsR6siA==",
+ "license": "(MPL-2.0 OR Apache-2.0)",
+ "optionalDependencies": {
+ "@types/trusted-types": "^2.0.7"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
+ "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/fast-equals": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz",
+ "integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
+ "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
+ "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/highlight.js": {
+ "version": "11.11.1",
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz",
+ "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/linkifyjs": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.3.tgz",
+ "integrity": "sha512-P8aEP5U/D1/IlTY2OeYsErdwh9bGuLE30NcXtKEjgdHcahveQoQwM2yZNsioQHsWFz0P7KKudisbrzCgR0sDHg==",
+ "license": "MIT"
+ },
+ "node_modules/marked": {
+ "version": "18.0.4",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.4.tgz",
+ "integrity": "sha512-c/BTaKzg0G6ezQx97DAkYU7k0HM6ys0FqYeKBL6hlBByZwy+ycA1+f0vDdjMHKKeEjdgkx0GOv9Il6D+85cOqA==",
+ "license": "MIT",
+ "bin": {
+ "marked": "bin/marked.js"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/marked-highlight": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/marked-highlight/-/marked-highlight-2.2.4.tgz",
+ "integrity": "sha512-PZxisNMJDduSjc0q6uvjsnqqHCXc9s0eyzxDO9sB1eNGJnd/H1/Fu+z6g/liC1dfJdFW4SftMwMlLvsBhUPrqQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "marked": ">=4 <19"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.12",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
+ "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/next": {
+ "version": "16.2.6",
+ "resolved": "https://registry.npmjs.org/next/-/next-16.2.6.tgz",
+ "integrity": "sha512-qOVgKJg1+At15NpeUP+eJgCHvTCgXsogweq87Ri/Ix7PkqQHg4sdaXmSFqKlgaIXE4kW0g25LE68W87UANlHtw==",
+ "license": "MIT",
+ "dependencies": {
+ "@next/env": "16.2.6",
+ "@swc/helpers": "0.5.15",
+ "baseline-browser-mapping": "^2.9.19",
+ "caniuse-lite": "^1.0.30001579",
+ "postcss": "8.4.31",
+ "styled-jsx": "5.1.6"
+ },
+ "bin": {
+ "next": "dist/bin/next"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "optionalDependencies": {
+ "@next/swc-darwin-arm64": "16.2.6",
+ "@next/swc-darwin-x64": "16.2.6",
+ "@next/swc-linux-arm64-gnu": "16.2.6",
+ "@next/swc-linux-arm64-musl": "16.2.6",
+ "@next/swc-linux-x64-gnu": "16.2.6",
+ "@next/swc-linux-x64-musl": "16.2.6",
+ "@next/swc-win32-arm64-msvc": "16.2.6",
+ "@next/swc-win32-x64-msvc": "16.2.6",
+ "sharp": "^0.34.5"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.1.0",
+ "@playwright/test": "^1.51.1",
+ "babel-plugin-react-compiler": "*",
+ "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "sass": "^1.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@playwright/test": {
+ "optional": true
+ },
+ "babel-plugin-react-compiler": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/orderedmap": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz",
+ "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==",
+ "license": "MIT"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/postcss": {
+ "version": "8.4.31",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+ "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.6",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/prosemirror-changeset": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.4.1.tgz",
+ "integrity": "sha512-96WBLhOaYhJ+kPhLg3uW359Tz6I/MfcrQfL4EGv4SrcqKEMC1gmoGrXHecPE8eOwTVCJ4IwgfzM8fFad25wNfw==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-transform": "^1.0.0"
+ }
+ },
+ "node_modules/prosemirror-commands": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz",
+ "integrity": "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-model": "^1.0.0",
+ "prosemirror-state": "^1.0.0",
+ "prosemirror-transform": "^1.10.2"
+ }
+ },
+ "node_modules/prosemirror-dropcursor": {
+ "version": "1.8.2",
+ "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.2.tgz",
+ "integrity": "sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-state": "^1.0.0",
+ "prosemirror-transform": "^1.1.0",
+ "prosemirror-view": "^1.1.0"
+ }
+ },
+ "node_modules/prosemirror-gapcursor": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.4.1.tgz",
+ "integrity": "sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-keymap": "^1.0.0",
+ "prosemirror-model": "^1.0.0",
+ "prosemirror-state": "^1.0.0",
+ "prosemirror-view": "^1.0.0"
+ }
+ },
+ "node_modules/prosemirror-history": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.5.0.tgz",
+ "integrity": "sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-state": "^1.2.2",
+ "prosemirror-transform": "^1.0.0",
+ "prosemirror-view": "^1.31.0",
+ "rope-sequence": "^1.3.0"
+ }
+ },
+ "node_modules/prosemirror-inputrules": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.5.1.tgz",
+ "integrity": "sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-state": "^1.0.0",
+ "prosemirror-transform": "^1.0.0"
+ }
+ },
+ "node_modules/prosemirror-keymap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.3.tgz",
+ "integrity": "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-state": "^1.0.0",
+ "w3c-keyname": "^2.2.0"
+ }
+ },
+ "node_modules/prosemirror-model": {
+ "version": "1.25.7",
+ "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.7.tgz",
+ "integrity": "sha512-A79aN8QEFUwI6cax8Yq4Rpcx1TJZ3Kagn+ii7qLo4/V8H3mMiHrhFyhTyHHvpSnOgMPpWiDGSwM3etwrxE50ug==",
+ "license": "MIT",
+ "dependencies": {
+ "orderedmap": "^2.0.0"
+ }
+ },
+ "node_modules/prosemirror-schema-list": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz",
+ "integrity": "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-model": "^1.0.0",
+ "prosemirror-state": "^1.0.0",
+ "prosemirror-transform": "^1.7.3"
+ }
+ },
+ "node_modules/prosemirror-state": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz",
+ "integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-model": "^1.0.0",
+ "prosemirror-transform": "^1.0.0",
+ "prosemirror-view": "^1.27.0"
+ }
+ },
+ "node_modules/prosemirror-tables": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.8.5.tgz",
+ "integrity": "sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==",
+ "license": "MIT",
+ "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"
+ }
+ },
+ "node_modules/prosemirror-transform": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.12.0.tgz",
+ "integrity": "sha512-GxboyN4AMIsoHNtz5uf2r2Ru551i5hWeCMD6E2Ib4Eogqoub0NflniaBPVQ4MrGE5yZ8JV9tUHg9qcZTTrcN4w==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-model": "^1.21.0"
+ }
+ },
+ "node_modules/prosemirror-view": {
+ "version": "1.41.8",
+ "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.8.tgz",
+ "integrity": "sha512-TnKDdohEatgyZNGCDWIdccOHXhYloJwbwU+phw/a23KBvJIR9lWQWW7WHHK3vBdOLDNuF7TaX98GObUZOWkOnA==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-model": "^1.20.0",
+ "prosemirror-state": "^1.0.0",
+ "prosemirror-transform": "^1.1.0"
+ }
+ },
+ "node_modules/proxy-from-env": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
+ "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.2.6",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz",
+ "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.6",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz",
+ "integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.6"
+ }
+ },
+ "node_modules/rope-sequence": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz",
+ "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==",
+ "license": "MIT"
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "7.8.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz",
+ "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==",
+ "license": "ISC",
+ "optional": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/sharp": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
+ "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@img/colour": "^1.0.0",
+ "detect-libc": "^2.1.2",
+ "semver": "^7.7.3"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "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"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/styled-jsx": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
+ "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==",
+ "license": "MIT",
+ "dependencies": {
+ "client-only": "0.0.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "@babel/core": {
+ "optional": true
+ },
+ "babel-plugin-macros": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tippy.js": {
+ "version": "6.3.7",
+ "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz",
+ "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@popperjs/core": "^2.9.0"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/use-sync-external-store": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
+ "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/w3c-keyname": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
+ "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
+ "license": "MIT"
+ },
+ "node_modules/zustand": {
+ "version": "5.0.14",
+ "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.14.tgz",
+ "integrity": "sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20.0"
+ },
+ "peerDependencies": {
+ "@types/react": ">=18.0.0",
+ "immer": ">=9.0.6",
+ "react": ">=18.0.0",
+ "use-sync-external-store": ">=1.2.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "immer": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "use-sync-external-store": {
+ "optional": true
+ }
+ }
+ }
+ }
+}
diff --git a/frontend_new/package.json b/frontend_new/package.json
new file mode 100644
index 0000000..ede6383
--- /dev/null
+++ b/frontend_new/package.json
@@ -0,0 +1,45 @@
+{
+ "name": "noteapp-frontend-next",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build",
+ "start": "next start",
+ "lint": "next lint"
+ },
+ "dependencies": {
+ "@mdi/font": "^7.4.47",
+ "@tiptap/extension-link": "^3.24.0",
+ "@tiptap/extension-mention": "^3.24.0",
+ "@tiptap/extension-placeholder": "^3.24.0",
+ "@tiptap/extension-table": "^3.24.0",
+ "@tiptap/extension-table-cell": "^3.24.0",
+ "@tiptap/extension-table-header": "^3.24.0",
+ "@tiptap/extension-table-row": "^3.24.0",
+ "@tiptap/extension-task-item": "^3.24.0",
+ "@tiptap/extension-task-list": "^3.24.0",
+ "@tiptap/pm": "^3.24.0",
+ "@tiptap/react": "^3.24.0",
+ "@tiptap/starter-kit": "^3.24.0",
+ "@tiptap/suggestion": "^3.24.0",
+ "axios": "^1.7.0",
+ "bootstrap": "^5.3.0",
+ "dompurify": "^3.0.0",
+ "highlight.js": "^11.11.1",
+ "marked": "^18.0.4",
+ "marked-highlight": "^2.2.4",
+ "next": "16.2.6",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0",
+ "tippy.js": "^6.3.7",
+ "zustand": "^5.0.0"
+ },
+ "devDependencies": {
+ "@types/dompurify": "^3.0.0",
+ "@types/node": "^22.0.0",
+ "@types/react": "^19.0.0",
+ "@types/react-dom": "^19.0.0",
+ "typescript": "^5.0.0"
+ }
+}
diff --git a/frontend_new/src/app/admin/layout.tsx b/frontend_new/src/app/admin/layout.tsx
new file mode 100644
index 0000000..f197b4e
--- /dev/null
+++ b/frontend_new/src/app/admin/layout.tsx
@@ -0,0 +1,51 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import { useRouter } from "next/navigation";
+import { useAuthStore } from "@/stores/authStore";
+import Navbar from "@/components/Navbar";
+
+export default function AdminLayout({ children }: { children: React.ReactNode }) {
+ const router = useRouter();
+ const ensureInitialized = useAuthStore((s) => s.ensureInitialized);
+ const hasPermission = useAuthStore((s) => s.hasPermission);
+
+ const [authChecked, setAuthChecked] = useState(false);
+
+ useEffect(() => {
+ const theme = localStorage.getItem("theme") === "dark" ? "dark" : "light";
+ document.documentElement.setAttribute("data-bs-theme", theme);
+
+ ensureInitialized().then(() => {
+ const state = useAuthStore.getState();
+ if (!state.user) {
+ router.replace("/login");
+ } else if (!state.hasPermission("admin.access") && !state.hasPermission("*")) {
+ router.replace("/dashboard");
+ } else {
+ setAuthChecked(true);
+ }
+ });
+ }, []);
+
+ if (!authChecked) {
+ return (
+
+ );
+ }
+
+ return (
+
+ );
+}
diff --git a/frontend_new/src/app/admin/page.tsx b/frontend_new/src/app/admin/page.tsx
new file mode 100644
index 0000000..8b94c5c
--- /dev/null
+++ b/frontend_new/src/app/admin/page.tsx
@@ -0,0 +1,887 @@
+"use client";
+
+import { useEffect, useState, useCallback } from "react";
+import { useRouter } from "next/navigation";
+import apiClient from "@/lib/apiClient";
+import AdminUserModal from "@/components/AdminUserModal";
+import AdminGroupModal from "@/components/AdminGroupModal";
+import AdminSpaceModal from "@/components/AdminSpaceModal";
+import AdminProviderModal from "@/components/AdminProviderModal";
+import ConfirmActionModal from "@/components/ConfirmActionModal";
+
+type AdminTab = "users" | "groups" | "spaces" | "providers" | "featureFlags";
+
+interface AdminUser {
+ id: string;
+ username: string;
+ email: string;
+ is_active: boolean;
+ created_at: string;
+ group_ids?: string[];
+}
+
+interface AdminGroup {
+ id: string;
+ name: string;
+ description: string;
+ is_system: boolean;
+ permissions: string[];
+}
+
+interface AdminSpace {
+ id: string;
+ name: string;
+ description: string;
+ icon?: string;
+ is_public: boolean;
+}
+
+interface AuthProvider {
+ id: string;
+ name: string;
+ type?: string;
+ client_id?: string;
+ authorization_url?: string;
+ token_url?: string;
+ userinfo_url?: string;
+ id_token_claim?: string;
+ scopes?: string[];
+ is_active: boolean;
+}
+
+interface FeatureFlags {
+ registration_enabled: boolean;
+ provider_login_enabled: boolean;
+ public_sharing_enabled: boolean;
+ file_explorer_enabled: boolean;
+ s3_endpoint: string;
+ s3_bucket: string;
+ s3_region: string;
+ s3_access_key: string;
+ s3_secret_key: string;
+ s3_secret_key_set: boolean;
+}
+
+const TABS: Array<{ id: AdminTab; label: string }> = [
+ { id: "users", label: "Users" },
+ { id: "groups", label: "Permission Groups" },
+ { id: "spaces", label: "Spaces" },
+ { id: "providers", label: "Identity Providers" },
+ { id: "featureFlags", label: "Feature Flags" },
+];
+
+const defaultFlags = (): FeatureFlags => ({
+ registration_enabled: true,
+ provider_login_enabled: true,
+ public_sharing_enabled: true,
+ file_explorer_enabled: false,
+ s3_endpoint: "",
+ s3_bucket: "",
+ s3_region: "",
+ s3_access_key: "",
+ s3_secret_key: "",
+ s3_secret_key_set: false,
+});
+
+export default function AdminPage() {
+ const router = useRouter();
+ const [activeTab, setActiveTab] = useState("users");
+ const [showMobileSidebar, setShowMobileSidebar] = useState(false);
+ const [error, setError] = useState("");
+ const [successMessage, setSuccessMessage] = useState("");
+
+ // Users
+ const [users, setUsers] = useState([]);
+ const [loadingUsers, setLoadingUsers] = useState(false);
+ const [showUserModal, setShowUserModal] = useState(false);
+ const [selectedUser, setSelectedUser] = useState(null);
+ const [submittingUser, setSubmittingUser] = useState(false);
+
+ // Groups
+ const [groups, setGroups] = useState([]);
+ const [loadingGroups, setLoadingGroups] = useState(false);
+ const [showGroupModal, setShowGroupModal] = useState(false);
+ const [groupModalMode, setGroupModalMode] = useState<"create" | "edit">("create");
+ const [selectedGroup, setSelectedGroup] = useState(null);
+ const [submittingGroup, setSubmittingGroup] = useState(false);
+
+ // Spaces
+ const [spaces, setSpaces] = useState([]);
+ const [loadingSpaces, setLoadingSpaces] = useState(false);
+ const [showSpaceModal, setShowSpaceModal] = useState(false);
+ const [selectedSpace, setSelectedSpace] = useState(null);
+
+ // Providers
+ const [providers, setProviders] = useState([]);
+ const [loadingProviders, setLoadingProviders] = useState(false);
+ const [showProviderModal, setShowProviderModal] = useState(false);
+ const [providerModalMode, setProviderModalMode] = useState<"create" | "edit">("create");
+ const [selectedProvider, setSelectedProvider] = useState(null);
+ const [submittingProvider, setSubmittingProvider] = useState(false);
+
+ // Delete confirm
+ const [confirmVisible, setConfirmVisible] = useState(false);
+ const [confirmBusy, setConfirmBusy] = useState(false);
+ const [confirmIntent, setConfirmIntent] = useState<{
+ type: "user" | "group" | "provider";
+ payload: AdminUser | AdminGroup | AuthProvider | null;
+ }>({ type: "user", payload: null });
+
+ // Feature flags
+ const [featureFlags, setFeatureFlags] = useState(defaultFlags());
+ const [loadingFeatureFlags, setLoadingFeatureFlags] = useState(false);
+ const [savingFlags, setSavingFlags] = useState(false);
+
+ const flash = (msg: string) => {
+ setSuccessMessage(msg);
+ setTimeout(() => setSuccessMessage(""), 3500);
+ };
+
+ const clearError = () => setError("");
+
+ // ── Loaders ──────────────────────────────────────────────────────────────────
+
+ const loadUsers = useCallback(async () => {
+ setLoadingUsers(true);
+ clearError();
+ try {
+ const res = await apiClient.get("/api/v1/admin/users");
+ setUsers(Array.isArray(res.data) ? res.data : res.data?.users || []);
+ } catch {
+ setError("Failed to load users.");
+ } finally {
+ setLoadingUsers(false);
+ }
+ }, []);
+
+ const loadGroups = useCallback(async () => {
+ setLoadingGroups(true);
+ clearError();
+ try {
+ const res = await apiClient.get("/api/v1/admin/groups");
+ setGroups(Array.isArray(res.data) ? res.data : res.data?.groups || []);
+ } catch {
+ setError("Failed to load groups.");
+ } finally {
+ setLoadingGroups(false);
+ }
+ }, []);
+
+ const loadSpaces = useCallback(async () => {
+ setLoadingSpaces(true);
+ clearError();
+ try {
+ const res = await apiClient.get("/api/v1/admin/spaces");
+ setSpaces(Array.isArray(res.data) ? res.data : res.data?.spaces || []);
+ } catch {
+ setError("Failed to load spaces.");
+ } finally {
+ setLoadingSpaces(false);
+ }
+ }, []);
+
+ const loadProviders = useCallback(async () => {
+ setLoadingProviders(true);
+ clearError();
+ try {
+ const res = await apiClient.get("/api/v1/admin/auth/providers");
+ setProviders(res.data?.providers || []);
+ } catch {
+ setError("Failed to load providers.");
+ } finally {
+ setLoadingProviders(false);
+ }
+ }, []);
+
+ const loadFeatureFlagsData = useCallback(async () => {
+ setLoadingFeatureFlags(true);
+ clearError();
+ try {
+ const res = await apiClient.get("/api/v1/admin/feature-flags");
+ setFeatureFlags({
+ registration_enabled: !!res.data.registration_enabled,
+ provider_login_enabled: !!res.data.provider_login_enabled,
+ public_sharing_enabled: !!res.data.public_sharing_enabled,
+ file_explorer_enabled: !!res.data.file_explorer_enabled,
+ s3_endpoint: res.data.s3_endpoint || "",
+ s3_bucket: res.data.s3_bucket || "",
+ s3_region: res.data.s3_region || "",
+ s3_access_key: res.data.s3_access_key || "",
+ s3_secret_key: "",
+ s3_secret_key_set: !!res.data.s3_secret_key_set,
+ });
+ } catch {
+ setError("Failed to load feature flags.");
+ } finally {
+ setLoadingFeatureFlags(false);
+ }
+ }, []);
+
+ useEffect(() => {
+ loadUsers();
+ loadGroups();
+ loadSpaces();
+ loadProviders();
+ loadFeatureFlagsData();
+ }, [loadUsers, loadGroups, loadSpaces, loadProviders, loadFeatureFlagsData]);
+
+ const selectTab = (tab: AdminTab) => {
+ setActiveTab(tab);
+ setShowMobileSidebar(false);
+ setError("");
+ };
+
+ // ── User CRUD ─────────────────────────────────────────────────────────────────
+
+ const openEditUser = (u: AdminUser) => {
+ setSelectedUser({ ...u });
+ setShowUserModal(true);
+ };
+
+ const submitEditUser = async ({ group_ids }: { group_ids: string[] }) => {
+ if (!selectedUser) return;
+ setSubmittingUser(true);
+ clearError();
+ try {
+ const res = await apiClient.put(`/api/v1/admin/users/${selectedUser.id}/groups`, { group_ids });
+ setUsers((prev) => prev.map((u) => (u.id === selectedUser.id ? { ...u, ...res.data } : u)));
+ flash("User updated.");
+ setShowUserModal(false);
+ setSelectedUser(null);
+ } catch {
+ setError("Failed to update user groups.");
+ } finally {
+ setSubmittingUser(false);
+ }
+ };
+
+ const requestDeleteUser = (u: AdminUser) => {
+ setConfirmIntent({ type: "user", payload: u });
+ setConfirmVisible(true);
+ };
+
+ // ── Group CRUD ────────────────────────────────────────────────────────────────
+
+ const openCreateGroup = () => {
+ setGroupModalMode("create");
+ setSelectedGroup(null);
+ setShowGroupModal(true);
+ };
+
+ const openEditGroup = (g: AdminGroup) => {
+ setGroupModalMode("edit");
+ setSelectedGroup({ ...g });
+ setShowGroupModal(true);
+ };
+
+ const submitGroupModal = async (data: { name: string; description: string; permissions: string[] }) => {
+ setSubmittingGroup(true);
+ clearError();
+ try {
+ if (groupModalMode === "create") {
+ await apiClient.post("/api/v1/admin/groups", data);
+ flash("Group created.");
+ } else {
+ await apiClient.put(`/api/v1/admin/groups/${selectedGroup!.id}`, data);
+ flash("Group updated.");
+ }
+ setShowGroupModal(false);
+ setSelectedGroup(null);
+ await Promise.all([loadGroups(), loadUsers()]);
+ } catch {
+ setError(`Failed to ${groupModalMode === "create" ? "create" : "update"} group.`);
+ } finally {
+ setSubmittingGroup(false);
+ }
+ };
+
+ const requestDeleteGroup = (g: AdminGroup) => {
+ if (g.is_system) return;
+ setConfirmIntent({ type: "group", payload: g });
+ setConfirmVisible(true);
+ };
+
+ // ── Space CRUD ────────────────────────────────────────────────────────────────
+
+ const openEditSpace = (s: AdminSpace) => {
+ setSelectedSpace({ ...s });
+ setShowSpaceModal(true);
+ };
+
+ const onSpaceSaved = (updated: AdminSpace) => {
+ setSpaces((prev) => prev.map((s) => (s.id === updated.id ? { ...s, ...updated } : s)));
+ setSelectedSpace(updated);
+ flash("Space updated.");
+ };
+
+ const onSpaceDeleted = (deleted: AdminSpace) => {
+ setSpaces((prev) => prev.filter((s) => s.id !== deleted.id));
+ setShowSpaceModal(false);
+ setSelectedSpace(null);
+ flash(`Space "${deleted.name}" deleted.`);
+ };
+
+ // ── Provider CRUD ─────────────────────────────────────────────────────────────
+
+ const openCreateProvider = () => {
+ setProviderModalMode("create");
+ setSelectedProvider(null);
+ setShowProviderModal(true);
+ };
+
+ const openEditProvider = (p: AuthProvider) => {
+ setProviderModalMode("edit");
+ setSelectedProvider({ ...p });
+ setShowProviderModal(true);
+ };
+
+ const submitProviderModal = async (formData: object) => {
+ setSubmittingProvider(true);
+ clearError();
+ try {
+ if (providerModalMode === "create") {
+ await apiClient.post("/api/v1/admin/auth/providers", formData);
+ flash("Provider added.");
+ } else {
+ await apiClient.put(`/api/v1/admin/auth/providers/${selectedProvider!.id}`, formData);
+ flash("Provider updated.");
+ }
+ setShowProviderModal(false);
+ setSelectedProvider(null);
+ await loadProviders();
+ } catch {
+ setError(`Failed to ${providerModalMode === "create" ? "add" : "update"} provider.`);
+ } finally {
+ setSubmittingProvider(false);
+ }
+ };
+
+ const requestDeleteProvider = (p: AuthProvider) => {
+ setShowProviderModal(false);
+ setConfirmIntent({ type: "provider", payload: { ...p } });
+ setConfirmVisible(true);
+ };
+
+ // ── Confirm delete ────────────────────────────────────────────────────────────
+
+ const confirmDelete = async () => {
+ if (confirmBusy) return;
+ setConfirmBusy(true);
+ clearError();
+ try {
+ const { type, payload } = confirmIntent;
+ if (type === "user") {
+ const u = payload as AdminUser;
+ await apiClient.delete(`/api/v1/admin/users/${u.id}`);
+ setUsers((prev) => prev.filter((x) => x.id !== u.id));
+ flash(`User "${u.username}" deleted.`);
+ } else if (type === "group") {
+ const g = payload as AdminGroup;
+ await apiClient.delete(`/api/v1/admin/groups/${g.id}`);
+ flash(`Group "${g.name}" deleted.`);
+ await Promise.all([loadGroups(), loadUsers()]);
+ } else if (type === "provider") {
+ const p = payload as AuthProvider;
+ await apiClient.delete(`/api/v1/admin/auth/providers/${p.id}`);
+ setProviders((prev) => prev.filter((x) => x.id !== p.id));
+ flash(`Provider "${p.name}" deleted.`);
+ }
+ setConfirmVisible(false);
+ setConfirmIntent({ type: "user", payload: null });
+ } catch {
+ setError("Delete failed.");
+ } finally {
+ setConfirmBusy(false);
+ }
+ };
+
+ const closeConfirm = () => {
+ if (confirmBusy) return;
+ setConfirmVisible(false);
+ };
+
+ // ── Feature Flags ─────────────────────────────────────────────────────────────
+
+ const saveFeatureFlags = async () => {
+ setSavingFlags(true);
+ clearError();
+ try {
+ const res = await apiClient.put("/api/v1/admin/feature-flags", {
+ registration_enabled: featureFlags.registration_enabled,
+ provider_login_enabled: featureFlags.provider_login_enabled,
+ public_sharing_enabled: featureFlags.public_sharing_enabled,
+ file_explorer_enabled: featureFlags.file_explorer_enabled,
+ s3_endpoint: featureFlags.s3_endpoint,
+ s3_bucket: featureFlags.s3_bucket,
+ s3_region: featureFlags.s3_region,
+ s3_access_key: featureFlags.s3_access_key,
+ s3_secret_key: featureFlags.s3_secret_key,
+ });
+ setFeatureFlags({
+ registration_enabled: !!res.data.registration_enabled,
+ provider_login_enabled: !!res.data.provider_login_enabled,
+ public_sharing_enabled: !!res.data.public_sharing_enabled,
+ file_explorer_enabled: !!res.data.file_explorer_enabled,
+ s3_endpoint: res.data.s3_endpoint || "",
+ s3_bucket: res.data.s3_bucket || "",
+ s3_region: res.data.s3_region || "",
+ s3_access_key: res.data.s3_access_key || "",
+ s3_secret_key: "",
+ s3_secret_key_set: !!res.data.s3_secret_key_set,
+ });
+ flash("Feature flags saved.");
+ } catch {
+ setError("Failed to save feature flags.");
+ } finally {
+ setSavingFlags(false);
+ }
+ };
+
+ // ── Helpers ───────────────────────────────────────────────────────────────────
+
+ const formatDate = (d: string) => (d ? new Date(d).toLocaleDateString(undefined, { year: "numeric", month: "short", day: "numeric" }) : "—");
+
+ const getUserGroupSummary = (u: AdminUser) => {
+ const ids = u.group_ids || [];
+ if (!ids.length) return "No groups";
+ const names = ids.map((id) => groups.find((g) => g.id === id)?.name).filter(Boolean);
+ return names.length ? names.join(", ") : "No groups";
+ };
+
+ const confirmTitle = confirmIntent.type === "user" ? "Delete User" : confirmIntent.type === "group" ? "Delete Group" : "Delete Identity Provider";
+
+ const confirmMessage =
+ confirmIntent.type === "user"
+ ? `Delete user "${(confirmIntent.payload as AdminUser)?.username}"? This cannot be undone.`
+ : confirmIntent.type === "group"
+ ? `Delete group "${(confirmIntent.payload as AdminGroup)?.name}"? This cannot be undone.`
+ : `Delete identity provider "${(confirmIntent.payload as AuthProvider)?.name}"? This cannot be undone.`;
+
+ // ── Render ────────────────────────────────────────────────────────────────────
+
+ return (
+
+
+
+
+
+
Admin Panel
+
Manage users, groups, spaces, and identity providers.
+
+
+
+
+
+ {error &&
{error}
}
+ {successMessage &&
{successMessage}
}
+
+
+ {showMobileSidebar &&
setShowMobileSidebar(false)} />}
+
+
+
+
+ {/* ── Users ── */}
+ {activeTab === "users" && (
+
+
+
+
All Users
+
+
+ {loadingUsers ? (
+
Loading users…
+ ) : users.length === 0 ? (
+
No users found.
+ ) : (
+
+ {users.map((u) => (
+
+
+
+ {u.username}
+ {u.is_active ? "Active" : "Inactive"}
+
+
+
+
+
Joined
+
{formatDate(u.created_at)}
+
+
+
Groups
+
{getUserGroupSummary(u)}
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+ )}
+
+
+ )}
+
+ {/* ── Groups ── */}
+ {activeTab === "groups" && (
+
+
+
+
Permission Groups
+
+
+
+
+
+ {loadingGroups ? (
+
Loading groups…
+ ) : groups.length === 0 ? (
+
No groups created yet.
+ ) : (
+
+ {groups.map((g) => (
+
+
+
+ {g.name}
+ {g.is_system && System}
+
+
{g.description || "No description"}
+
+ {(g.permissions || []).length} permission{(g.permissions || []).length === 1 ? "" : "s"}
+
+
+
+
+
+
+
+ ))}
+
+ )}
+
+
+ )}
+
+ {/* ── Spaces ── */}
+ {activeTab === "spaces" && (
+
+
+
+
All Spaces
+
+
+ {loadingSpaces ? (
+
Loading spaces…
+ ) : spaces.length === 0 ? (
+
No spaces found.
+ ) : (
+
+ {spaces.map((s) => (
+
+
+
{s.name}
+
{s.description || "No description"}
+
+
+ {s.is_public ? "Public" : "Private"}
+
+
+
+ ))}
+
+ )}
+
+
+ )}
+
+ {/* ── Providers ── */}
+ {activeTab === "providers" && (
+
+
+
+
Identity Providers
+
+
+
+
+
+ {loadingProviders ? (
+
Loading providers…
+ ) : providers.length === 0 ? (
+
No providers configured yet.
+ ) : (
+
+ {providers.map((p) => (
+
+
+
+ {p.name}
+
+
+
+ ))}
+
+ )}
+
+
+ )}
+
+ {/* ── Feature Flags ── */}
+ {activeTab === "featureFlags" && (
+
+
+
+
Application Feature Flags
+
+
+ {loadingFeatureFlags ? (
+
Loading feature flags…
+ ) : (
+
+
setFeatureFlags((f) => ({ ...f, registration_enabled: v }))}
+ />
+ setFeatureFlags((f) => ({ ...f, provider_login_enabled: v }))}
+ />
+ setFeatureFlags((f) => ({ ...f, public_sharing_enabled: v }))}
+ />
+ {/* File Explorer with S3 config */}
+
+
+
+
Enable File Explorer
+
Allow users to browse and insert files from an S3 bucket directly into notes.
+
+
+ setFeatureFlags((f) => ({ ...f, file_explorer_enabled: e.target.checked }))}
+ />
+
+
+ {featureFlags.file_explorer_enabled && (
+
+
+
+ setFeatureFlags((f) => ({ ...f, s3_endpoint: e.target.value }))}
+ />
+
+
+
+ setFeatureFlags((f) => ({ ...f, s3_bucket: e.target.value }))}
+ />
+
+
+
+ setFeatureFlags((f) => ({ ...f, s3_region: e.target.value }))}
+ />
+
+
+
+ setFeatureFlags((f) => ({ ...f, s3_access_key: e.target.value }))}
+ />
+
+
+
+
setFeatureFlags((f) => ({ ...f, s3_secret_key: e.target.value }))}
+ />
+ {featureFlags.s3_secret_key_set && !featureFlags.s3_secret_key && (
+
+ Secret key is set
+
+ )}
+
+
+ )}
+
+
+
+
+
+
+ )}
+
+
+ )}
+
+
+
+ {/* Modals */}
+ {showUserModal && selectedUser && (
+
{
+ setShowUserModal(false);
+ setSelectedUser(null);
+ }}
+ onSubmit={submitEditUser}
+ />
+ )}
+
+ {showGroupModal && (
+ {
+ setShowGroupModal(false);
+ setSelectedGroup(null);
+ }}
+ onSubmit={submitGroupModal}
+ />
+ )}
+
+ {showSpaceModal && selectedSpace && (
+ {
+ setShowSpaceModal(false);
+ setSelectedSpace(null);
+ }}
+ onSaved={onSpaceSaved}
+ onDeleted={onSpaceDeleted}
+ />
+ )}
+
+ {showProviderModal && (
+ {
+ setShowProviderModal(false);
+ setSelectedProvider(null);
+ }}
+ onSubmit={submitProviderModal}
+ onDelete={requestDeleteProvider}
+ />
+ )}
+
+
+
+ );
+}
+
+function FlagItem({ id, title, description, checked, onChange }: { id: string; title: string; description: string; checked: boolean; onChange: (v: boolean) => void }) {
+ return (
+
+
+
{title}
+
{description}
+
+
+ onChange(e.target.checked)} />
+
+
+ );
+}
diff --git a/frontend_new/src/app/dashboard/layout.tsx b/frontend_new/src/app/dashboard/layout.tsx
new file mode 100644
index 0000000..b567a95
--- /dev/null
+++ b/frontend_new/src/app/dashboard/layout.tsx
@@ -0,0 +1,167 @@
+"use client";
+
+import { useEffect, useRef, useState } from "react";
+import { useRouter } from "next/navigation";
+import { useAuthStore } from "@/stores/authStore";
+import { Space, useSpaceStore } from "@/stores/spaceStore";
+import Navbar from "@/components/Navbar";
+import Sidebar from "@/components/Sidebar";
+import SpaceSettingsModal from "@/components/SpaceSettingsModal";
+import apiClient from "@/lib/apiClient";
+
+export default function DashboardLayout({ children }: { children: React.ReactNode }) {
+ const router = useRouter();
+ const ensureInitialized = useAuthStore((s) => s.ensureInitialized);
+ const fetchSpaces = useSpaceStore((s) => s.fetchSpaces);
+ const currentSpace = useSpaceStore((s) => s.currentSpace!);
+
+ const [authChecked, setAuthChecked] = useState(false);
+ const [showSidebar, setShowSidebar] = useState(false);
+ const navbarRef = useRef
(null);
+ const [navbarHeight, setNavbarHeight] = useState(56);
+
+ const [showCreateCategoryModal, setShowCreateCategoryModal] = useState(false);
+ const [showSpaceSettingsModal, setShowSpaceSettingsModal] = useState(false);
+
+ useEffect(() => {
+ const theme = localStorage.getItem("theme") === "dark" ? "dark" : "light";
+ document.documentElement.setAttribute("data-bs-theme", theme);
+
+ ensureInitialized().then(() => {
+ if (!useAuthStore.getState().user) {
+ router.replace("/login");
+ } else {
+ setAuthChecked(true);
+ fetchSpaces();
+ }
+ });
+ }, []);
+
+ useEffect(() => {
+ const el = navbarRef.current;
+ if (!el) return;
+ setNavbarHeight(el.offsetHeight);
+ const ro = new ResizeObserver(() => setNavbarHeight(el.offsetHeight));
+ ro.observe(el);
+ return () => ro.disconnect();
+ }, [authChecked]);
+
+ if (!authChecked) {
+ return (
+
+ );
+ }
+
+ function handleCreateCategory(name: string) {
+ apiClient.post(`/api/v1/spaces/${currentSpace?.id}/categories`, { name }).then(() => {
+ useSpaceStore.getState().fetchCategories(currentSpace?.id || "");
+ });
+ }
+
+ function handleSpaceSaved(_updatedSpace: Space) {
+ useSpaceStore.getState().fetchSpaces();
+ setShowSpaceSettingsModal(false);
+ }
+
+ function handleSpaceDeleted() {
+ useSpaceStore.getState().fetchSpaces();
+ setShowSpaceSettingsModal(false);
+ }
+
+ return (
+ <>
+
+
+
+
+ setShowSidebar(false)}
+ navbarHeight={navbarHeight}
+ onOpenCreateCategory={() => setShowCreateCategoryModal(true)}
+ onOpenSpaceSettings={() => setShowSpaceSettingsModal(true)}
+ />
+ {children}
+
+
+
+ {showCreateCategoryModal && (
+ setShowCreateCategoryModal(false)}
+ onSave={(name) => {
+ handleCreateCategory(name);
+ setShowCreateCategoryModal(false);
+ }}
+ />
+ )}
+
+ {showSpaceSettingsModal && currentSpace && (
+ setShowSpaceSettingsModal(false)}
+ onSaved={handleSpaceSaved}
+ onDeleted={handleSpaceDeleted}
+ />
+ )}
+ >
+ );
+}
+
+function CreateCategoryModal({ onClose, onSave }: { onClose: () => void; onSave: (name: string) => void }) {
+ const [categoryName, setCategoryName] = useState("");
+
+ function handleKeyDown(e: React.KeyboardEvent) {
+ if (e.key === "Enter") onSave(categoryName);
+ }
+
+ return (
+ e.target === e.currentTarget && onClose()}
+ >
+
+
+
+
Create Category
+
+
+
+
+
+ setCategoryName(e.target.value)}
+ onKeyDown={handleKeyDown}
+ autoFocus
+ />
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/frontend_new/src/app/dashboard/page.tsx b/frontend_new/src/app/dashboard/page.tsx
new file mode 100644
index 0000000..f324d0f
--- /dev/null
+++ b/frontend_new/src/app/dashboard/page.tsx
@@ -0,0 +1,315 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import { useRouter, useSearchParams } from "next/navigation";
+import { useAuthStore } from "@/stores/authStore";
+import { useSpaceStore, type Note, type TaskList, type Category } from "@/stores/spaceStore";
+import apiClient from "@/lib/apiClient";
+
+export default function DashboardPage() {
+ const router = useRouter();
+ const searchParams = useSearchParams();
+ const spaces = useSpaceStore((s) => s.spaces);
+ const currentSpace = useSpaceStore((s) => s.currentSpace);
+ const notes = useSpaceStore((s) => s.notes);
+ const taskLists = useSpaceStore((s) => s.taskLists);
+ const categoryTree = useSpaceStore((s) => s.categoryTree);
+ const notesLoading = useSpaceStore((s) => s.notesLoading);
+ const notesHasMore = useSpaceStore((s) => s.notesHasMore);
+ const fetchNotes = useSpaceStore((s) => s.fetchNotes);
+ const hasSpacePermission = useAuthStore((s) => s.hasSpacePermission);
+ const hasPermission = useAuthStore((s) => s.hasPermission);
+
+ const [searchQuery, setSearchQuery] = useState(searchParams?.get("search") ?? "");
+ const [searchResults, setSearchResults] = useState([]);
+ const [searchLoading, setSearchLoading] = useState(false);
+
+ const canCreateNotes = hasPermission("*") || hasSpacePermission(currentSpace, "notes.create");
+ const canCreateTasklists = hasPermission("*") || hasSpacePermission(currentSpace, "tasklists.create");
+ const canCreateSpaces = hasPermission("*") || hasPermission("spaces.create");
+
+ useEffect(() => {
+ const q = searchParams?.get("search");
+ if (q && currentSpace) {
+ setSearchQuery(q);
+ doSearch(q, currentSpace.id);
+ } else {
+ setSearchQuery("");
+ setSearchResults([]);
+ }
+ }, [searchParams, currentSpace]);
+
+ async function doSearch(q: string, spaceId: string) {
+ if (!q.trim()) return;
+ setSearchLoading(true);
+ try {
+ const res = await apiClient.get(`/api/v1/spaces/${spaceId}/notes/search`, {
+ params: { q, limit: 50 },
+ });
+ setSearchResults(res.data || []);
+ } catch {
+ setSearchResults([]);
+ } finally {
+ setSearchLoading(false);
+ }
+ }
+
+ const isSearch = !!(searchQuery && searchParams?.get("search"));
+
+ // Group notes and task lists by category id
+ const notesByCategory: Record = {};
+ const taskListsByCategory: Record = {};
+ const uncategorizedNotes: Note[] = [];
+ const uncategorizedTaskLists: TaskList[] = [];
+
+ for (const note of notes) {
+ if (note.category_id) {
+ (notesByCategory[note.category_id] ??= []).push(note);
+ } else {
+ uncategorizedNotes.push(note);
+ }
+ }
+ for (const tl of taskLists) {
+ if (tl.category_id) {
+ (taskListsByCategory[tl.category_id] ??= []).push(tl);
+ } else {
+ uncategorizedTaskLists.push(tl);
+ }
+ }
+
+ // Flatten category tree for lookup
+ function flattenTree(cats: Category[]): Category[] {
+ const result: Category[] = [];
+ function walk(list: Category[]) {
+ for (const c of list) {
+ result.push(c);
+ const children = c.subcategories ?? c.children ?? [];
+ if (children.length) walk(children);
+ }
+ }
+ walk(cats);
+ return result;
+ }
+ const flatCategories = flattenTree(categoryTree);
+
+ // ── No spaces ──────────────────────────────────────────────────────────────
+ if (spaces.length === 0) {
+ return (
+
+
+
+
No Spaces Yet
+
Create a space to start organising your notes.
+ {canCreateSpaces && (
+
+ )}
+
+
+ );
+ }
+
+ // ── No space selected ──────────────────────────────────────────────────────
+ if (!currentSpace) {
+ return (
+
+
+
+
Select a space to get started
+
+
+ );
+ }
+
+ return (
+ <>
+ {/* Toolbar */}
+
+
+
+
{currentSpace.name}
+
+
+
+
+ {/* Content */}
+
+ {/* Search results */}
+ {isSearch ? (
+
+
+ Search results for "{searchQuery}"
+ {!searchLoading && ` — ${searchResults.length} found`}
+
+ {searchLoading ? (
+
+ ) : searchResults.length === 0 ? (
+
No results found.
+ ) : (
+
+ {searchResults.map((note) => (
+ router.push(`/dashboard/spaces/${currentSpace.id}/notes/${note.id}`)}
+ />
+ ))}
+
+ )}
+
+ ) : notesLoading && notes.length === 0 ? (
+
+ ) : (
+
+ {/* Category sections */}
+ {flatCategories.map((cat) => {
+ const catNotes = notesByCategory[cat.id] ?? [];
+ const catTaskLists = taskListsByCategory[cat.id] ?? [];
+ return (
+
router.push(`/dashboard/spaces/${currentSpace.id}/notes/${id}`)}
+ onTaskListClick={(id) => router.push(`/dashboard/spaces/${currentSpace.id}/tasklists/${id}`)}
+ onNewNote={() => router.push(`/dashboard/spaces/${currentSpace.id}/notes/new?categoryId=${cat.id}`)}
+ onNewTaskList={() => router.push(`/dashboard/spaces/${currentSpace.id}/tasklists/new?categoryId=${cat.id}`)}
+ />
+ );
+ })}
+
+ {/* Uncategorized section — only show if there are items */}
+ {(uncategorizedNotes.length > 0 || uncategorizedTaskLists.length > 0) && (
+ router.push(`/dashboard/spaces/${currentSpace.id}/notes/${id}`)}
+ onTaskListClick={(id) => router.push(`/dashboard/spaces/${currentSpace.id}/tasklists/${id}`)}
+ onNewNote={() => router.push(`/dashboard/spaces/${currentSpace.id}/notes/new`)}
+ onNewTaskList={() => router.push(`/dashboard/spaces/${currentSpace.id}/tasklists/new`)}
+ />
+ )}
+
+ {/* Empty space */}
+ {flatCategories.length === 0 && uncategorizedNotes.length === 0 && uncategorizedTaskLists.length === 0 && (
+
+
+
No content yet. Create a category in the sidebar to get started.
+
+ )}
+
+ {notesHasMore && (
+
+
+
+ )}
+
+ )}
+
+ >
+ );
+}
+
+function CategorySection({
+ category,
+ notes,
+ taskLists,
+ canCreateNotes,
+ canCreateTasklists,
+ onNoteClick,
+ onTaskListClick,
+ onNewNote,
+ onNewTaskList,
+}: {
+ category: Category | null;
+ notes: Note[];
+ taskLists: TaskList[];
+ canCreateNotes: boolean;
+ canCreateTasklists: boolean;
+ onNoteClick: (id: string) => void;
+ onTaskListClick: (id: string) => void;
+ onNewNote: () => void;
+ onNewTaskList: () => void;
+}) {
+ return (
+
+
+
+
+ {category?.name ?? "Uncategorized"}
+
+
+
+
+ {notes.map((note) => (
+ onNoteClick(note.id)} />
+ ))}
+ {taskLists.map((tl) => (
+ onTaskListClick(tl.id)} />
+ ))}
+ {canCreateNotes && (
+
+ )}
+ {canCreateTasklists && (
+
+ )}
+
+
+ );
+}
+
+function NoteCard({ note, onClick }: { note: Note; onClick: () => void }) {
+ return (
+
+
+
+
+
+ {note.is_password_protected && }
+ {note.title || "Untitled"}
+
+
{new Date(note.updated_at).toLocaleDateString()}
+
+ );
+}
+
+function TaskListCard({ taskList, onClick }: { taskList: TaskList; onClick: () => void }) {
+ return (
+
+
+
+
+
{taskList.name}
+ {taskList.description &&
{taskList.description.slice(0, 60)}
}
+
+ );
+}
+
+function CreateCard({ icon, label, onClick }: { icon: string; label: string; onClick: () => void }) {
+ return (
+
+ );
+}
diff --git a/frontend_new/src/app/dashboard/spaces/[spaceId]/notes/[noteId]/NotePageClient.tsx b/frontend_new/src/app/dashboard/spaces/[spaceId]/notes/[noteId]/NotePageClient.tsx
new file mode 100644
index 0000000..e9a2054
--- /dev/null
+++ b/frontend_new/src/app/dashboard/spaces/[spaceId]/notes/[noteId]/NotePageClient.tsx
@@ -0,0 +1,572 @@
+"use client";
+
+import { useEffect, useRef, useState, useCallback } from "react";
+import { useRouter } from "next/navigation";
+import { useAuthStore } from "@/stores/authStore";
+import { useSpaceStore, type Note, type Category, type TaskList } from "@/stores/spaceStore";
+import apiClient from "@/lib/apiClient";
+import RichTextEditor from "@/components/RichTextEditor";
+
+/** Read real URL params from window.location — useParams() returns static
+ * placeholder values in a Next.js static export. */
+function getNoteParams(): { spaceId: string; noteId: string } {
+ if (typeof window === "undefined") return { spaceId: "", noteId: "" };
+ const m = window.location.pathname.match(/\/dashboard\/spaces\/([^/]+)\/notes\/([^/]+)/);
+ return { spaceId: m?.[1] ?? "", noteId: m?.[2] ?? "" };
+}
+
+type PasswordMode = "keep" | "set" | "remove";
+
+export default function NotePage() {
+ const { spaceId, noteId } = getNoteParams();
+ const router = useRouter();
+
+ const ensureInitialized = useAuthStore((s) => s.ensureInitialized);
+ const user = useAuthStore((s) => s.user);
+ const hasPermission = useAuthStore((s) => s.hasPermission);
+ const hasSpacePermission = useAuthStore((s) => s.hasSpacePermission);
+ const fetchCategories = useSpaceStore((s) => s.fetchCategories);
+ const categoryTree = useSpaceStore((s) => s.categoryTree);
+ const currentSpace = useSpaceStore((s) => s.currentSpace);
+ const selectSpace = useSpaceStore((s) => s.selectSpace);
+
+ const [authChecked, setAuthChecked] = useState(false);
+ const [note, setNote] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState("");
+ const [isEditing, setIsEditing] = useState(false);
+
+ // Password-lock state
+ const [unlocked, setUnlocked] = useState(false);
+ const [unlockPassword, setUnlockPassword] = useState("");
+ const [unlockError, setUnlockError] = useState("");
+ const [unlocking, setUnlocking] = useState(false);
+
+ // Editor state
+ const [title, setTitle] = useState("");
+ const [description, setDescription] = useState("");
+ const [content, setContent] = useState("");
+ const [tags, setTags] = useState("");
+ const [categoryId, setCategoryId] = useState("");
+ const [isPinned, setIsPinned] = useState(false);
+ const [isFavorite, setIsFavorite] = useState(false);
+ const [isPublic, setIsPublic] = useState(false);
+ const [passwordMode, setPasswordMode] = useState("keep");
+ const [newPassword, setNewPassword] = useState("");
+ const [saveStatus, setSaveStatus] = useState<"saved" | "saving" | "dirty">("saved");
+
+ // Task lists for @TaskList mentions
+ const [taskLists, setTaskLists] = useState([]);
+
+ const saveTimerRef = useRef | null>(null);
+
+ // Flat list of categories for dropdown
+ function flattenCategories(cats: Category[]): Category[] {
+ const result: Category[] = [];
+ function traverse(list: Category[]) {
+ for (const c of list) {
+ result.push(c);
+ const subs = c.subcategories ?? c.children ?? [];
+ if (subs.length) traverse(subs);
+ }
+ }
+ traverse(cats);
+ return result;
+ }
+
+ useEffect(() => {
+ ensureInitialized().then(() => {
+ if (!useAuthStore.getState().user) {
+ router.replace("/login");
+ } else {
+ setAuthChecked(true);
+ // Ensure space is selected
+ if (!useSpaceStore.getState().currentSpace || useSpaceStore.getState().currentSpace?.id !== spaceId) {
+ selectSpace(spaceId);
+ } else {
+ fetchCategories(spaceId);
+ }
+ }
+ });
+ }, []);
+
+ useEffect(() => {
+ if (!authChecked) return;
+ loadNote();
+ }, [authChecked]);
+
+ async function loadNote() {
+ setLoading(true);
+ setError("");
+ try {
+ const res = await apiClient.get(`/api/v1/spaces/${spaceId}/notes/${noteId}`);
+ const n: Note = res.data;
+ setNote(n);
+ setUnlocked(!n.is_password_protected);
+ if (!n.is_password_protected) populateEditor(n);
+ loadTaskLists();
+ } catch (e: unknown) {
+ const err = e as { response?: { status: number } };
+ if (err?.response?.status === 403) {
+ setError("Access denied.");
+ } else {
+ setError("Failed to load note.");
+ }
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ async function unlockNote() {
+ if (!unlockPassword.trim()) {
+ setUnlockError("Password is required.");
+ return;
+ }
+ setUnlocking(true);
+ setUnlockError("");
+ try {
+ const res = await apiClient.post(`/api/v1/spaces/${spaceId}/notes/${noteId}/unlock`, {
+ password: unlockPassword,
+ });
+ const n: Note = res.data;
+ setNote(n);
+ populateEditor(n);
+ setUnlocked(true);
+ setUnlockPassword("");
+ } catch {
+ setUnlockError("Incorrect password.");
+ } finally {
+ setUnlocking(false);
+ }
+ }
+
+ async function loadTaskLists() {
+ try {
+ const res = await apiClient.get(`/api/v1/spaces/${spaceId}/task-lists`);
+ setTaskLists(Array.isArray(res.data) ? res.data : []);
+ } catch {
+ // ignore
+ }
+ }
+
+ async function fetchTasksForList(taskListId: string) {
+ const [tasksRes, statusRes] = await Promise.all([
+ apiClient.get(`/api/v1/spaces/${spaceId}/tasks`, { params: { taskListId } }),
+ apiClient.get(`/api/v1/spaces/${spaceId}/task-lists/${taskListId}/statuses`),
+ ]);
+ const tasks = Array.isArray(tasksRes.data) ? tasksRes.data : [];
+ const statuses = Array.isArray(statusRes.data) ? statusRes.data : [];
+ const statusMap = new Map(statuses.map((s: { id: string; name: string; color: string }) => [s.id, s]));
+ return tasks
+ .filter((t: { parent_task_id: string | null }) => !t.parent_task_id)
+ .map((t: { id: string; title: string; status_id: string }) => {
+ const status = statusMap.get(t.status_id) as { name: string; color: string } | undefined;
+ return {
+ id: t.id,
+ title: t.title,
+ statusColor: status?.color ?? "#7c8596",
+ statusName: status?.name ?? "",
+ };
+ });
+ }
+
+ function populateEditor(n: Note) {
+ setTitle(n.title);
+ setDescription(n.description ?? "");
+ setContent(n.content ?? "");
+ setTags((n.tags ?? []).join(", "));
+ setCategoryId(n.category_id ?? "");
+ setIsPinned(n.is_pinned);
+ setIsFavorite(n.is_favorite);
+ setIsPublic(n.is_public);
+ setPasswordMode("keep");
+ setNewPassword("");
+ setSaveStatus("saved");
+ }
+
+ function startEditing() {
+ if (note) populateEditor(note);
+ setIsEditing(true);
+ }
+
+ function cancelEditing() {
+ setIsEditing(false);
+ if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
+ }
+
+ // Auto-save with 3s debounce
+ const scheduleSave = useCallback(() => {
+ setSaveStatus("dirty");
+ if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
+ saveTimerRef.current = setTimeout(() => {
+ performSave();
+ }, 3000);
+ }, [title, description, content, tags, categoryId, isPinned, isFavorite, isPublic, passwordMode, newPassword]);
+
+ async function performSave() {
+ setSaveStatus("saving");
+ try {
+ const tagList = tags
+ .split(",")
+ .map((t) => t.trim())
+ .filter(Boolean);
+ const catId = categoryId || null;
+ const body: Record = {
+ title,
+ description,
+ content,
+ tags: tagList,
+ category_id: catId,
+ is_pinned: isPinned,
+ is_favorite: isFavorite,
+ is_public: isPublic,
+ };
+ if (passwordMode === "set" && newPassword) {
+ body.note_password = newPassword;
+ } else if (passwordMode === "remove") {
+ body.note_password = "";
+ }
+ const res = await apiClient.put(`/api/v1/spaces/${spaceId}/notes/${noteId}`, body);
+ setNote(res.data);
+ setSaveStatus("saved");
+ } catch {
+ setSaveStatus("dirty");
+ }
+ }
+
+ async function deleteNote() {
+ if (!confirm("Delete this note? This cannot be undone.")) return;
+ try {
+ await apiClient.delete(`/api/v1/spaces/${spaceId}/notes/${noteId}`);
+ router.push(`/dashboard`);
+ } catch {
+ alert("Failed to delete note.");
+ }
+ }
+
+ const canEdit = hasPermission("*") || hasSpacePermission(currentSpace, "notes.edit");
+ const canDelete = hasPermission("*") || hasSpacePermission(currentSpace, "notes.delete");
+
+ if (!authChecked || loading) {
+ return (
+
+ );
+ }
+
+ if (error) {
+ return (
+
+
{error}
+
+
+ );
+ }
+
+ if (!note) return null;
+
+ const flatCategories = flattenCategories(categoryTree);
+
+ // --- EDITOR MODE ---
+ if (isEditing) {
+ return (
+
+ {/* Toolbar */}
+
+
+
+
+
+ {saveStatus === "saved" ? "Saved" : saveStatus === "saving" ? "Saving…" : "Unsaved"}
+
+
+
+ {/* Title */}
+
{
+ setTitle(e.target.value);
+ scheduleSave();
+ }}
+ maxLength={255}
+ />
+
+ {/* Description */}
+
+ );
+ }
+
+ // --- VIEWER MODE ---
+ const categoryLabel = flatCategories.find((c) => c.id === note.category_id)?.name;
+
+ // Password gate — show unlock form, hide all content and Edit button
+ if (note.is_password_protected && !unlocked) {
+ return (
+
+
+
+
+
+
+
+
+
{note.title}
+
This note is password protected. Enter the password to view it.
+
{ setUnlockPassword(e.target.value); setUnlockError(""); }}
+ onKeyDown={(e) => e.key === "Enter" && unlockNote()}
+ autoFocus
+ />
+ {unlockError &&
{unlockError}
}
+
+
+
+
+
+ );
+ }
+
+ return (
+
+ {/* Toolbar */}
+
+
+
+ {canEdit && (
+
+ )}
+
+
+ {/* Note header */}
+
+
{note.title}
+
+ {note.is_pinned && (
+
+
+ Pinned
+
+ )}
+ {note.is_favorite && (
+
+
+ Favourite
+
+ )}
+ {note.is_public && (
+
+
+ Public
+
+ )}
+ {!note.is_public && (
+
+
+ Private
+
+ )}
+ {note.is_password_protected && (
+
+
+ Password
+
+ )}
+
+
+ {note.description &&
{note.description}
}
+
+
+ {(note.tags ?? []).map((tag) => (
+
+ {tag}
+
+ ))}
+ {categoryLabel && (
+
+
+ {categoryLabel}
+
+ )}
+ Updated {new Date(note.updated_at).toLocaleString()}
+
+
+
+
+ {/* WYSIWYG content (read-only) */}
+
router.push(path)}
+ onFetchTasksForList={fetchTasksForList}
+ />
+
+ );
+}
diff --git a/frontend_new/src/app/dashboard/spaces/[spaceId]/notes/[noteId]/page.tsx b/frontend_new/src/app/dashboard/spaces/[spaceId]/notes/[noteId]/page.tsx
new file mode 100644
index 0000000..7b27a90
--- /dev/null
+++ b/frontend_new/src/app/dashboard/spaces/[spaceId]/notes/[noteId]/page.tsx
@@ -0,0 +1,9 @@
+import NotePageClient from "./NotePageClient";
+
+export function generateStaticParams() {
+ return [{ spaceId: "__space__", noteId: "__note__" }];
+}
+
+export default function NotePage() {
+ return ;
+}
diff --git a/frontend_new/src/app/dashboard/spaces/[spaceId]/notes/new/NewNotePageClient.tsx b/frontend_new/src/app/dashboard/spaces/[spaceId]/notes/new/NewNotePageClient.tsx
new file mode 100644
index 0000000..152bebd
--- /dev/null
+++ b/frontend_new/src/app/dashboard/spaces/[spaceId]/notes/new/NewNotePageClient.tsx
@@ -0,0 +1,346 @@
+"use client";
+import { useEffect, useRef, useState, useCallback } from "react";
+import { useRouter } from "next/navigation";
+import { useAuthStore } from "@/stores/authStore";
+import { useSpaceStore, type Note, type Category, type TaskList } from "@/stores/spaceStore";
+import apiClient from "@/lib/apiClient";
+import RichTextEditor from "@/components/RichTextEditor";
+
+/** Read real URL params from window.location — useParams() returns static
+ * placeholder values in a Next.js static export. */
+function getNoteParams(): { spaceId: string; noteId: string } {
+ if (typeof window === "undefined") return { spaceId: "", noteId: "" };
+ const m = window.location.pathname.match(/\/dashboard\/spaces\/([^/]+)\/notes\/([^/]+)/);
+ return { spaceId: m?.[1] ?? "", noteId: m?.[2] ?? "" };
+}
+
+type PasswordMode = "keep" | "set" | "remove";
+
+export default function NewNotePage() {
+ const { spaceId, noteId } = getNoteParams();
+ const router = useRouter();
+
+ const ensureInitialized = useAuthStore((s) => s.ensureInitialized);
+ const user = useAuthStore((s) => s.user);
+ const hasPermission = useAuthStore((s) => s.hasPermission);
+ const hasSpacePermission = useAuthStore((s) => s.hasSpacePermission);
+ const fetchCategories = useSpaceStore((s) => s.fetchCategories);
+ const categoryTree = useSpaceStore((s) => s.categoryTree);
+ const currentSpace = useSpaceStore((s) => s.currentSpace);
+ const selectSpace = useSpaceStore((s) => s.selectSpace);
+
+ const [authChecked, setAuthChecked] = useState(false);
+ const [note, setNote] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState("");
+ const [isEditing, setIsEditing] = useState(false);
+
+ // Editor state
+ const [title, setTitle] = useState("");
+ const [description, setDescription] = useState("");
+ const [content, setContent] = useState("");
+ const [tags, setTags] = useState("");
+ const [categoryId, setCategoryId] = useState("");
+ const [isPinned, setIsPinned] = useState(false);
+ const [isFavorite, setIsFavorite] = useState(false);
+ const [isPublic, setIsPublic] = useState(false);
+ const [passwordMode, setPasswordMode] = useState("keep");
+ const [newPassword, setNewPassword] = useState("");
+ const [saveStatus, setSaveStatus] = useState<"saved" | "saving" | "dirty">("saved");
+
+ // Task lists for @TaskList mentions
+ const [taskLists, setTaskLists] = useState([]);
+
+ const saveTimerRef = useRef | null>(null);
+
+ // Flat list of categories for dropdown — computed before any hooks that depend on it
+ function flattenCategories(cats: Category[]): Category[] {
+ const result: Category[] = [];
+ function traverse(list: Category[]) {
+ for (const c of list) {
+ result.push(c);
+ const subs = c.subcategories ?? c.children ?? [];
+ if (subs.length) traverse(subs);
+ }
+ }
+ traverse(cats);
+ return result;
+ }
+ const flatCategories = flattenCategories(categoryTree);
+
+ // Set default category once when categories first load (must be before early returns)
+ useEffect(() => {
+ if (flatCategories.length > 0 && !categoryId) {
+ setCategoryId(flatCategories[0].id);
+ }
+ }, [flatCategories.length]); // eslint-disable-line react-hooks/exhaustive-deps
+
+ useEffect(() => {
+ ensureInitialized().then(() => {
+ if (!useAuthStore.getState().user) {
+ router.replace("/login");
+ } else {
+ setAuthChecked(true);
+ // Ensure space is selected
+ if (!useSpaceStore.getState().currentSpace || useSpaceStore.getState().currentSpace?.id !== spaceId) {
+ selectSpace(spaceId);
+ } else {
+ fetchCategories(spaceId);
+ }
+ }
+ });
+ }, []);
+
+ useEffect(() => {
+ if (!authChecked) return;
+ loadTaskLists();
+ }, [authChecked]);
+
+ async function loadTaskLists() {
+ try {
+ const res = await apiClient.get(`/api/v1/spaces/${spaceId}/task-lists`);
+ setTaskLists(Array.isArray(res.data) ? res.data : []);
+
+ setLoading(false);
+ } catch {
+ // ignore
+ }
+ }
+
+ async function fetchTasksForList(taskListId: string) {
+ const [tasksRes, statusRes] = await Promise.all([
+ apiClient.get(`/api/v1/spaces/${spaceId}/tasks`, { params: { taskListId } }),
+ apiClient.get(`/api/v1/spaces/${spaceId}/task-lists/${taskListId}/statuses`),
+ ]);
+ const tasks = Array.isArray(tasksRes.data) ? tasksRes.data : [];
+ const statuses = Array.isArray(statusRes.data) ? statusRes.data : [];
+ const statusMap = new Map(statuses.map((s: { id: string; name: string; color: string }) => [s.id, s]));
+ return tasks
+ .filter((t: { parent_task_id: string | null }) => !t.parent_task_id)
+ .map((t: { id: string; title: string; status_id: string }) => {
+ const status = statusMap.get(t.status_id) as { name: string; color: string } | undefined;
+ return {
+ id: t.id,
+ title: t.title,
+ statusColor: status?.color ?? "#7c8596",
+ statusName: status?.name ?? "",
+ };
+ });
+ }
+
+ function cancelEditing() {
+ router.push(`/dashboard/spaces/${spaceId}/notes`);
+ }
+
+ if (!authChecked || loading) {
+ return (
+
+ );
+ }
+
+ if (error) {
+ return (
+
+
New Note Error: {error}
+
+
+ );
+ }
+
+ if (flatCategories.length === 0) {
+ return (
+
+
Please create a category before creating notes.
+
+ );
+ }
+
+ async function performSave() {
+ setSaveStatus("saving");
+ try {
+ const tagList = tags
+ .split(",")
+ .map((t) => t.trim())
+ .filter(Boolean);
+ const catId = categoryId || null;
+ const body: Record = {
+ title,
+ description,
+ content,
+ tags: tagList,
+ category_id: catId,
+ is_pinned: isPinned,
+ is_favorite: isFavorite,
+ is_public: isPublic,
+ };
+ if (passwordMode === "set" && newPassword) {
+ body.note_password = newPassword;
+ } else if (passwordMode === "remove") {
+ body.note_password = "";
+ }
+ const res = await apiClient.post(`/api/v1/spaces/${spaceId}/notes`, body);
+ setNote(res.data);
+ setSaveStatus("saved");
+ window.location.href = `/dashboard/spaces/${spaceId}/notes/${res.data.id}`;
+ } catch {
+ setSaveStatus("dirty");
+ }
+ }
+
+ return (
+
+ {/* Toolbar */}
+
+
+
+
+
+ {saveStatus === "saved" ? "Saved" : saveStatus === "saving" ? "Saving…" : "Unsaved"}
+
+
+
+ {/* Title */}
+
{
+ setTitle(e.target.value);
+ }}
+ maxLength={255}
+ />
+
+ {/* Description */}
+
+ );
+}
diff --git a/frontend_new/src/app/dashboard/spaces/[spaceId]/notes/new/page.tsx b/frontend_new/src/app/dashboard/spaces/[spaceId]/notes/new/page.tsx
new file mode 100644
index 0000000..3b246a6
--- /dev/null
+++ b/frontend_new/src/app/dashboard/spaces/[spaceId]/notes/new/page.tsx
@@ -0,0 +1,9 @@
+import NewNotePageClient from "./NewNotePageClient";
+
+export function generateStaticParams() {
+ return [{ spaceId: "__space__" }];
+}
+
+export default function NewNotePage() {
+ return ;
+}
diff --git a/frontend_new/src/app/dashboard/spaces/[spaceId]/tasklists/[taskListId]/TaskListPageClient.tsx b/frontend_new/src/app/dashboard/spaces/[spaceId]/tasklists/[taskListId]/TaskListPageClient.tsx
new file mode 100644
index 0000000..9cc6dbd
--- /dev/null
+++ b/frontend_new/src/app/dashboard/spaces/[spaceId]/tasklists/[taskListId]/TaskListPageClient.tsx
@@ -0,0 +1,1025 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import { useRouter } from "next/navigation";
+import { useAuthStore } from "@/stores/authStore";
+import { useSpaceStore, type Task, type TaskStatus, type TaskList, Category } from "@/stores/spaceStore";
+import apiClient from "@/lib/apiClient";
+import RichTextEditor from "@/components/RichTextEditor";
+
+/** Read real URL params from window.location — useParams() returns static
+ * placeholder values in a Next.js static export. */
+function getTaskListParams(): { spaceId: string; taskListId: string } {
+ if (typeof window === "undefined") return { spaceId: "", taskListId: "" };
+ const m = window.location.pathname.match(/\/dashboard\/spaces\/([^/]+)\/tasklists\/([^/]+)/);
+ return { spaceId: m?.[1] ?? "", taskListId: m?.[2] ?? "" };
+}
+
+export default function TaskListPage() {
+ const { spaceId, taskListId } = getTaskListParams();
+ const router = useRouter();
+
+ const ensureInitialized = useAuthStore((s) => s.ensureInitialized);
+ const hasPermission = useAuthStore((s) => s.hasPermission);
+ const hasSpacePermission = useAuthStore((s) => s.hasSpacePermission);
+ const currentSpace = useSpaceStore((s) => s.currentSpace);
+ const selectSpace = useSpaceStore((s) => s.selectSpace);
+ const categories = useSpaceStore((s) => s.categoryTree);
+
+ const [authChecked, setAuthChecked] = useState(false);
+ const [taskList, setTaskList] = useState(null);
+ const [tasks, setTasks] = useState([]);
+ const [statuses, setStatuses] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState("");
+
+ // Filters
+ const [filterStatus, setFilterStatus] = useState("");
+ const [filterParent, setFilterParent] = useState("");
+
+ // Task detail modal
+ const [selectedTask, setSelectedTask] = useState(null);
+ const [showTaskModal, setShowTaskModal] = useState(false);
+ const [isNewTask, setIsNewTask] = useState(false);
+
+ // Edit task list modal
+ const [showEditModal, setShowEditModal] = useState(false);
+
+ useEffect(() => {
+ ensureInitialized().then(() => {
+ if (!useAuthStore.getState().user) {
+ router.replace("/login");
+ } else {
+ setAuthChecked(true);
+ if (!useSpaceStore.getState().currentSpace || useSpaceStore.getState().currentSpace?.id !== spaceId) {
+ selectSpace(spaceId);
+ }
+ }
+ });
+ }, []);
+
+ useEffect(() => {
+ if (!authChecked) return;
+ loadAll();
+ }, [authChecked]);
+
+ async function loadAll() {
+ setLoading(true);
+ setError("");
+ try {
+ const [tlRes, tasksRes, statusRes] = await Promise.all([
+ apiClient.get(`/api/v1/spaces/${spaceId}/task-lists`),
+ apiClient.get(`/api/v1/spaces/${spaceId}/tasks`, { params: { taskListId } }),
+ apiClient.get(`/api/v1/spaces/${spaceId}/task-lists/${taskListId}/statuses`),
+ ]);
+ const lists: TaskList[] = Array.isArray(tlRes.data) ? tlRes.data : [];
+ const found = lists.find((l) => l.id === taskListId) || null;
+ setTaskList(found);
+ setTasks(Array.isArray(tasksRes.data) ? tasksRes.data : []);
+ setStatuses(Array.isArray(statusRes.data) ? statusRes.data : []);
+ } catch {
+ setError("Failed to load task list.");
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ const canCreateTasks = hasPermission("*") || hasSpacePermission(currentSpace, "tasks.create");
+ const canEditTasks = hasPermission("*") || hasSpacePermission(currentSpace, "tasks.edit");
+ const canDeleteTasks = hasPermission("*") || hasSpacePermission(currentSpace, "tasks.delete");
+ const canEditTaskList = hasPermission("*") || hasSpacePermission(currentSpace, "task_lists.edit");
+
+ // Filter tasks
+ const filteredTasks = tasks.filter((t) => {
+ if (filterStatus && t.status_id !== filterStatus) return false;
+ if (filterParent && t.parent_task_id !== filterParent) return false;
+ return true;
+ });
+
+ // Group tasks by status
+ const tasksByStatus: Record = {};
+ for (const s of statuses) tasksByStatus[s.id] = [];
+ for (const t of filteredTasks) {
+ if (!tasksByStatus[t.status_id]) tasksByStatus[t.status_id] = [];
+ tasksByStatus[t.status_id].push(t);
+ }
+
+ // Top-level tasks (no parent) for parent filter dropdown
+ const topLevelTasks = tasks.filter((t) => !t.parent_task_id);
+
+ async function updateTaskStatus(taskId: string, statusId: string) {
+ try {
+ const res = await apiClient.put(`/api/v1/spaces/${spaceId}/tasks/${taskId}`, { status_id: statusId });
+ setTasks((prev) => prev.map((t) => (t.id === taskId ? { ...t, ...res.data } : t)));
+ } catch {
+ alert("Failed to update task status.");
+ }
+ }
+
+ async function transitionTask(taskId: string, direction: "forward" | "backward") {
+ try {
+ await apiClient.post(`/api/v1/spaces/${spaceId}/tasks/${taskId}/transition`, { direction });
+ await reloadTasks();
+ } catch {
+ alert("Failed to transition task.");
+ }
+ }
+
+ async function reloadTasks() {
+ try {
+ const res = await apiClient.get(`/api/v1/spaces/${spaceId}/tasks`, { params: { taskListId } });
+ setTasks(Array.isArray(res.data) ? res.data : []);
+ } catch {
+ // ignore
+ }
+ }
+
+ function openCreateTask() {
+ setSelectedTask({
+ id: "",
+ title: "",
+ description: "",
+ task_list_id: taskListId,
+ status_id: statuses[0]?.id ?? "",
+ parent_task_id: null,
+ depth: 0,
+ note_links: [],
+ space_id: spaceId,
+ created_by: "",
+ updated_by: "",
+ created_at: "",
+ updated_at: "",
+ });
+ setIsNewTask(true);
+ setShowTaskModal(true);
+ }
+
+ function openEditTask(task: Task) {
+ setSelectedTask({ ...task });
+ setIsNewTask(false);
+ setShowTaskModal(true);
+ }
+
+ async function saveTask(task: Task) {
+ try {
+ if (isNewTask) {
+ await apiClient.post(`/api/v1/spaces/${spaceId}/tasks`, {
+ title: task.title,
+ description: task.description,
+ task_list_id: taskListId,
+ status_id: task.status_id || statuses[0]?.id,
+ parent_task_id: task.parent_task_id || undefined,
+ });
+ } else {
+ await apiClient.put(`/api/v1/spaces/${spaceId}/tasks/${task.id}`, {
+ title: task.title,
+ description: task.description,
+ status_id: task.status_id,
+ parent_task_id: task.parent_task_id || null,
+ });
+ }
+ setShowTaskModal(false);
+ await reloadTasks();
+ } catch {
+ alert("Failed to save task.");
+ }
+ }
+
+ async function deleteTask(task: Task) {
+ if (!confirm(`Delete task "${task.title}"?`)) return;
+ try {
+ await apiClient.delete(`/api/v1/spaces/${spaceId}/tasks/${task.id}`);
+ setShowTaskModal(false);
+ await reloadTasks();
+ } catch {
+ alert("Failed to delete task.");
+ }
+ }
+
+ async function reloadStatuses() {
+ try {
+ const res = await apiClient.get(`/api/v1/spaces/${spaceId}/task-lists/${taskListId}/statuses`);
+ setStatuses(Array.isArray(res.data) ? res.data : []);
+ } catch {
+ // ignore
+ }
+ }
+
+ if (!authChecked || loading) {
+ return (
+
+ );
+ }
+
+ if (error) {
+ return (
+
+
{error}
+
+
+ );
+ }
+
+ function flattenCategories(cats: Category[]): Category[] {
+ const result: Category[] = [];
+ function traverse(list: Category[]) {
+ for (const c of list) {
+ result.push(c);
+ const subs = c.subcategories ?? c.children ?? [];
+ if (subs.length) traverse(subs);
+ }
+ }
+ traverse(cats);
+ return result;
+ }
+
+ const flatCategories = flattenCategories(categories);
+
+ if (flatCategories.length == 0) {
+ return (
+
+
Please create a category before creating notes.
+
+ );
+ }
+
+ return (
+
+ {/* Header */}
+
+
+
{taskList?.name ?? "Task List"}
+ {canEditTaskList && (
+
+ )}
+ {canCreateTasks && (
+
+ )}
+
+
+ {taskList?.description &&
{taskList.description}
}
+
+ {/* Filters */}
+
+
+
+
+
+ {/* Task board — grouped by status */}
+ {statuses.length === 0 ? (
+
No statuses defined for this task list.
+ ) : (
+
+ {statuses.map((status) => {
+ const statusTasks = (tasksByStatus[status.id] ?? []).filter((t) => !t.parent_task_id);
+ return (
+
+
+
+ {status.name}
+ {(tasksByStatus[status.id] ?? []).length}
+
+ {statusTasks.length === 0 ? (
+
No tasks
+ ) : (
+
+ )}
+
+ );
+ })}
+
+ )}
+
+ {/* Task detail/create modal */}
+ {showTaskModal && selectedTask && (
+
setShowTaskModal(false)}
+ onSave={saveTask}
+ onDelete={deleteTask}
+ onTransition={transitionTask}
+ />
+ )}
+
+ {/* Edit task list modal */}
+ {showEditModal && taskList && (
+ setShowEditModal(false)}
+ onSaved={(updated: Partial) => {
+ setTaskList((prev) => (prev ? { ...prev, ...updated } : prev));
+ setShowEditModal(false);
+ }}
+ onDeleted={() => router.push("/dashboard")}
+ onStatusesChanged={reloadStatuses}
+ />
+ )}
+
+ );
+}
+
+// Recursive task tree component
+function TaskTree({
+ tasks,
+ allTasks,
+ statuses,
+ depth,
+ onEdit,
+ onStatusChange,
+ canEdit,
+}: {
+ tasks: Task[];
+ allTasks: Task[];
+ statuses: TaskStatus[];
+ depth: number;
+ onEdit: (t: Task) => void;
+ onStatusChange: (taskId: string, statusId: string) => void;
+ canEdit: boolean;
+}) {
+ return (
+ 0 ? `${depth * 20}px` : 0 }}>
+ {tasks.map((task) => {
+ const subtasks = allTasks.filter((t) => t.parent_task_id === task.id);
+ return (
+
+ );
+ })}
+
+ );
+}
+
+function TaskRow({
+ task,
+ subtasks,
+ allTasks,
+ statuses,
+ depth,
+ onEdit,
+ onStatusChange,
+ canEdit,
+}: {
+ task: Task;
+ subtasks: Task[];
+ allTasks: Task[];
+ statuses: TaskStatus[];
+ depth: number;
+ onEdit: (t: Task) => void;
+ onStatusChange: (taskId: string, statusId: string) => void;
+ canEdit: boolean;
+}) {
+ const [expanded, setExpanded] = useState(true);
+ const [showStatusPicker, setShowStatusPicker] = useState(false);
+ const currentStatus = statuses.find((s) => s.id === task.status_id);
+
+ const taskDescription = task.description.replace(/<[^>]+>/g, "");
+
+ return (
+
+
+ {/* Expand toggle */}
+
+
+ {/* Title + description */}
+
canEdit && onEdit(task)}>
+ {task.title}
+ {taskDescription != "" && (
+
+ {taskDescription.substring(0, 80)}
+ {taskDescription.length > 80 ? "…" : ""}
+
+ )}
+ {taskDescription == "" && No description}
+
+ {/* Status dot button */}
+
+
+ {showStatusPicker && (
+
+ {statuses.map((s) => (
+
+ ))}
+
+ )}
+
+
+
+ {expanded && subtasks.length > 0 && (
+
+ )}
+
+ );
+}
+
+// Task create/edit modal
+function TaskModal({
+ task,
+ isNew,
+ statuses,
+ allTasks,
+ canDelete,
+ onClose,
+ onSave,
+ onDelete,
+ onTransition,
+}: {
+ task: Task;
+ isNew: boolean;
+ statuses: TaskStatus[];
+ allTasks: Task[];
+ canDelete: boolean;
+ onClose: () => void;
+ onSave: (t: Task) => void;
+ onDelete: (t: Task) => void;
+ onTransition: (taskId: string, direction: "forward" | "backward") => void;
+}) {
+ const [local, setLocal] = useState({ ...task });
+ const parentOptions = allTasks.filter((t) => t.id !== task.id && !t.parent_task_id);
+ const subtasks = allTasks.filter((t) => t.parent_task_id === task.id);
+ const currentStatusIdx = statuses.findIndex((s) => s.id === local.status_id);
+
+ return (
+ e.target === e.currentTarget && onClose()}>
+
+
+
+
{isNew ? "Create Task" : "Task Detail"}
+
+
+
+
+
+
+ setLocal((p) => ({ ...p, title: e.target.value }))} />
+
+
+ setLocal((p) => ({ ...p, description: html }))}
+ placeholder="Task description…"
+ minHeight={180}
+ />
+
+
+
+
+
+
+
+
+
+ {/* Status progress */}
+
+ {statuses.map((s, idx) => {
+ const reached = currentStatusIdx >= idx;
+ return (
+
+
+ {s.name}
+
+ );
+ })}
+
+
+ {!isNew && (
+
+
+
+
+ )}
+
+ {!isNew && subtasks.length > 0 && (
+
+
Subtasks
+ {subtasks.map((st) => {
+ const stStatus = statuses.find((s) => s.id === st.status_id);
+ return (
+
+
+ {st.title}
+ L{(st.depth ?? 0) + 1}
+
+ );
+ })}
+
+ )}
+
+
+
+
+
+ {canDelete && (
+
+ )}
+
+
+
+
+
+
+ );
+}
+
+// Status form used in create/edit sub-modal
+interface StatusForm {
+ name: string;
+ color: string;
+}
+
+function EditTaskListModal({
+ taskList,
+ statuses: initialStatuses,
+ spaceId,
+ categoryOptions,
+ canDeleteTaskList,
+ onClose,
+ onSaved,
+ onDeleted,
+ onStatusesChanged,
+}: {
+ taskList: TaskList;
+ statuses: TaskStatus[];
+ spaceId: string;
+ categoryOptions: Category[];
+ canDeleteTaskList: boolean;
+ onClose: () => void;
+ onSaved: (updated: Partial) => void;
+ onDeleted: () => void;
+ onStatusesChanged: () => void;
+}) {
+ const taskListId = taskList.id;
+
+ const [name, setName] = useState(taskList.name || "");
+ const [desc, setDesc] = useState(taskList.description || "");
+ const [categoryId, setCategoryId] = useState(taskList.category_id || "");
+ const [savingDetails, setSavingDetails] = useState(false);
+
+ // Local copy of statuses for optimistic drag-and-drop
+ const [statuses, setStatuses] = useState(initialStatuses);
+
+ // Drag state
+ const [draggedId, setDraggedId] = useState("");
+ const [dragOverId, setDragOverId] = useState("");
+
+ // Status sub-modal
+ const [showStatusModal, setShowStatusModal] = useState(false);
+ const [statusMode, setStatusMode] = useState<"create" | "edit">("create");
+ const [editingStatusId, setEditingStatusId] = useState("");
+ const [statusForm, setStatusForm] = useState({ name: "", color: "#7c8596" });
+ const [savingStatus, setSavingStatus] = useState(false);
+
+ // Confirm delete task list
+ const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
+ const [deleting, setDeleting] = useState(false);
+
+ async function saveDetails() {
+ const trimmed = name.trim();
+ if (!trimmed) return;
+ setSavingDetails(true);
+ try {
+ await apiClient.put(`/api/v1/spaces/${spaceId}/task-lists/${taskListId}`, {
+ name: trimmed,
+ description: desc,
+ category_id: categoryId || null,
+ });
+ onSaved({ name: trimmed, description: desc, category_id: categoryId || null });
+ } catch {
+ alert("Failed to update task list.");
+ } finally {
+ setSavingDetails(false);
+ }
+ }
+
+ // Drag-and-drop handlers
+ function onDragStart(id: string) {
+ setDraggedId(id);
+ }
+ function onDragOver(id: string) {
+ setDragOverId(id);
+ }
+ function onDragLeave(id: string) {
+ if (dragOverId === id) setDragOverId("");
+ }
+ async function onDrop(targetId: string) {
+ if (!draggedId || draggedId === targetId) {
+ onDragEnd();
+ return;
+ }
+ const ordered = statuses.map((s) => s.id);
+ const fromIdx = ordered.indexOf(draggedId);
+ const targetIdx = ordered.indexOf(targetId);
+ if (fromIdx < 0 || targetIdx < 0) { onDragEnd(); return; }
+ ordered.splice(fromIdx, 1);
+ const insertIdx = ordered.indexOf(targetId);
+ ordered.splice(insertIdx, 0, draggedId);
+ // Optimistic update
+ const reordered = ordered.map((id) => statuses.find((s) => s.id === id)!);
+ setStatuses(reordered);
+ onDragEnd();
+ try {
+ const res = await apiClient.put(
+ `/api/v1/spaces/${spaceId}/task-lists/${taskListId}/statuses/reorder`,
+ { ordered_status_ids: ordered }
+ );
+ setStatuses(Array.isArray(res.data) ? res.data : reordered);
+ onStatusesChanged();
+ } catch {
+ // Revert on failure
+ setStatuses(initialStatuses);
+ alert("Failed to reorder statuses.");
+ }
+ }
+ function onDragEnd() {
+ setDraggedId("");
+ setDragOverId("");
+ }
+
+ // Status sub-modal helpers
+ function openCreateStatus() {
+ setStatusMode("create");
+ setEditingStatusId("");
+ setStatusForm({ name: "", color: "#7c8596" });
+ setShowStatusModal(true);
+ }
+ function openEditStatus(s: TaskStatus) {
+ setStatusMode("edit");
+ setEditingStatusId(s.id);
+ setStatusForm({ name: s.name, color: s.color || "#7c8596" });
+ setShowStatusModal(true);
+ }
+ function closeStatusModal() {
+ setShowStatusModal(false);
+ setEditingStatusId("");
+ setStatusForm({ name: "", color: "#7c8596" });
+ }
+
+ async function submitStatusForm() {
+ const trimmed = statusForm.name.trim();
+ if (!trimmed) return;
+ setSavingStatus(true);
+ try {
+ if (statusMode === "create") {
+ const res = await apiClient.post(
+ `/api/v1/spaces/${spaceId}/task-lists/${taskListId}/statuses`,
+ { name: trimmed, color: statusForm.color }
+ );
+ setStatuses((prev) => [...prev, res.data]);
+ } else {
+ const res = await apiClient.put(
+ `/api/v1/spaces/${spaceId}/task-lists/${taskListId}/statuses/${editingStatusId}`,
+ { name: trimmed, color: statusForm.color }
+ );
+ setStatuses((prev) => prev.map((s) => (s.id === editingStatusId ? res.data : s)));
+ }
+ onStatusesChanged();
+ closeStatusModal();
+ } catch {
+ alert(statusMode === "create" ? "Failed to create status." : "Failed to update status.");
+ } finally {
+ setSavingStatus(false);
+ }
+ }
+
+ async function deleteStatus() {
+ if (!editingStatusId) return;
+ setSavingStatus(true);
+ try {
+ await apiClient.delete(
+ `/api/v1/spaces/${spaceId}/task-lists/${taskListId}/statuses/${editingStatusId}`
+ );
+ setStatuses((prev) => prev.filter((s) => s.id !== editingStatusId));
+ onStatusesChanged();
+ closeStatusModal();
+ } catch {
+ alert("Failed to delete status.");
+ } finally {
+ setSavingStatus(false);
+ }
+ }
+
+ async function confirmDeleteTaskList() {
+ setDeleting(true);
+ try {
+ await apiClient.delete(`/api/v1/spaces/${spaceId}/task-lists/${taskListId}`);
+ onDeleted();
+ } catch {
+ alert("Failed to delete task list.");
+ setDeleting(false);
+ setShowDeleteConfirm(false);
+ }
+ }
+
+ return (
+ <>
+ e.target === e.currentTarget && !showStatusModal && onClose()}
+ >
+
+
+
+
Edit Task List
+
+
+
+ {/* Details */}
+
+
+ setName(e.target.value)} maxLength={120} />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Status progression */}
+
+
+ Status Progression
+
+
+
+ {statuses.map((s) => (
+
onDragStart(s.id)}
+ onDragOver={(e) => { e.preventDefault(); onDragOver(s.id); }}
+ onDragLeave={() => onDragLeave(s.id)}
+ onDrop={(e) => { e.preventDefault(); onDrop(s.id); }}
+ onDragEnd={onDragEnd}
+ style={{
+ display: "flex",
+ alignItems: "center",
+ gap: "0.5rem",
+ padding: "0.35rem 0.5rem",
+ borderRadius: "0.375rem",
+ border: `1px solid ${dragOverId === s.id ? "var(--bs-primary)" : "var(--bs-border-color)"}`,
+ background: dragOverId === s.id ? "rgba(var(--bs-primary-rgb), 0.08)" : "var(--bs-body-bg)",
+ cursor: "grab",
+ opacity: draggedId === s.id ? 0.4 : 1,
+ }}
+ >
+
+
+ {s.name}
+
+
+ ))}
+ {statuses.length === 0 &&
No statuses yet. Add one above.
}
+
+
+
+ {canDeleteTaskList && (
+ <>
+
+
+
Danger Zone
+
Delete this task list and all its tasks permanently. This cannot be undone.
+
+
+ >
+ )}
+
+
+
+
+
+
+
+
+
+ {/* Status create/edit sub-modal */}
+ {showStatusModal && (
+ e.target === e.currentTarget && closeStatusModal()}
+ >
+
+
+
+
{statusMode === "create" ? "Create Status" : "Edit Status"}
+
+
+
+
+
+ setStatusForm((p) => ({ ...p, name: e.target.value }))}
+ maxLength={100}
+ placeholder="e.g. Blocked"
+ autoFocus
+ />
+
+
+
+ {statusMode === "edit" && (
+
+
Danger Zone
+
Deleting this status is permanent and cannot be undone.
+
+
+ )}
+
+
+
+
+
+
+
+
+
+ )}
+
+ {/* Delete task list confirm */}
+ {showDeleteConfirm && (
+
+
+
+
+
Delete Task List
+
+
+
Delete task list {taskList.name}? All tasks will be permanently removed.
+
+
+
+
+
+
+
+
+
+ )}
+ >
+ );
+}
diff --git a/frontend_new/src/app/dashboard/spaces/[spaceId]/tasklists/[taskListId]/page.tsx b/frontend_new/src/app/dashboard/spaces/[spaceId]/tasklists/[taskListId]/page.tsx
new file mode 100644
index 0000000..03c4d40
--- /dev/null
+++ b/frontend_new/src/app/dashboard/spaces/[spaceId]/tasklists/[taskListId]/page.tsx
@@ -0,0 +1,9 @@
+import TaskListPageClient from "./TaskListPageClient";
+
+export function generateStaticParams() {
+ return [{ spaceId: "__space__", taskListId: "__tasklist__" }];
+}
+
+export default function TaskListPage() {
+ return ;
+}
diff --git a/frontend_new/src/app/dashboard/spaces/[spaceId]/tasklists/new/NewTaskListPageClient.tsx b/frontend_new/src/app/dashboard/spaces/[spaceId]/tasklists/new/NewTaskListPageClient.tsx
new file mode 100644
index 0000000..0169ce4
--- /dev/null
+++ b/frontend_new/src/app/dashboard/spaces/[spaceId]/tasklists/new/NewTaskListPageClient.tsx
@@ -0,0 +1,132 @@
+"use client";
+import apiClient from "@/lib/apiClient";
+import { useAuthStore } from "@/stores/authStore";
+import { Category, useSpaceStore } from "@/stores/spaceStore";
+import { useRouter } from "next/navigation";
+import { useEffect, useState } from "react";
+
+function getTaskListParams(): { spaceId: string; taskListId: string } {
+ if (typeof window === "undefined") return { spaceId: "", taskListId: "" };
+ const m = window.location.pathname.match(/\/dashboard\/spaces\/([^/]+)\/tasklists\/([^/]+)/);
+ return { spaceId: m?.[1] ?? "", taskListId: m?.[2] ?? "" };
+}
+
+export default function NewTaskListPageClient() {
+ const router = useRouter();
+ const ensureInitialized = useAuthStore((s) => s.ensureInitialized);
+ const [authChecked, setAuthChecked] = useState(false);
+
+ const [tasklistName, setTasklistName] = useState("");
+ const [tasklistDescription, setTasklistDescription] = useState("");
+ const [tasklistCategory, setTasklistCategory] = useState("");
+
+ const { spaceId } = getTaskListParams();
+
+ const selectSpace = useSpaceStore((s) => s.selectSpace);
+ const categories = useSpaceStore((s) => s.categoryTree);
+
+ function flattenCategories(cats: Category[]): Category[] {
+ const result: Category[] = [];
+ function traverse(list: Category[]) {
+ for (const c of list) {
+ result.push(c);
+ const subs = c.subcategories ?? c.children ?? [];
+ if (subs.length) traverse(subs);
+ }
+ }
+ traverse(cats);
+ return result;
+ }
+
+ async function HandleSubmit(e: React.SubmitEvent) {
+ e.preventDefault();
+ const { spaceId } = getTaskListParams();
+ try {
+ const res = await apiClient.post(`/api/v1/spaces/${spaceId}/task-lists`, {
+ name: tasklistName,
+ description: tasklistDescription,
+ category_id: tasklistCategory,
+ });
+ router.push(`/dashboard/spaces/${spaceId}/tasklists/${res.data.id}`);
+ } catch (error) {
+ console.error("Error creating task list:", error);
+ }
+ }
+
+ useEffect(() => {
+ ensureInitialized().then(() => {
+ if (!useAuthStore.getState().user) {
+ router.replace("/login");
+ } else {
+ setAuthChecked(true);
+ if (!useSpaceStore.getState().currentSpace || useSpaceStore.getState().currentSpace?.id !== spaceId) {
+ selectSpace(spaceId);
+ }
+ }
+ });
+ }, []);
+
+ useEffect(() => {
+ if (!authChecked) return;
+ setTasklistCategory(categories[0]?.id ?? "");
+ }, [authChecked, categories]);
+
+ if (!authChecked) {
+ return (
+
+ );
+ }
+
+ const flatCategories = flattenCategories(categories);
+
+ if (flatCategories.length == 0) {
+ return (
+
+
Please create a category before creating notes.
+
+ );
+ }
+
+ return (
+
+
Create New Task List
+
+
+ );
+}
diff --git a/frontend_new/src/app/dashboard/spaces/[spaceId]/tasklists/new/page.tsx b/frontend_new/src/app/dashboard/spaces/[spaceId]/tasklists/new/page.tsx
new file mode 100644
index 0000000..b7ca956
--- /dev/null
+++ b/frontend_new/src/app/dashboard/spaces/[spaceId]/tasklists/new/page.tsx
@@ -0,0 +1,9 @@
+import NewTaskListPageClient from "./NewTaskListPageClient";
+
+export function generateStaticParams() {
+ return [{ spaceId: "__space__" }];
+}
+
+export default function TaskListPage() {
+ return ;
+}
diff --git a/frontend_new/src/app/layout.tsx b/frontend_new/src/app/layout.tsx
new file mode 100644
index 0000000..1088d20
--- /dev/null
+++ b/frontend_new/src/app/layout.tsx
@@ -0,0 +1,18 @@
+import type { Metadata } from "next";
+import "bootstrap/dist/css/bootstrap.min.css";
+import "@mdi/font/css/materialdesignicons.min.css";
+import "highlight.js/styles/github-dark.min.css";
+import "../styles/globals.css";
+
+export const metadata: Metadata = {
+ title: "Notely",
+ description: "Note taking application",
+};
+
+export default function RootLayout({ children }: { children: React.ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/frontend_new/src/app/login/page.tsx b/frontend_new/src/app/login/page.tsx
new file mode 100644
index 0000000..559a0db
--- /dev/null
+++ b/frontend_new/src/app/login/page.tsx
@@ -0,0 +1,166 @@
+"use client";
+
+import { useEffect, useRef, useState } from "react";
+import Link from "next/link";
+import { useRouter } from "next/navigation";
+import { useAuthStore } from "@/stores/authStore";
+import { useSettingsStore } from "@/stores/settingsStore";
+import apiClient from "@/lib/apiClient";
+
+export default function LoginPage() {
+ const router = useRouter();
+ const login = useAuthStore((s) => s.login);
+ const ensureInitialized = useAuthStore((s) => s.ensureInitialized);
+ const loadFeatureFlags = useSettingsStore((s) => s.loadFeatureFlags);
+
+ const [email, setEmail] = useState("");
+ const [password, setPassword] = useState("");
+ const [error, setError] = useState("");
+ const [loading, setLoading] = useState(false);
+ const [providers, setProviders] = useState>([]);
+ const [registrationEnabled, setRegistrationEnabled] = useState(true);
+ const [providerLoginEnabled, setProviderLoginEnabled] = useState(true);
+
+ const handled = useRef(false);
+
+ useEffect(() => {
+ // apply saved theme
+ const theme = localStorage.getItem("theme") === "dark" ? "dark" : "light";
+ document.documentElement.setAttribute("data-bs-theme", theme);
+
+ const init = async () => {
+ const flags = await loadFeatureFlags();
+ setRegistrationEnabled(!!flags.registration_enabled);
+ setProviderLoginEnabled(!!flags.provider_login_enabled);
+
+ await ensureInitialized();
+ if (useAuthStore.getState().user) {
+ router.replace("/dashboard");
+ return;
+ }
+
+ // Handle OAuth callback
+ if (!handled.current) {
+ handled.current = true;
+ const params = new URLSearchParams(window.location.search);
+ const status = params.get("status");
+
+ if (status === "oauth_error") {
+ setError(params.get("message") || "Provider sign-in failed.");
+ return;
+ }
+
+ if (status === "oauth_success") {
+ await ensureInitialized();
+ if (useAuthStore.getState().user) {
+ router.replace("/dashboard");
+ } else {
+ setError("Provider sign-in returned an incomplete session.");
+ }
+ return;
+ }
+ }
+
+ // Load OAuth providers
+ if (flags.provider_login_enabled) {
+ try {
+ const res = await apiClient.get("/api/v1/auth/providers");
+ setProviders(res.data?.providers || []);
+ } catch {
+ setProviders([]);
+ }
+ }
+
+ // Show query message
+ const msg = new URLSearchParams(window.location.search).get("message");
+ if (msg) setError(msg);
+ };
+
+ init();
+ }, []);
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ setError("");
+ setLoading(true);
+ try {
+ await login(email, password);
+ router.replace("/dashboard");
+ } catch (err: unknown) {
+ setError(String(err));
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const startProviderLogin = (providerId: string) => {
+ window.location.href = `${window.location.origin}/api/v1/auth/providers/${providerId}/start`;
+ };
+
+ return (
+
+
+
+
+
+
Login
+
+
+
+ {providerLoginEnabled && providers.length > 0 && (
+
+
+ or continue with
+
+
+ {providers.map((provider) => (
+
+ ))}
+
+
+ )}
+
+ {registrationEnabled && (
+
+ Don't have an account? Register here
+
+ )}
+
+
+
+ );
+}
diff --git a/frontend_new/src/app/page.tsx b/frontend_new/src/app/page.tsx
new file mode 100644
index 0000000..fce8983
--- /dev/null
+++ b/frontend_new/src/app/page.tsx
@@ -0,0 +1,30 @@
+"use client";
+
+import { useEffect } from "react";
+import { useRouter } from "next/navigation";
+import { useAuthStore } from "@/stores/authStore";
+
+export default function RootPage() {
+ const router = useRouter();
+ const initialized = useAuthStore((s) => s.initialized);
+ const user = useAuthStore((s) => s.user);
+ const ensureInitialized = useAuthStore((s) => s.ensureInitialized);
+
+ useEffect(() => {
+ ensureInitialized().then(() => {
+ if (useAuthStore.getState().user) {
+ router.replace("/dashboard");
+ } else {
+ router.replace("/login");
+ }
+ });
+ }, []);
+
+ return (
+
+ );
+}
diff --git a/frontend_new/src/app/register/page.tsx b/frontend_new/src/app/register/page.tsx
new file mode 100644
index 0000000..4fcf948
--- /dev/null
+++ b/frontend_new/src/app/register/page.tsx
@@ -0,0 +1,167 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import Link from "next/link";
+import { useRouter } from "next/navigation";
+import { useAuthStore } from "@/stores/authStore";
+import { useSettingsStore } from "@/stores/settingsStore";
+
+export default function RegisterPage() {
+ const router = useRouter();
+ const register = useAuthStore((s) => s.register);
+ const ensureInitialized = useAuthStore((s) => s.ensureInitialized);
+ const loadFeatureFlags = useSettingsStore((s) => s.loadFeatureFlags);
+
+ const [form, setForm] = useState({
+ email: "",
+ username: "",
+ password: "",
+ confirmPassword: "",
+ firstName: "",
+ lastName: "",
+ });
+ const [error, setError] = useState("");
+ const [loading, setLoading] = useState(false);
+ const [registrationEnabled, setRegistrationEnabled] = useState(true);
+
+ useEffect(() => {
+ const theme = localStorage.getItem("theme") === "dark" ? "dark" : "light";
+ document.documentElement.setAttribute("data-bs-theme", theme);
+
+ const init = async () => {
+ const flags = await loadFeatureFlags();
+ setRegistrationEnabled(!!flags.registration_enabled);
+
+ if (!flags.registration_enabled) return;
+
+ await ensureInitialized();
+ if (useAuthStore.getState().user) {
+ router.replace("/dashboard");
+ }
+ };
+ init();
+ }, []);
+
+ const update = (field: string) => (e: React.ChangeEvent) => setForm((prev) => ({ ...prev, [field]: e.target.value }));
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ setError("");
+
+ if (!registrationEnabled) {
+ setError("Registration is currently disabled.");
+ return;
+ }
+
+ if (form.password !== form.confirmPassword) {
+ setError("Passwords do not match.");
+ return;
+ }
+
+ setLoading(true);
+ try {
+ await register(form.email, form.username, form.password, form.firstName, form.lastName);
+ router.replace("/dashboard");
+ } catch (err: unknown) {
+ setError(String(err));
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ return (
+
+
+
+
+
+
Register
+
+ {!registrationEnabled && (
+
+ Registration is currently disabled by an administrator.{" "}
+
+ Go to login
+
+
+ )}
+
+
+
+
+ Already have an account? Login here
+
+
+
+
+ );
+}
diff --git a/frontend_new/src/components/AdminGroupModal.tsx b/frontend_new/src/components/AdminGroupModal.tsx
new file mode 100644
index 0000000..e1a8f2f
--- /dev/null
+++ b/frontend_new/src/components/AdminGroupModal.tsx
@@ -0,0 +1,106 @@
+"use client";
+
+import { useEffect, useState } from "react";
+
+interface AdminGroup {
+ id: string;
+ name: string;
+ description: string;
+ is_system: boolean;
+ permissions: string[];
+}
+
+interface Props {
+ mode: "create" | "edit";
+ group: AdminGroup | null;
+ submitting: boolean;
+ onClose: () => void;
+ onSubmit: (data: { name: string; description: string; permissions: string[] }) => void;
+}
+
+export default function AdminGroupModal({ mode, group, submitting, onClose, onSubmit }: Props) {
+ const [name, setName] = useState("");
+ const [description, setDescription] = useState("");
+ const [permissionsText, setPermissionsText] = useState("");
+
+ const isSystem = mode === "edit" && !!group?.is_system;
+
+ useEffect(() => {
+ if (mode === "edit" && group) {
+ setName(group.name || "");
+ setDescription(group.description || "");
+ setPermissionsText((group.permissions || []).join("\n"));
+ } else {
+ setName("");
+ setDescription("");
+ setPermissionsText("");
+ }
+ }, [mode, group]);
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ const permissions = permissionsText
+ .split(/\r?\n/)
+ .map((p) => p.trim())
+ .filter(Boolean);
+ onSubmit({ name, description, permissions });
+ };
+
+ return (
+ <>
+ {
+ if (e.target === e.currentTarget) onClose();
+ }}
+ >
+
+
+
+
{mode === "create" ? "Create Group" : "Edit Group"}
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/frontend_new/src/components/AdminProviderModal.tsx b/frontend_new/src/components/AdminProviderModal.tsx
new file mode 100644
index 0000000..d2a476b
--- /dev/null
+++ b/frontend_new/src/components/AdminProviderModal.tsx
@@ -0,0 +1,212 @@
+"use client";
+
+import { useEffect, useState } from "react";
+
+interface AuthProvider {
+ id: string;
+ name: string;
+ type?: string;
+ client_id?: string;
+ authorization_url?: string;
+ token_url?: string;
+ userinfo_url?: string;
+ id_token_claim?: string;
+ scopes?: string[];
+ is_active: boolean;
+}
+
+interface ProviderForm {
+ name: string;
+ type: string;
+ client_id: string;
+ client_secret: string;
+ authorization_url: string;
+ token_url: string;
+ userinfo_url: string;
+ id_token_claim: string;
+ scopes: string;
+ is_active: boolean;
+}
+
+const defaultForm = (): ProviderForm => ({
+ name: "",
+ type: "oidc",
+ client_id: "",
+ client_secret: "",
+ authorization_url: "",
+ token_url: "",
+ userinfo_url: "",
+ id_token_claim: "id_token",
+ scopes: "openid, profile, email",
+ is_active: true,
+});
+
+interface Props {
+ mode: "create" | "edit";
+ provider: AuthProvider | null;
+ submitting: boolean;
+ onClose: () => void;
+ onSubmit: (data: Omit & { scopes: string[] }) => void;
+ onDelete: (provider: AuthProvider) => void;
+}
+
+export default function AdminProviderModal({ mode, provider, submitting, onClose, onSubmit, onDelete }: Props) {
+ const [form, setForm] = useState(defaultForm());
+
+ useEffect(() => {
+ if (mode === "edit" && provider) {
+ setForm({
+ name: provider.name || "",
+ type: provider.type || "oidc",
+ client_id: provider.client_id || "",
+ client_secret: "",
+ authorization_url: provider.authorization_url || "",
+ token_url: provider.token_url || "",
+ userinfo_url: provider.userinfo_url || "",
+ id_token_claim: provider.id_token_claim || "id_token",
+ scopes: (provider.scopes || []).join(", "),
+ is_active: provider.is_active ?? true,
+ });
+ } else {
+ setForm(defaultForm());
+ }
+ }, [mode, provider]);
+
+ const set = (field: keyof ProviderForm) => (e: React.ChangeEvent) => setForm((f) => ({ ...f, [field]: e.target.value }));
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ onSubmit({
+ ...form,
+ scopes: form.scopes
+ .split(",")
+ .map((s) => s.trim())
+ .filter(Boolean),
+ });
+ };
+
+ return (
+ <>
+ {
+ if (e.target === e.currentTarget) onClose();
+ }}
+ >
+
+
+
+
{mode === "create" ? "Add Identity Provider" : "Edit Identity Provider"}
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/frontend_new/src/components/AdminSpaceModal.tsx b/frontend_new/src/components/AdminSpaceModal.tsx
new file mode 100644
index 0000000..f7659af
--- /dev/null
+++ b/frontend_new/src/components/AdminSpaceModal.tsx
@@ -0,0 +1,294 @@
+"use client";
+
+import { useEffect, useState, useCallback } from "react";
+import apiClient from "@/lib/apiClient";
+import ConfirmActionModal from "./ConfirmActionModal";
+
+interface AdminSpace {
+ id: string;
+ name: string;
+ description: string;
+ icon?: string;
+ is_public: boolean;
+}
+
+interface AdminUser {
+ id: string;
+ username: string;
+}
+
+interface SpaceMember {
+ user_id: string;
+ username?: string;
+ joined_at?: string;
+}
+
+interface Props {
+ space: AdminSpace;
+ users: AdminUser[];
+ onClose: () => void;
+ onSaved: (updated: AdminSpace) => void;
+ onDeleted: (space: AdminSpace) => void;
+}
+
+export default function AdminSpaceModal({ space, users, onClose, onSaved, onDeleted }: Props) {
+ const [name, setName] = useState("");
+ const [description, setDescription] = useState("");
+ const [icon, setIcon] = useState("");
+ const [isPublic, setIsPublic] = useState(false);
+ const [savingSpace, setSavingSpace] = useState(false);
+
+ const [members, setMembers] = useState([]);
+ const [loadingMembers, setLoadingMembers] = useState(false);
+ const [addingMember, setAddingMember] = useState(false);
+ const [removingMemberId, setRemovingMemberId] = useState("");
+ const [newUserId, setNewUserId] = useState("");
+
+ const [error, setError] = useState("");
+ const [success, setSuccess] = useState("");
+
+ // confirm dialog state
+ const [confirmVisible, setConfirmVisible] = useState(false);
+ const [confirmBusy, setConfirmBusy] = useState(false);
+ const [confirmIntent, setConfirmIntent] = useState<{ type: "member" | "space"; payload: SpaceMember | AdminSpace | null }>({ type: "space", payload: null });
+
+ const clearMessages = () => {
+ setError("");
+ setSuccess("");
+ };
+
+ const loadMembers = useCallback(async () => {
+ setLoadingMembers(true);
+ clearMessages();
+ try {
+ const res = await apiClient.get(`/api/v1/admin/spaces/${space.id}/members`);
+ setMembers(res.data?.members || []);
+ } catch {
+ setError("Failed to load members.");
+ } finally {
+ setLoadingMembers(false);
+ }
+ }, [space.id]);
+
+ useEffect(() => {
+ setName(space.name || "");
+ setDescription(space.description || "");
+ setIcon(space.icon || "");
+ setIsPublic(!!space.is_public);
+ loadMembers();
+ }, [space, loadMembers]);
+
+ const saveSpace = async () => {
+ setSavingSpace(true);
+ clearMessages();
+ try {
+ const res = await apiClient.put(`/api/v1/admin/spaces/${space.id}`, { name, description, icon, is_public: isPublic });
+ setSuccess("Space updated.");
+ onSaved(res.data);
+ } catch {
+ setError("Failed to update space.");
+ } finally {
+ setSavingSpace(false);
+ }
+ };
+
+ const addMember = async (e: React.FormEvent) => {
+ e.preventDefault();
+ if (!newUserId) return;
+ setAddingMember(true);
+ clearMessages();
+ try {
+ await apiClient.post(`/api/v1/admin/spaces/${space.id}/members`, { user_id: newUserId });
+ setSuccess("Member added.");
+ setNewUserId("");
+ await loadMembers();
+ } catch {
+ setError("Failed to add member.");
+ } finally {
+ setAddingMember(false);
+ }
+ };
+
+ const requestRemoveMember = (member: SpaceMember) => {
+ setConfirmIntent({ type: "member", payload: member });
+ setConfirmVisible(true);
+ };
+
+ const requestDeleteSpace = () => {
+ setConfirmIntent({ type: "space", payload: space });
+ setConfirmVisible(true);
+ };
+
+ const confirmAction = async () => {
+ if (confirmBusy) return;
+ setConfirmBusy(true);
+ try {
+ if (confirmIntent.type === "member") {
+ const member = confirmIntent.payload as SpaceMember;
+ setRemovingMemberId(member.user_id);
+ await apiClient.delete(`/api/v1/admin/spaces/${space.id}/members/${member.user_id}`);
+ setSuccess("Member removed.");
+ await loadMembers();
+ setRemovingMemberId("");
+ } else {
+ await apiClient.delete(`/api/v1/admin/spaces/${space.id}`);
+ onDeleted(space);
+ }
+ setConfirmVisible(false);
+ } catch {
+ setError("Action failed.");
+ setRemovingMemberId("");
+ } finally {
+ setConfirmBusy(false);
+ }
+ };
+
+ const selectableUsers = users.filter((u) => !members.some((m) => m.user_id === u.id));
+
+ const formatDate = (iso?: string) => (iso ? new Date(iso).toLocaleDateString() : "—");
+
+ const confirmTitle = confirmIntent.type === "member" ? "Remove Member" : "Delete Space";
+ const confirmMessage =
+ confirmIntent.type === "member"
+ ? `Remove member "${(confirmIntent.payload as SpaceMember)?.username || (confirmIntent.payload as SpaceMember)?.user_id}" from this space?`
+ : `Permanently delete space "${space.name}"? All notes, categories, and members will be removed. This cannot be undone.`;
+
+ return (
+ <>
+ {
+ if (e.target === e.currentTarget && !confirmVisible) onClose();
+ }}
+ >
+
+
+
+
Edit Space
+
+
+
+ {/* Space settings */}
+
+
+
+ setName(e.target.value)} />
+
+
+
+ setDescription(e.target.value)} />
+
+
+
+ setIcon(e.target.value)} />
+
+
+
+ setIsPublic(e.target.checked)} />
+
+
+
+
+
+
+
+
+ {/* Members */}
+
+
Members
+
+
+
+
+
+ {loadingMembers ? (
+
Loading members...
+ ) : members.length === 0 ? (
+
No members found.
+ ) : (
+
+
+
+
+ | Username |
+ Joined |
+ Actions |
+
+
+
+ {members.map((m) => (
+
+ | {m.username || m.user_id} |
+ {formatDate(m.joined_at)} |
+
+
+ |
+
+ ))}
+
+
+
+ )}
+
+ {error &&
{error}
}
+ {success &&
{success}
}
+
+
+
+ {/* Danger zone */}
+
+
Danger Zone
+
Permanently delete this space and all its notes, categories, and members. This cannot be undone.
+
+
+
+
+
+
+
+
+ {
+ if (!confirmBusy) setConfirmVisible(false);
+ }}
+ onConfirm={confirmAction}
+ />
+ >
+ );
+}
diff --git a/frontend_new/src/components/AdminUserModal.tsx b/frontend_new/src/components/AdminUserModal.tsx
new file mode 100644
index 0000000..200154a
--- /dev/null
+++ b/frontend_new/src/components/AdminUserModal.tsx
@@ -0,0 +1,112 @@
+"use client";
+
+import { useEffect, useState } from "react";
+
+interface Group {
+ id: string;
+ name: string;
+}
+
+interface AdminUser {
+ id: string;
+ username: string;
+ email: string;
+ is_active: boolean;
+ group_ids?: string[];
+}
+
+interface Props {
+ user: AdminUser | null;
+ groups: Group[];
+ submitting: boolean;
+ onClose: () => void;
+ onSubmit: (data: { group_ids: string[] }) => void;
+}
+
+export default function AdminUserModal({ user, groups, submitting, onClose, onSubmit }: Props) {
+ const [groupIds, setGroupIds] = useState([]);
+
+ useEffect(() => {
+ setGroupIds([...(user?.group_ids || [])]);
+ }, [user]);
+
+ if (!user) return null;
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ onSubmit({ group_ids: groupIds });
+ };
+
+ const toggleGroup = (id: string) => {
+ setGroupIds((prev) => (prev.includes(id) ? prev.filter((g) => g !== id) : [...prev, id]));
+ };
+
+ return (
+ <>
+ {
+ if (e.target === e.currentTarget) onClose();
+ }}
+ >
+
+
+
+
Edit User
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/frontend_new/src/components/ConfirmActionModal.tsx b/frontend_new/src/components/ConfirmActionModal.tsx
new file mode 100644
index 0000000..a58892d
--- /dev/null
+++ b/frontend_new/src/components/ConfirmActionModal.tsx
@@ -0,0 +1,66 @@
+"use client";
+
+interface Props {
+ visible: boolean;
+ title?: string;
+ message?: string;
+ confirmLabel?: string;
+ cancelLabel?: string;
+ busyLabel?: string;
+ busy?: boolean;
+ onClose: () => void;
+ onConfirm: () => void;
+}
+
+export default function ConfirmActionModal({
+ visible,
+ title = "Confirm",
+ message = "Are you sure you want to continue?",
+ confirmLabel = "Delete",
+ cancelLabel = "Cancel",
+ busyLabel = "Deleting...",
+ busy = false,
+ onClose,
+ onConfirm,
+}: Props) {
+ if (!visible) return null;
+
+ return (
+ <>
+ {
+ if (e.target === e.currentTarget) onClose();
+ }}
+ >
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/frontend_new/src/components/Navbar.tsx b/frontend_new/src/components/Navbar.tsx
new file mode 100644
index 0000000..e5533ea
--- /dev/null
+++ b/frontend_new/src/components/Navbar.tsx
@@ -0,0 +1,160 @@
+"use client";
+
+import { useEffect, useRef, useState } from "react";
+import { useRouter } from "next/navigation";
+import { useAuthStore } from "@/stores/authStore";
+import { useSpaceStore, type Space } from "@/stores/spaceStore";
+
+interface NavbarProps {
+ onToggleSidebar?: () => void;
+ showSidebarToggle?: boolean;
+}
+
+export default function Navbar({ onToggleSidebar, showSidebarToggle }: NavbarProps) {
+ const router = useRouter();
+ const user = useAuthStore((s) => s.user);
+ const logout = useAuthStore((s) => s.logout);
+ const isAdmin = useAuthStore((s) => s.hasPermission("admin.access") || s.hasPermission("*"));
+ const spaces = useSpaceStore((s) => s.spaces);
+ const currentSpace = useSpaceStore((s) => s.currentSpace);
+ const selectSpace = useSpaceStore((s) => s.selectSpace);
+
+ const [showSpaceDropdown, setShowSpaceDropdown] = useState(false);
+ const [showUserMenu, setShowUserMenu] = useState(false);
+ const [searchQuery, setSearchQuery] = useState("");
+ const [isDarkMode, setIsDarkMode] = useState(false);
+
+ const spaceDropdownRef = useRef(null);
+ const userDropdownRef = useRef(null);
+
+ useEffect(() => {
+ const theme = document.documentElement.getAttribute("data-bs-theme");
+ setIsDarkMode(theme === "dark");
+ }, []);
+
+ useEffect(() => {
+ const theme = isDarkMode ? "dark" : "light";
+ document.documentElement.setAttribute("data-bs-theme", theme);
+ localStorage.setItem("theme", theme);
+ }, [isDarkMode]);
+
+ // Close dropdowns on outside click
+ useEffect(() => {
+ const handler = (e: MouseEvent) => {
+ if (spaceDropdownRef.current && !spaceDropdownRef.current.contains(e.target as Node)) {
+ setShowSpaceDropdown(false);
+ }
+ if (userDropdownRef.current && !userDropdownRef.current.contains(e.target as Node)) {
+ setShowUserMenu(false);
+ }
+ };
+ document.addEventListener("mousedown", handler);
+ return () => document.removeEventListener("mousedown", handler);
+ }, []);
+
+ const handleLogout = () => {
+ logout();
+ router.replace("/login");
+ };
+
+ const handleSpaceSelect = async (space: Space) => {
+ setShowSpaceDropdown(false);
+ await selectSpace(space.id);
+ router.push("/dashboard");
+ };
+
+ const performSearch = () => {
+ if (searchQuery.trim()) {
+ router.push(`/dashboard?search=${encodeURIComponent(searchQuery.trim())}`);
+ }
+ };
+
+ return (
+
+ );
+}
diff --git a/frontend_new/src/components/RichTextEditor.tsx b/frontend_new/src/components/RichTextEditor.tsx
new file mode 100644
index 0000000..0971836
--- /dev/null
+++ b/frontend_new/src/components/RichTextEditor.tsx
@@ -0,0 +1,618 @@
+"use client";
+
+import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from "react";
+import { useEditor, EditorContent, ReactRenderer, mergeAttributes } from "@tiptap/react";
+import { StarterKit } from "@tiptap/starter-kit";
+import { Link } from "@tiptap/extension-link";
+import { TaskList } from "@tiptap/extension-task-list";
+import { TaskItem } from "@tiptap/extension-task-item";
+import { Table, TableRow, TableCell, TableHeader } from "@tiptap/extension-table";
+import { Placeholder } from "@tiptap/extension-placeholder";
+import { Mention } from "@tiptap/extension-mention";
+import type { SuggestionOptions, SuggestionProps, SuggestionKeyDownProps } from "@tiptap/suggestion";
+import tippy, { type Instance as TippyInstance } from "tippy.js";
+import "tippy.js/dist/tippy.css";
+
+/* ─── Types ────────────────────────────────────────────────────────────── */
+export interface TaskListItem {
+ id: string;
+ name: string;
+}
+
+export interface TaskPickerItem {
+ id: string;
+ title: string;
+ statusColor: string;
+ statusName: string;
+}
+
+interface RichTextEditorProps {
+ content: string;
+ onChange?: (html: string) => void;
+ readOnly?: boolean;
+ placeholder?: string;
+ taskLists?: TaskListItem[];
+ spaceId?: string;
+ onNavigate?: (path: string) => void;
+ onFetchTasksForList?: (taskListId: string) => Promise;
+ minHeight?: number;
+}
+
+/* ─── Extended Mention node with statusColor + taskId attrs ──────────── */
+const TaskMention = Mention.extend({
+ addAttributes() {
+ return {
+ ...this.parent?.(),
+ statusColor: {
+ default: null,
+ parseHTML: (el) => el.getAttribute("data-status-color"),
+ renderHTML: (attrs) => (attrs.statusColor ? { "data-status-color": attrs.statusColor } : {}),
+ },
+ taskId: {
+ default: null,
+ parseHTML: (el) => el.getAttribute("data-task-id"),
+ renderHTML: (attrs) => (attrs.taskId ? { "data-task-id": attrs.taskId } : {}),
+ },
+ };
+ },
+ renderHTML({ node, HTMLAttributes }) {
+ const color = node.attrs.statusColor ?? "#7c8596";
+ return [
+ "span",
+ mergeAttributes(
+ {
+ class: "tasklist-mention-node",
+ "data-type": "mention",
+ style: `--status-color:${color}`,
+ },
+ HTMLAttributes,
+ ),
+ [
+ "i",
+ {
+ class: "mdi mdi-circle",
+ style: `color:${color}`,
+ "aria-hidden": "true",
+ },
+ ],
+ node.attrs.label ?? "",
+ ];
+ },
+ parseHTML() {
+ return [{ tag: 'span[data-type="mention"]' }];
+ },
+});
+
+/* ─── Two-stage mention dropdown ──────────────────────────────────────── */
+interface MentionListHandle {
+ onKeyDown: (props: SuggestionKeyDownProps) => boolean;
+}
+
+type MentionListProps = SuggestionProps & {
+ fetchTasks: ((taskListId: string) => Promise) | undefined;
+};
+
+const MentionList = forwardRef(({ items, command, fetchTasks }, ref) => {
+ const [stage, setStage] = useState<"list" | "task">("list");
+ const [selectedList, setSelectedList] = useState(null);
+ const [tasks, setTasks] = useState([]);
+ const [loadingTasks, setLoadingTasks] = useState(false);
+ const [cursor, setCursor] = useState(0);
+
+ const stageRef = useRef(stage);
+ const tasksRef = useRef(tasks);
+ const itemsRef = useRef(items);
+ useEffect(() => {
+ stageRef.current = stage;
+ }, [stage]);
+ useEffect(() => {
+ tasksRef.current = tasks;
+ }, [tasks]);
+ useEffect(() => {
+ itemsRef.current = items;
+ }, [items]);
+
+ async function pickList(list: TaskListItem) {
+ setSelectedList(list);
+ setCursor(0);
+ setTasks([]);
+ if (!fetchTasks) {
+ command({ id: list.id, label: list.name } as never);
+ return;
+ }
+ setStage("task");
+ setLoadingTasks(true);
+ try {
+ const fetched = await fetchTasks(list.id);
+ setTasks(fetched);
+ } finally {
+ setLoadingTasks(false);
+ }
+ }
+
+ function pickTask(task: TaskPickerItem) {
+ if (!selectedList) return;
+ command({
+ id: selectedList.id,
+ label: `${selectedList.name} — ${task.title}`,
+ statusColor: task.statusColor,
+ taskId: task.id,
+ } as never);
+ }
+
+ useImperativeHandle(ref, () => ({
+ onKeyDown: ({ event }: SuggestionKeyDownProps) => {
+ const cur = stageRef.current;
+ const list = cur === "list" ? itemsRef.current : tasksRef.current;
+ if (event.key === "ArrowUp") {
+ setCursor((s) => (s - 1 + Math.max(list.length, 1)) % Math.max(list.length, 1));
+ return true;
+ }
+ if (event.key === "ArrowDown") {
+ setCursor((s) => (s + 1) % Math.max(list.length, 1));
+ return true;
+ }
+ if (event.key === "Enter") {
+ if (cur === "list") {
+ const item = itemsRef.current[cursor];
+ if (item) {
+ pickList(item);
+ }
+ } else {
+ const task = tasksRef.current[cursor];
+ if (task) pickTask(task);
+ }
+ return true;
+ }
+ if ((event.key === "Backspace" || event.key === "Escape") && cur === "task") {
+ setStage("list");
+ setCursor(0);
+ return true;
+ }
+ return false;
+ },
+ }));
+
+ if (!items.length && stage === "list") return null;
+
+ return (
+
+ {stage === "list" ? (
+ <>
+
Task Lists
+ {items.map((tl, i) => (
+
+ ))}
+ >
+ ) : (
+ <>
+
+
+ {selectedList?.name}
+
+ {loadingTasks ? (
+
+
+ Loading tasks…
+
+ ) : tasks.length === 0 ? (
+
No tasks found
+ ) : (
+ tasks.map((task, i) => (
+
+ ))
+ )}
+ >
+ )}
+
+ );
+});
+MentionList.displayName = "MentionList";
+
+/* ─── Build suggestion config ─────────────────────────────────────────── */
+function buildSuggestion(
+ taskListsRef: React.MutableRefObject,
+ fetchTasksRef: React.MutableRefObject<((id: string) => Promise) | undefined>,
+): Partial> {
+ return {
+ char: "@",
+ allowSpaces: true,
+ items: ({ query }) => taskListsRef.current.filter((tl) => tl.name.toLowerCase().includes(query.toLowerCase())).slice(0, 10),
+
+ render: () => {
+ let component: ReactRenderer;
+ let popup: TippyInstance[];
+
+ return {
+ onStart: (props) => {
+ component = new ReactRenderer(MentionList, {
+ props: { ...props, fetchTasks: fetchTasksRef.current } as MentionListProps,
+ editor: props.editor,
+ });
+
+ if (!props.clientRect) return;
+ popup = tippy("body", {
+ getReferenceClientRect: props.clientRect as () => DOMRect,
+ appendTo: () => document.body,
+ content: component.element,
+ showOnCreate: true,
+ interactive: true,
+ trigger: "manual",
+ placement: "bottom-start",
+ });
+ },
+ onUpdate: (props) => {
+ component?.updateProps({
+ ...props,
+ fetchTasks: fetchTasksRef.current,
+ } as MentionListProps);
+ if (props.clientRect) {
+ popup?.[0]?.setProps({
+ getReferenceClientRect: props.clientRect as () => DOMRect,
+ });
+ }
+ },
+ onKeyDown: (props) => {
+ if (props.event.key === "Escape") {
+ popup?.[0]?.hide();
+ return true;
+ }
+ return component?.ref?.onKeyDown(props) ?? false;
+ },
+ onExit: () => {
+ popup?.[0]?.destroy();
+ component?.destroy();
+ },
+ };
+ },
+ };
+}
+
+/* ─── Toolbar button helper ────────────────────────────────────────────── */
+function ToolBtn({ title, active, disabled, onClick, children }: { title: string; active?: boolean; disabled?: boolean; onClick: () => void; children: React.ReactNode }) {
+ return (
+
+ );
+}
+
+/* ─── Link dialog ──────────────────────────────────────────────────────── */
+function LinkDialog({ onConfirm, onCancel }: { onConfirm: (url: string) => void; onCancel: () => void }) {
+ const [url, setUrl] = useState("https://");
+ return (
+
+ setUrl(e.target.value)}
+ placeholder="https://…"
+ autoFocus
+ onKeyDown={(e) => {
+ if (e.key === "Enter") onConfirm(url);
+ if (e.key === "Escape") onCancel();
+ }}
+ />
+
+
+
+ );
+}
+
+/* ─── Main component ───────────────────────────────────────────────────── */
+export default function RichTextEditor({
+ content,
+ onChange,
+ readOnly = false,
+ placeholder = "Write something…",
+ taskLists = [],
+ spaceId,
+ onNavigate,
+ onFetchTasksForList,
+ minHeight = 300,
+}: RichTextEditorProps) {
+ const taskListsRef = useRef(taskLists);
+ const fetchTasksRef = useRef<((id: string) => Promise) | undefined>(onFetchTasksForList);
+ useEffect(() => {
+ taskListsRef.current = taskLists;
+ }, [taskLists]);
+ useEffect(() => {
+ fetchTasksRef.current = onFetchTasksForList;
+ }, [onFetchTasksForList]);
+
+ const [showLinkDialog, setShowLinkDialog] = useState(false);
+ const [headingOpen, setHeadingOpen] = useState(false);
+
+ const editor = useEditor({
+ extensions: [
+ StarterKit.configure({ heading: { levels: [1, 2, 3] } }),
+ Link.configure({ openOnClick: false, HTMLAttributes: { rel: "noopener noreferrer" } }),
+ TaskList,
+ TaskItem.configure({ nested: true }),
+ Table.configure({ resizable: false }),
+ TableRow,
+ TableCell,
+ TableHeader,
+ Placeholder.configure({ placeholder }),
+ TaskMention.configure({
+ HTMLAttributes: { class: "tasklist-mention-node" },
+ suggestion: buildSuggestion(taskListsRef, fetchTasksRef),
+ }),
+ ],
+ content: content || "",
+ editable: !readOnly,
+ onUpdate: ({ editor }) => {
+ onChange?.(editor.getHTML());
+ },
+ editorProps: {
+ attributes: {
+ class: "rte-content",
+ style: `min-height:${minHeight}px`,
+ },
+ },
+ immediatelyRender: false,
+ });
+
+ // Sync content when parent changes it (e.g. loading a new note)
+ const prevContentRef = useRef(content);
+ useEffect(() => {
+ if (!editor) return;
+ // Only reset when content changed externally (not from user typing)
+ if (content !== prevContentRef.current && content !== editor.getHTML()) {
+ editor.commands.setContent(content || "", { emitUpdate: false });
+ }
+ prevContentRef.current = content;
+ }, [content, editor]);
+
+ // Update editable state
+ useEffect(() => {
+ if (!editor) return;
+ editor.setEditable(!readOnly);
+ }, [readOnly, editor]);
+
+ // Handle clicks on mention nodes in read-only mode for navigation
+ const handleEditorClick = useCallback(
+ (e: React.MouseEvent) => {
+ if (!readOnly || !spaceId || !onNavigate) return;
+ const target = (e.target as HTMLElement).closest("[data-type='mention']") as HTMLElement | null;
+ if (target?.dataset.id) {
+ e.preventDefault();
+ onNavigate(`/dashboard/spaces/${spaceId}/tasklists/${target.dataset.id}`);
+ }
+ },
+ [readOnly, spaceId, onNavigate],
+ );
+
+ if (!editor) return null;
+
+ // --- READ-ONLY ---
+ if (readOnly) {
+ return (
+
+
+
+ );
+ }
+
+ // --- EDITABLE ---
+ const isTable = editor.isActive("table");
+
+ return (
+
+ {/* Toolbar */}
+
+ {/* History */}
+
editor.chain().focus().undo().run()}>
+
+
+
editor.chain().focus().redo().run()}>
+
+
+
+
+
+ {/* Heading dropdown */}
+
+
+ {headingOpen && (
+
setHeadingOpen(false)}>
+ {(["Normal", "H1", "H2", "H3"] as const).map((label) => (
+
+ ))}
+
+ )}
+
+
+
+
+ {/* Inline marks */}
+
editor.chain().focus().toggleBold().run()}>
+
+
+
editor.chain().focus().toggleItalic().run()}>
+
+
+
editor.chain().focus().toggleStrike().run()}>
+
+
+
editor.chain().focus().toggleCode().run()}>
+
+
+
+
+
+ {/* Lists */}
+
editor.chain().focus().toggleBulletList().run()}>
+
+
+
editor.chain().focus().toggleOrderedList().run()}>
+
+
+
editor.chain().focus().toggleTaskList().run()}>
+
+
+
+
+
+ {/* Blocks */}
+
editor.chain().focus().toggleBlockquote().run()}>
+
+
+
editor.chain().focus().toggleCodeBlock().run()}>
+
+
+
+
+
+ {/* Table */}
+ {!isTable ? (
+
editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run()}>
+
+
+ ) : (
+ <>
+
editor.chain().focus().addColumnAfter().run()}>
+
+
+
editor.chain().focus().deleteColumn().run()}>
+
+
+
editor.chain().focus().addRowAfter().run()}>
+
+
+
editor.chain().focus().deleteRow().run()}>
+
+
+
editor.chain().focus().deleteTable().run()}>
+
+
+ >
+ )}
+
+ {/* Link */}
+
{
+ if (editor.isActive("link")) {
+ editor.chain().focus().unsetLink().run();
+ } else {
+ setShowLinkDialog(true);
+ }
+ }}
+ >
+
+
+
+
+ {/* Link dialog */}
+ {showLinkDialog && (
+
+ {
+ editor.chain().focus().setLink({ href: url }).run();
+ setShowLinkDialog(false);
+ }}
+ onCancel={() => setShowLinkDialog(false)}
+ />
+
+ )}
+
+ {/* Editor area */}
+
+
+
+
+ );
+}
diff --git a/frontend_new/src/components/Sidebar.tsx b/frontend_new/src/components/Sidebar.tsx
new file mode 100644
index 0000000..d970618
--- /dev/null
+++ b/frontend_new/src/components/Sidebar.tsx
@@ -0,0 +1,125 @@
+"use client";
+
+import { useState } from "react";
+import { useRouter } from "next/navigation";
+import { useAuthStore } from "@/stores/authStore";
+import { useSpaceStore, type Category } from "@/stores/spaceStore";
+
+interface SidebarProps {
+ open: boolean;
+ onClose: () => void;
+ navbarHeight?: number;
+ onOpenCreateCategory?: () => void;
+ onOpenSpaceSettings?: () => void;
+}
+
+export default function Sidebar({ open, onClose, navbarHeight = 56, onOpenCreateCategory, onOpenSpaceSettings }: SidebarProps) {
+ const currentSpace = useSpaceStore((s) => s.currentSpace);
+ const categoryTree = useSpaceStore((s) => s.categoryTree);
+ const hasPermission = useAuthStore((s) => s.hasPermission);
+ const hasSpacePermission = useAuthStore((s) => s.hasSpacePermission);
+
+ const canCreateCategories = hasPermission("*") || hasSpacePermission(currentSpace, "categories.create");
+ const canManageSettings = hasPermission("*") || hasSpacePermission(currentSpace, "settings.manage");
+
+ return (
+ <>
+ {/* Mobile backdrop */}
+ {open && }
+
+
+ >
+ );
+}
+
+function CategoryTreeView({ categories, spaceId }: { categories: Category[]; spaceId: string }) {
+ return (
+ <>
+ {categories.map((cat) => (
+
+ ))}
+ >
+ );
+}
+
+function CategoryNode({ category, spaceId }: { category: Category; spaceId: string }) {
+ const router = useRouter();
+ const [expanded, setExpanded] = useState(true);
+
+ const subcategories = category.subcategories ?? category.children ?? [];
+ const notes = category.notes ?? [];
+ const taskLists = category.task_lists ?? [];
+ const hasContent = subcategories.length > 0 || notes.length > 0 || taskLists.length > 0;
+
+ return (
+
+
setExpanded((v) => !v)} style={{ cursor: "pointer" }}>
+ {hasContent ? (
+
+ ) : (
+
+ )}
+ {category.name}
+
+
+ {expanded && hasContent && (
+
+ {taskLists.map((tl) => (
+
{
+ e.stopPropagation();
+ router.push(`/dashboard/spaces/${spaceId}/tasklists/${tl.id}`);
+ }}
+ >
+
+ {tl.name}
+
+ ))}
+ {notes.map((note) => (
+
{
+ e.stopPropagation();
+ router.push(`/dashboard/spaces/${spaceId}/notes/${note.id}`);
+ }}
+ >
+
+ {note.title}
+ {note.is_pinned && }
+ {!note.is_pinned && note.is_favorite && }
+
+ ))}
+ {subcategories.length > 0 &&
}
+
+ )}
+
+ );
+}
diff --git a/frontend_new/src/components/SpaceSettingsModal.tsx b/frontend_new/src/components/SpaceSettingsModal.tsx
new file mode 100644
index 0000000..4de7949
--- /dev/null
+++ b/frontend_new/src/components/SpaceSettingsModal.tsx
@@ -0,0 +1,320 @@
+"use client";
+
+import { useCallback, useEffect, useState } from "react";
+import { Space, SpaceMember } from "@/stores/spaceStore";
+import { useAuthStore } from "@/stores/authStore";
+import apiClient from "@/lib/apiClient";
+
+interface SpaceSettingsModalProps {
+ space: Space;
+ onClose: () => void;
+ onSaved: (space: Space) => void;
+ onDeleted: () => void;
+}
+
+interface AvailableUser {
+ id: string;
+ username: string;
+}
+
+type ConfirmIntent = { type: "member"; payload: SpaceMember } | { type: "space" } | null;
+
+export default function SpaceSettingsModal({ space, onClose, onSaved, onDeleted }: SpaceSettingsModalProps) {
+ const hasSpacePermission = useAuthStore((s) => s.hasSpacePermission);
+ const hasPermission = useAuthStore((s) => s.hasPermission);
+
+ const canViewMembers = hasPermission("*") || hasSpacePermission(space, "settings.member.view");
+ const canManageMembers = hasPermission("*") || hasSpacePermission(space, "settings.member.manage");
+ const canDeleteSpace = hasPermission("*") || hasSpacePermission(space, "settings.delete");
+
+ const [form, setForm] = useState({ name: space.name || "", is_public: !!space.is_public });
+ const [saving, setSaving] = useState(false);
+ const [error, setError] = useState("");
+ const [success, setSuccess] = useState("");
+
+ const [members, setMembers] = useState([]);
+ const [userOptions, setUserOptions] = useState([]);
+ const [loadingMembers, setLoadingMembers] = useState(false);
+ const [addingMember, setAddingMember] = useState(false);
+ const [memberUserId, setMemberUserId] = useState("");
+ const [removingMemberId, setRemovingMemberId] = useState("");
+
+ const [confirmIntent, setConfirmIntent] = useState(null);
+ const [confirmBusy, setConfirmBusy] = useState(false);
+
+ const clearMessages = () => {
+ setError("");
+ setSuccess("");
+ };
+
+ const loadMembers = useCallback(async () => {
+ if (!canViewMembers) return;
+ setLoadingMembers(true);
+ clearMessages();
+ try {
+ const res = await apiClient.get(`/api/v1/spaces/${space.id}/members`);
+ setMembers(res.data.members || []);
+ } catch (e: unknown) {
+ const err = e as { response?: { data?: string } };
+ setError(err.response?.data || "Failed to load members.");
+ } finally {
+ setLoadingMembers(false);
+ }
+ }, [space.id, canViewMembers]);
+
+ const loadUserOptions = useCallback(async () => {
+ if (!canManageMembers) return;
+ try {
+ const res = await apiClient.get(`/api/v1/spaces/${space.id}/available-users`);
+ setUserOptions(res.data.users || []);
+ } catch {
+ // ignore
+ }
+ }, [space.id, canManageMembers]);
+
+ useEffect(() => {
+ if (canViewMembers) {
+ Promise.all([loadMembers(), loadUserOptions()]);
+ }
+ }, []);
+
+ async function saveSettings() {
+ setSaving(true);
+ clearMessages();
+ try {
+ const res = await apiClient.put(`/api/v1/spaces/${space.id}`, {
+ name: form.name,
+ is_public: form.is_public,
+ });
+ setSuccess("Space settings saved.");
+ onSaved(res.data);
+ } catch (e: unknown) {
+ const err = e as { response?: { data?: string } };
+ setError(err.response?.data || "Failed to save settings.");
+ } finally {
+ setSaving(false);
+ }
+ }
+
+ async function addMember(e: React.FormEvent) {
+ e.preventDefault();
+ if (!canManageMembers || !memberUserId) return;
+ setAddingMember(true);
+ clearMessages();
+ try {
+ await apiClient.post(`/api/v1/spaces/${space.id}/members`, { user_id: memberUserId });
+ setSuccess("Member added.");
+ setMemberUserId("");
+ await Promise.all([loadMembers(), loadUserOptions()]);
+ } catch (e: unknown) {
+ const err = e as { response?: { data?: string } };
+ setError(err.response?.data || "Failed to add member.");
+ } finally {
+ setAddingMember(false);
+ }
+ }
+
+ async function confirmAction() {
+ if (!confirmIntent || confirmBusy) return;
+ setConfirmBusy(true);
+ clearMessages();
+ try {
+ if (confirmIntent.type === "member") {
+ const member = confirmIntent.payload;
+ setRemovingMemberId(member.user_id);
+ await apiClient.delete(`/api/v1/spaces/${space.id}/members/${member.user_id}`);
+ setSuccess("Member removed.");
+ setConfirmIntent(null);
+ await Promise.all([loadMembers(), loadUserOptions()]);
+ } else if (confirmIntent.type === "space") {
+ await apiClient.delete(`/api/v1/spaces/${space.id}`);
+ onDeleted();
+ }
+ } catch (e: unknown) {
+ const err = e as { response?: { data?: string } };
+ setError(err.response?.data || "Action failed.");
+ setConfirmIntent(null);
+ } finally {
+ setConfirmBusy(false);
+ setRemovingMemberId("");
+ }
+ }
+
+ const formatDate = (iso?: string) => (iso ? new Date(iso).toLocaleDateString() : "-");
+
+ const confirmTitle = confirmIntent?.type === "member" ? "Remove Member" : "Delete Space";
+ const confirmMessage =
+ confirmIntent?.type === "member"
+ ? `Remove "${confirmIntent.payload.username || confirmIntent.payload.user_id}" from this space?`
+ : `Permanently delete space "${space.name}"? All notes, categories, and members will be removed. This cannot be undone.`;
+
+ return (
+ <>
+ e.target === e.currentTarget && !confirmIntent && onClose()}
+ >
+
+
+
+
Space Settings
+
+
+
+
+
+ setForm((p) => ({ ...p, name: e.target.value }))}
+ />
+
+
+
+ setForm((p) => ({ ...p, is_public: e.target.checked }))}
+ />
+
+
+
+
+
+
+
+ {canViewMembers && (
+ <>
+
+
+
Members
+
+
+
+ {canManageMembers && (
+
+ )}
+
+ {loadingMembers ? (
+
Loading members...
+ ) : members.length === 0 ? (
+
No members found.
+ ) : (
+
+
+
+
+ | Username |
+ Joined |
+ Actions |
+
+
+
+ {members.map((m) => (
+
+ | {m.username || m.user_id} |
+ {formatDate(m.joined_at)} |
+
+
+ |
+
+ ))}
+
+
+
+ )}
+ >
+ )}
+
+ {canDeleteSpace && (
+ <>
+
+
+
Danger Zone
+
+ Permanently delete this space and all its notes, categories, and members. This cannot be undone.
+
+
+
+ >
+ )}
+
+ {error &&
{error}
}
+ {success &&
{success}
}
+
+
+
+
+
+
+ {confirmIntent && (
+
+
+
+
+
{confirmTitle}
+
+
+
+
+
+
+
+
+
+
+ )}
+ >
+ );
+}
diff --git a/frontend_new/src/lib/apiClient.ts b/frontend_new/src/lib/apiClient.ts
new file mode 100644
index 0000000..39f6dc0
--- /dev/null
+++ b/frontend_new/src/lib/apiClient.ts
@@ -0,0 +1,59 @@
+import axios from "axios";
+
+const apiClient = axios.create({
+ baseURL: typeof window !== "undefined" ? window.location.origin : "",
+ withCredentials: true,
+});
+
+let isRefreshing = false;
+let refreshSubscribers: Array<() => void> = [];
+
+function onRefreshed() {
+ refreshSubscribers.forEach((cb) => cb());
+ refreshSubscribers = [];
+}
+
+apiClient.interceptors.response.use(
+ (response) => response,
+ async (error) => {
+ const originalRequest = error.config;
+
+ if (error.response?.status === 401 && !originalRequest._retry) {
+ if (originalRequest.url?.includes("/auth/refresh") || originalRequest.url?.includes("/auth/login")) {
+ // Lazy-import to avoid circular dependency
+ const { useAuthStore } = await import("@/stores/authStore");
+ useAuthStore.getState().clearSession();
+ return Promise.reject(error);
+ }
+
+ if (isRefreshing) {
+ return new Promise((resolve, reject) => {
+ refreshSubscribers.push(() => {
+ originalRequest._retry = true;
+ apiClient(originalRequest).then(resolve).catch(reject);
+ });
+ });
+ }
+
+ originalRequest._retry = true;
+ isRefreshing = true;
+
+ try {
+ await apiClient.post("/api/v1/auth/refresh");
+ onRefreshed();
+ return apiClient(originalRequest);
+ } catch {
+ refreshSubscribers = [];
+ const { useAuthStore } = await import("@/stores/authStore");
+ useAuthStore.getState().clearSession();
+ return Promise.reject(error);
+ } finally {
+ isRefreshing = false;
+ }
+ }
+
+ return Promise.reject(error);
+ },
+);
+
+export default apiClient;
diff --git a/frontend_new/src/lib/markdown.ts b/frontend_new/src/lib/markdown.ts
new file mode 100644
index 0000000..d857172
--- /dev/null
+++ b/frontend_new/src/lib/markdown.ts
@@ -0,0 +1,35 @@
+import { marked } from "marked";
+import { markedHighlight } from "marked-highlight";
+import hljs from "highlight.js/lib/common";
+
+marked.use(
+ markedHighlight({
+ langPrefix: "hljs language-",
+ highlight(code, lang) {
+ if (lang && hljs.getLanguage(lang)) {
+ return hljs.highlight(code, { language: lang }).value;
+ }
+ return hljs.highlightAuto(code).value;
+ },
+ }),
+);
+
+/**
+ * Preprocesses markdown with extended image size syntax:
+ * 
+ */
+export function preprocessMarkdown(content: string): string {
+ if (!content) return content;
+ return content.replace(/!\[([^\]]*)\]\(([^\s)"]+)(?:\s+"([^"]*)")?\s+=(\d*%?)[xX](\d*%?)\)/gi, (_, alt, url, title, w, h) => {
+ const safeAlt = alt.replace(/"/g, """);
+ let attrs = `src="${url}" alt="${safeAlt}"`;
+ if (title) attrs += ` title="${title.replace(/"/g, """)}"`;
+ if (w) attrs += ` width="${w}"`;
+ if (h) attrs += ` height="${h}"`;
+ return `
`;
+ });
+}
+
+export function renderMarkdown(content: string): string {
+ return marked.parse(preprocessMarkdown(content || ""), { async: false }) as string;
+}
diff --git a/frontend_new/src/stores/authStore.ts b/frontend_new/src/stores/authStore.ts
new file mode 100644
index 0000000..9dd41ec
--- /dev/null
+++ b/frontend_new/src/stores/authStore.ts
@@ -0,0 +1,127 @@
+import { create } from "zustand";
+import apiClient from "@/lib/apiClient";
+
+export interface User {
+ id: string;
+ username: string;
+ email: string;
+ first_name: string;
+ last_name: string;
+ is_active: boolean;
+ created_at: string;
+ permissions: string[];
+ groups?: Array<{ id: string; name: string }>;
+}
+
+interface AuthState {
+ user: User | null;
+ initialized: boolean;
+ initPromise: Promise | null;
+
+ // Actions
+ setSession: (data: unknown) => void;
+ clearSession: () => void;
+ loadSession: () => Promise;
+ ensureInitialized: () => Promise;
+ login: (email: string, password: string) => Promise;
+ logout: () => void;
+ register: (email: string, username: string, password: string, firstName?: string, lastName?: string) => Promise;
+ hasPermission: (permission: string) => boolean;
+ hasSpacePermission: (space: { permission_key?: string } | null, action: string) => boolean;
+}
+
+const normalizePermission = (p: string) => (p || "").trim().toLowerCase();
+
+const permissionMatches = (pattern: string, permission: string): boolean => {
+ const p = normalizePermission(pattern);
+ const q = normalizePermission(permission);
+ if (!p || !q) return false;
+ if (p === "*" || p === q) return true;
+ if (!p.includes("*")) return false;
+ const escaped = p.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
+ return new RegExp(`^${escaped}$`).test(q);
+};
+
+export const useAuthStore = create((set, get) => ({
+ user: null,
+ initialized: false,
+ initPromise: null,
+
+ setSession(data: unknown) {
+ const d = data as { user?: User } | null;
+ set({ user: d?.user ?? null, initialized: true });
+ },
+
+ clearSession() {
+ set({ user: null, initialized: true });
+ },
+
+ async loadSession() {
+ try {
+ const res = await apiClient.get("/api/v1/auth/me");
+ set({ user: res.data?.user ?? null, initialized: true });
+ } catch {
+ set({ user: null, initialized: true });
+ }
+ },
+
+ async ensureInitialized() {
+ const state = get();
+ if (state.initialized) return;
+ if (state.initPromise) return state.initPromise;
+
+ const promise = get()
+ .loadSession()
+ .finally(() => set({ initPromise: null }));
+ set({ initPromise: promise });
+ return promise;
+ },
+
+ async login(email, password) {
+ try {
+ const res = await apiClient.post("/api/v1/auth/login", {
+ email: email?.trim(),
+ password,
+ });
+ get().setSession(res.data);
+ return res.data;
+ } catch (err: unknown) {
+ const e = err as { response?: { data?: { message?: string } }; message?: string };
+ throw e.response?.data?.message || e.message;
+ }
+ },
+
+ logout() {
+ apiClient.post("/api/v1/auth/logout").catch(() => {});
+ get().clearSession();
+ },
+
+ async register(email, username, password, firstName = "", lastName = "") {
+ try {
+ const res = await apiClient.post("/api/v1/auth/register", {
+ email,
+ username,
+ password,
+ password_confirm: password,
+ first_name: firstName,
+ last_name: lastName,
+ });
+ get().setSession(res.data);
+ return res.data;
+ } catch (err: unknown) {
+ const e = err as { response?: { data?: { message?: string } }; message?: string };
+ throw e.response?.data?.message || e.message;
+ }
+ },
+
+ hasPermission(permission: string) {
+ const perms = get().user?.permissions ?? [];
+ return perms.some((p) => permissionMatches(p, permission));
+ },
+
+ hasSpacePermission(space, action) {
+ const token = space?.permission_key ?? "";
+ if (!token) return false;
+ return get().hasPermission(`space.${token}.${action}`);
+ },
+}));
diff --git a/frontend_new/src/stores/settingsStore.ts b/frontend_new/src/stores/settingsStore.ts
new file mode 100644
index 0000000..18a0e45
--- /dev/null
+++ b/frontend_new/src/stores/settingsStore.ts
@@ -0,0 +1,42 @@
+import { create } from "zustand";
+import apiClient from "@/lib/apiClient";
+
+export interface FeatureFlags {
+ registration_enabled: boolean;
+ provider_login_enabled: boolean;
+ public_sharing_enabled: boolean;
+ file_explorer_enabled: boolean;
+}
+
+const DEFAULT_FLAGS: FeatureFlags = {
+ registration_enabled: true,
+ provider_login_enabled: true,
+ public_sharing_enabled: true,
+ file_explorer_enabled: false,
+};
+
+interface SettingsState {
+ featureFlags: FeatureFlags;
+ flagsLoaded: boolean;
+ loadFeatureFlags: (force?: boolean) => Promise;
+}
+
+export const useSettingsStore = create((set, get) => ({
+ featureFlags: { ...DEFAULT_FLAGS },
+ flagsLoaded: false,
+
+ async loadFeatureFlags(force = false) {
+ const state = get();
+ if (state.flagsLoaded && !force) return state.featureFlags;
+
+ try {
+ const res = await apiClient.get("/api/v1/settings/feature-flags");
+ const flags = { ...DEFAULT_FLAGS, ...res.data };
+ set({ featureFlags: flags, flagsLoaded: true });
+ return flags;
+ } catch {
+ set({ featureFlags: { ...DEFAULT_FLAGS }, flagsLoaded: true });
+ return get().featureFlags;
+ }
+ },
+}));
diff --git a/frontend_new/src/stores/spaceStore.ts b/frontend_new/src/stores/spaceStore.ts
new file mode 100644
index 0000000..05a8615
--- /dev/null
+++ b/frontend_new/src/stores/spaceStore.ts
@@ -0,0 +1,213 @@
+import { create } from "zustand";
+import apiClient from "@/lib/apiClient";
+
+export interface Space {
+ id: string;
+ name: string;
+ description: string;
+ is_public: boolean;
+ permission_key?: string;
+}
+
+export interface SpaceMember {
+ user_id: string;
+ username?: string;
+ joined_at?: string;
+}
+
+export interface NoteListItem {
+ id: string;
+ space_id: string;
+ category_id?: string | null;
+ title: string;
+ description: string;
+ is_pinned: boolean;
+ is_favorite: boolean;
+ is_public: boolean;
+ is_password_protected: boolean;
+ updated_at: string;
+}
+
+export interface Category {
+ id: string;
+ name: string;
+ space_id: string;
+ parent_id?: string | null;
+ description?: string;
+ icon?: string;
+ order?: number;
+ subcategories?: Category[];
+ children?: Category[];
+ notes?: NoteListItem[];
+ task_lists?: TaskList[];
+}
+
+export interface Note {
+ id: string;
+ title: string;
+ description: string;
+ content: string;
+ tags: string[];
+ space_id: string;
+ category_id?: string | null;
+ is_pinned: boolean;
+ is_favorite: boolean;
+ is_public: boolean;
+ is_password_protected: boolean;
+ created_by: string;
+ updated_by: string;
+ created_at: string;
+ updated_at: string;
+}
+
+export interface TaskList {
+ id: string;
+ name: string;
+ description?: string;
+ space_id: string;
+ category_id?: string | null;
+ created_at?: string;
+ updated_at?: string;
+}
+
+export interface Task {
+ id: string;
+ space_id: string;
+ title: string;
+ description: string;
+ task_list_id: string;
+ status_id: string;
+ parent_task_id?: string | null;
+ depth: number;
+ note_links?: string[];
+ created_by: string;
+ updated_by: string;
+ created_at: string;
+ updated_at: string;
+ status_name?: string;
+ status_color?: string;
+ status_order?: number;
+ subtasks?: Task[];
+}
+
+export interface TaskStatus {
+ id: string;
+ task_list_id: string;
+ name: string;
+ color?: string;
+ order: number;
+ created_at: string;
+ updated_at: string;
+}
+
+interface SpaceState {
+ spaces: Space[];
+ currentSpace: Space | null;
+ notes: Note[];
+ categoryTree: Category[];
+ taskLists: TaskList[];
+ notesLoading: boolean;
+ notesHasMore: boolean;
+
+ fetchSpaces: () => Promise;
+ selectSpace: (spaceId: string) => Promise;
+ fetchCategories: (spaceId: string) => Promise;
+ fetchNotes: (spaceId: string, options?: { reset?: boolean }) => Promise;
+ fetchTaskLists: (spaceId: string) => Promise;
+ setCurrentSpace: (space: Space | null) => void;
+ reset: () => void;
+}
+
+export const useSpaceStore = create((set, get) => ({
+ spaces: [],
+ currentSpace: null,
+ notes: [],
+ categoryTree: [],
+ taskLists: [],
+ notesLoading: false,
+ notesHasMore: true,
+
+ async fetchSpaces() {
+ try {
+ const res = await apiClient.get("/api/v1/spaces");
+ const spaces: Space[] = res.data || [];
+ set({ spaces });
+
+ // Auto-restore previously selected space
+ const savedId = typeof window !== "undefined" ? localStorage.getItem("selectedSpaceId") : null;
+ if (savedId && spaces.some((s) => s.id === savedId)) {
+ const current = get().currentSpace;
+ if (!current || current.id !== savedId) {
+ await get().selectSpace(savedId);
+ }
+ }
+ } catch {
+ set({ spaces: [] });
+ }
+ },
+
+ async selectSpace(spaceId) {
+ try {
+ const res = await apiClient.get(`/api/v1/spaces/${spaceId}`);
+ set({ currentSpace: res.data });
+ if (typeof window !== "undefined") {
+ localStorage.setItem("selectedSpaceId", spaceId);
+ }
+ await Promise.all([get().fetchCategories(spaceId), get().fetchNotes(spaceId), get().fetchTaskLists(spaceId)]);
+ } catch {
+ // ignore
+ }
+ },
+
+ async fetchCategories(spaceId) {
+ try {
+ const res = await apiClient.get(`/api/v1/spaces/${spaceId}/categories`);
+ set({ categoryTree: res.data || [] });
+ } catch {
+ set({ categoryTree: [] });
+ }
+ },
+
+ async fetchNotes(spaceId, { reset = true } = {}) {
+ if (get().notesLoading) return;
+ set({ notesLoading: true });
+ try {
+ const skip = reset ? 0 : get().notes.length;
+ const limit = 20;
+ const res = await apiClient.get(`/api/v1/spaces/${spaceId}/notes`, {
+ params: { skip, limit },
+ });
+ const fetched: Note[] = res.data || [];
+ set((s) => ({
+ notes: reset ? fetched : [...s.notes, ...fetched],
+ notesHasMore: fetched.length === limit,
+ notesLoading: false,
+ }));
+ } catch {
+ set({ notesLoading: false });
+ }
+ },
+
+ async fetchTaskLists(spaceId) {
+ try {
+ const res = await apiClient.get(`/api/v1/spaces/${spaceId}/task-lists`);
+ set({ taskLists: res.data || [] });
+ } catch {
+ set({ taskLists: [] });
+ }
+ },
+
+ setCurrentSpace(space) {
+ set({ currentSpace: space });
+ },
+
+ reset() {
+ set({
+ currentSpace: null,
+ notes: [],
+ categoryTree: [],
+ taskLists: [],
+ notesHasMore: true,
+ });
+ },
+}));
diff --git a/frontend_new/src/styles/globals.css b/frontend_new/src/styles/globals.css
new file mode 100644
index 0000000..1351108
--- /dev/null
+++ b/frontend_new/src/styles/globals.css
@@ -0,0 +1,1260 @@
+:root {
+ --color-primary: #667eea;
+ --color-primary-strong: #4f46a5;
+ --color-text: #333333;
+ --color-text-muted: #6c757d;
+ --color-bg: #f8f9fa;
+ --color-surface: #ffffff;
+ --color-surface-muted: #f1f3f5;
+ --color-border: #dee2e6;
+ --color-info: #748ffc;
+ --color-code-bg: #353943;
+ --color-code-text: #f9fafb;
+ --color-scroll-track: #f1f1f1;
+ --color-scroll-thumb: #888888;
+ --color-scroll-thumb-hover: #555555;
+
+ --primary-color: var(--color-primary);
+ --secondary-color: var(--color-primary-strong);
+ --text-color: var(--color-text);
+ --bg-color: var(--color-bg);
+ --border-color: var(--color-border);
+}
+
+[data-bs-theme="dark"] {
+ --color-text: #e2e8f0;
+ --color-text-muted: #94a3b8;
+ --color-bg: #1a1d23;
+ --color-surface: #21252e;
+ --color-surface-muted: #2d3748;
+ --color-border: #3a3f4b;
+ --color-info: #7aa2f7;
+ --color-code-bg: #2d3748;
+ --color-code-text: #e2e8f0;
+ --color-scroll-track: #2d3748;
+ --color-scroll-thumb: #4a5568;
+ --color-scroll-thumb-hover: #718096;
+
+ --text-color: var(--color-text);
+ --bg-color: var(--color-bg);
+ --border-color: var(--color-border);
+}
+
+[data-bs-theme="dark"] body {
+ background-color: var(--color-bg);
+ color: var(--color-text);
+}
+
+[data-bs-theme="dark"] .sidebar {
+ background-color: var(--color-surface) !important;
+ border-color: var(--color-border) !important;
+}
+
+[data-bs-theme="dark"] .toolbar {
+ background-color: var(--color-surface);
+ border-color: var(--color-border) !important;
+}
+
+[data-bs-theme="dark"] .main-content {
+ background-color: var(--color-bg);
+}
+
+[data-bs-theme="dark"] ::-webkit-scrollbar-track {
+ background: var(--color-scroll-track);
+}
+
+[data-bs-theme="dark"] ::-webkit-scrollbar-thumb {
+ background: var(--color-scroll-thumb);
+}
+
+[data-bs-theme="dark"] ::-webkit-scrollbar-thumb:hover {
+ background: var(--color-scroll-thumb-hover);
+}
+
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+html,
+body,
+#__next {
+ height: 100%;
+ width: 100%;
+}
+
+body {
+ font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
+ color: var(--text-color);
+ background-color: var(--bg-color);
+}
+
+::-webkit-scrollbar {
+ width: 8px;
+ height: 8px;
+}
+
+::-webkit-scrollbar-track {
+ background: var(--color-scroll-track);
+}
+
+::-webkit-scrollbar-thumb {
+ background: var(--color-scroll-thumb);
+ border-radius: 4px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background: var(--color-scroll-thumb-hover);
+}
+
+/* ── App Layout ─────────────────────────────── */
+.app-container {
+ display: flex;
+ flex-direction: column;
+ height: 100vh;
+ overflow-x: hidden;
+}
+
+.app-main {
+ flex: 1;
+ overflow: hidden;
+}
+
+.navbar {
+ z-index: 1100;
+ overflow: visible;
+}
+
+.app-navbar {
+ align-items: center;
+}
+
+.navbar-left {
+ min-width: 0;
+}
+
+.navbar-controls {
+ min-width: 0;
+}
+
+.app-brand {
+ white-space: nowrap;
+}
+
+.sidebar {
+ width: 280px;
+ overflow-y: auto;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+}
+
+.sidebar-header {
+ border-bottom: 1px solid var(--color-border);
+}
+
+.sidebar-content {
+ flex: 1;
+ min-height: 0;
+ overflow-y: auto;
+}
+
+.main-content {
+ display: flex;
+ flex-direction: column;
+ overflow-y: auto;
+}
+
+.toolbar {
+ position: relative;
+ z-index: 0;
+}
+
+.breadcrumb-title {
+ font-size: 1rem;
+ color: var(--color-text);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.search-box {
+ width: 300px;
+}
+
+.dropdown {
+ position: relative;
+}
+
+.dropdown-menu {
+ z-index: 1200;
+ max-width: min(92vw, 320px);
+}
+
+.dropdown-menu-end {
+ right: 0;
+ left: auto;
+}
+
+.dropdown-menu.show {
+ display: block;
+}
+
+/* ── Auth pages ──────────────────────────────── */
+.login-page,
+.register-page {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ min-height: 100vh;
+ padding: 1.25rem;
+ background: radial-gradient(circle at 10% 10%, rgba(255, 255, 255, 0.2), transparent 45%), linear-gradient(135deg, #3554d1 0%, #4f46a5 100%);
+}
+
+.auth-container {
+ width: 100%;
+ max-width: 460px;
+}
+
+.register-container {
+ width: 100%;
+ max-width: 560px;
+}
+
+.login-card,
+.register-card {
+ background: var(--color-surface);
+ padding: 2rem;
+ border-radius: 18px;
+ box-shadow: 0 22px 48px rgba(16, 24, 40, 0.22);
+ width: 100%;
+}
+
+.brand-block {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.75rem;
+ margin-bottom: 1.25rem;
+}
+
+.brand-mark {
+ width: 42px;
+ height: 42px;
+ display: grid;
+ place-items: center;
+ border-radius: 12px;
+ background: rgba(102, 126, 234, 0.15);
+ color: var(--color-primary);
+ font-size: 1.35rem;
+}
+
+.brand-title {
+ margin: 0;
+ font-size: 2.05rem;
+ font-weight: 700;
+ letter-spacing: 0.01em;
+ color: var(--color-text);
+}
+
+.auth-title {
+ text-align: center;
+ font-size: 2.1rem;
+ font-weight: 600;
+ margin-bottom: 1.5rem;
+ color: var(--color-text);
+}
+
+.auth-submit {
+ min-height: 48px;
+ font-weight: 600;
+}
+
+.auth-switch-link {
+ color: var(--color-text-muted);
+}
+
+.oauth-divider {
+ display: flex;
+ align-items: center;
+ color: var(--color-text-muted);
+ font-size: 0.9rem;
+}
+
+.oauth-divider::before,
+.oauth-divider::after {
+ content: "";
+ flex: 1;
+ border-bottom: 1px solid var(--color-border);
+}
+
+.oauth-divider span {
+ padding: 0 0.75rem;
+}
+
+.auth-provider-btn {
+ min-height: 48px;
+ border-radius: 0.65rem;
+}
+
+/* ── Admin pages ─────────────────────────────── */
+.admin-page {
+ width: 100%;
+ max-width: none;
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ min-height: 0;
+ overflow: hidden;
+}
+
+.admin-topbar {
+ flex-wrap: wrap;
+ padding: 1rem;
+ border-bottom: 1px solid var(--color-border);
+}
+
+.admin-shell {
+ display: flex;
+ flex: 1;
+ min-height: 0;
+ overflow: hidden;
+}
+
+.admin-sidebar {
+ width: 220px;
+ flex-shrink: 0;
+ background: var(--color-bg);
+ border-right: 1px solid var(--color-border);
+}
+
+.admin-sidebar-inner {
+ padding: 0.75rem;
+}
+
+.admin-nav .nav-link {
+ border-radius: 0.6rem;
+ color: var(--color-text-muted);
+ font-weight: 500;
+ cursor: pointer;
+}
+
+.admin-nav .nav-link:hover {
+ background: var(--color-surface-muted);
+ color: var(--color-text);
+}
+
+.admin-nav .nav-link.active {
+ background: var(--color-text);
+ color: var(--color-surface);
+}
+
+.admin-content {
+ flex: 1;
+ min-width: 0;
+ overflow-y: auto;
+ padding: 1rem;
+}
+
+.admin-section {
+ border-radius: 12px;
+}
+
+.users-list .list-group-item {
+ padding: 1rem;
+}
+
+.user-row {
+ display: flex;
+ gap: 1rem;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.user-row-main {
+ flex: 1;
+ min-width: 0;
+}
+
+.user-row-actions {
+ flex-shrink: 0;
+}
+
+.user-name-line {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ margin-bottom: 0.6rem;
+}
+
+.user-name {
+ font-size: 1.1rem;
+}
+
+.user-meta-grid {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 0.75rem 1.25rem;
+}
+
+.user-meta-label {
+ font-size: 0.75rem;
+ text-transform: uppercase;
+ letter-spacing: 0.04em;
+ color: var(--color-text-muted);
+ margin-bottom: 0.1rem;
+}
+
+.user-meta-value {
+ color: var(--color-text);
+ overflow-wrap: anywhere;
+}
+
+.feature-flag-item {
+ transition: background 0.15s;
+}
+
+/* ── Category tree ───────────────────────────── */
+.category-tree-item {
+ cursor: pointer;
+ padding: 0.35rem 0.5rem;
+ border-radius: 0.4rem;
+ display: flex;
+ align-items: center;
+ gap: 0.4rem;
+ user-select: none;
+ font-size: 0.9rem;
+}
+
+.category-tree-item:hover {
+ background: var(--color-surface-muted);
+}
+
+.category-tree-item.active {
+ background: rgba(102, 126, 234, 0.15);
+ color: var(--color-primary);
+ font-weight: 500;
+}
+
+.category-tree-children {
+ padding-left: 1.1rem;
+ border-left: 1px solid var(--color-border);
+ margin-left: 0.65rem;
+}
+
+/* ── Notes grid ──────────────────────────────── */
+.notes-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
+ gap: 1rem;
+ padding: 1rem;
+}
+
+.note-card {
+ cursor: pointer;
+ border-radius: 12px;
+ border: 1px solid var(--color-border);
+ padding: 1rem;
+ background: var(--color-surface);
+ transition:
+ box-shadow 0.15s,
+ border-color 0.15s;
+}
+
+.note-card:hover {
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
+ border-color: var(--color-primary);
+}
+
+.note-card-title {
+ font-weight: 600;
+ margin-bottom: 0.35rem;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.note-card-preview {
+ font-size: 0.85rem;
+ color: var(--color-text-muted);
+ overflow: hidden;
+ display: -webkit-box;
+ -webkit-line-clamp: 3;
+ -webkit-box-orient: vertical;
+}
+
+/* ── Category sections ───────────────────────── */
+.category-section {
+}
+
+.category-section-header {
+ user-select: none;
+}
+
+.category-items-row {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.75rem;
+}
+
+/* ── Unified content cards ───────────────────── */
+.content-card {
+ cursor: pointer;
+ border-radius: 12px;
+ border: 1px solid var(--color-border);
+ padding: 0.85rem 1rem;
+ background: var(--color-surface);
+ transition:
+ box-shadow 0.15s,
+ border-color 0.15s;
+ width: 180px;
+ min-height: 90px;
+ display: flex;
+ flex-direction: column;
+ gap: 0.3rem;
+ flex-shrink: 0;
+}
+
+.content-card:hover {
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
+ border-color: var(--color-primary);
+}
+
+.content-card-icon {
+ font-size: 1.2rem;
+ color: var(--color-text-muted);
+ line-height: 1;
+}
+
+.content-card-title {
+ font-weight: 600;
+ font-size: 0.875rem;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.content-card-meta {
+ font-size: 0.75rem;
+ color: var(--color-text-muted);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.task-list-card .content-card-icon {
+ color: var(--color-primary);
+}
+
+.create-card {
+ border-style: dashed;
+ border-color: var(--color-border);
+ background: transparent;
+ color: var(--color-text-muted);
+}
+
+.create-card:hover {
+ border-color: var(--color-primary);
+ color: var(--color-primary);
+}
+
+.create-card .content-card-title {
+ font-weight: 500;
+ color: inherit;
+}
+
+.create-card .create-icon {
+ color: inherit;
+}
+
+/* ── Notes list ──────────────────────────────── */
+.notes-list {
+ padding: 1rem;
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+}
+
+.note-list-item {
+ cursor: pointer;
+ border-radius: 8px;
+ border: 1px solid var(--color-border);
+ padding: 0.75rem 1rem;
+ background: var(--color-surface);
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ transition: border-color 0.15s;
+}
+
+.note-list-item:hover {
+ border-color: var(--color-primary);
+}
+
+.note-list-item.active {
+ border-color: var(--color-primary);
+ background: rgba(102, 126, 234, 0.06);
+}
+
+/* ── Responsive ──────────────────────────────── */
+@media (max-width: 768px) {
+ .app-navbar {
+ display: grid;
+ grid-template-columns: 1fr auto;
+ grid-template-areas:
+ "left user"
+ "space space"
+ "search search";
+ row-gap: 0.5rem;
+ column-gap: 0.5rem;
+ align-items: center;
+ }
+
+ .navbar-left {
+ grid-area: left;
+ }
+
+ .nav-user-menu {
+ grid-area: user;
+ justify-self: end;
+ }
+
+ .nav-space-selector {
+ grid-area: space;
+ }
+
+ .nav-space-selector > .btn {
+ width: 100%;
+ text-align: left;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .nav-search {
+ grid-area: search;
+ width: 100%;
+ }
+
+ .nav-search .form-control {
+ width: 100%;
+ }
+
+ .search-box {
+ width: 100%;
+ }
+
+ .sidebar-backdrop {
+ position: fixed;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: rgba(0, 0, 0, 0.5);
+ z-index: 1090;
+ }
+
+ .sidebar {
+ position: fixed;
+ left: 0;
+ bottom: 0;
+ width: 280px;
+ z-index: 1095;
+ transform: translateX(-100%);
+ transition: transform 0.3s ease-in-out;
+ box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
+ overflow-y: auto;
+ }
+
+ .sidebar.open {
+ transform: translateX(0);
+ }
+
+ .admin-shell {
+ display: block;
+ }
+
+ .admin-sidebar {
+ display: none;
+ }
+
+ .admin-sidebar.open {
+ display: block;
+ position: fixed;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ z-index: 1200;
+ box-shadow: 4px 0 20px rgba(0, 0, 0, 0.15);
+ }
+
+ .admin-sidebar-backdrop {
+ position: fixed;
+ inset: 0;
+ background: rgba(0, 0, 0, 0.4);
+ z-index: 1199;
+ }
+
+ .user-meta-grid {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+
+ .notes-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .login-card,
+ .register-card {
+ border-radius: 14px;
+ padding: 1.35rem;
+ }
+
+ .brand-title {
+ font-size: 1.8rem;
+ }
+
+ .auth-title {
+ font-size: 1.85rem;
+ }
+}
+
+/* ========================================
+ MODAL Z-INDEX FIX
+ Bootstrap's .modal creates a stacking context (z-index 1055).
+ .modal-backdrop has z-index 1040 > .modal-dialog (z-index auto=0),
+ so the backdrop renders above the dialog. Fix by giving .modal-dialog
+ an explicit z-index above the backdrop.
+ ======================================== */
+.modal > .modal-dialog {
+ position: relative;
+ z-index: 1056;
+}
+
+/* ========================================
+ NOTE VIEWER / EDITOR
+ ======================================== */
+
+.note-viewer-container,
+.note-editor-container {
+ max-width: 100%;
+ height: 100%;
+ overflow-y: auto;
+}
+
+.note-viewer-title {
+ font-size: 1.75rem;
+ font-weight: 600;
+ color: var(--color-text);
+}
+
+.note-title-input {
+ font-size: 1.5rem;
+ font-weight: 600;
+ border: none;
+ border-bottom: 2px solid var(--color-border);
+ border-radius: 0;
+ padding: 0.25rem 0;
+ background: transparent;
+ color: var(--color-text);
+}
+
+.note-title-input:focus {
+ box-shadow: none;
+ border-bottom-color: var(--color-primary);
+ outline: none;
+}
+
+.note-content-textarea {
+ font-family: "Fira Code", "Cascadia Code", monospace;
+ font-size: 0.9rem;
+ line-height: 1.6;
+ resize: vertical;
+}
+
+.note-viewer-content {
+ font-size: 1rem;
+ line-height: 1.7;
+ color: var(--color-text);
+ word-break: break-word;
+}
+
+.note-viewer-content h1,
+.note-viewer-content h2,
+.note-viewer-content h3,
+.note-viewer-content h4,
+.note-viewer-content h5,
+.note-viewer-content h6 {
+ margin-top: 1.5rem;
+ margin-bottom: 0.5rem;
+ font-weight: 600;
+}
+
+.note-viewer-content p {
+ margin-bottom: 1rem;
+}
+
+.note-viewer-content pre {
+ background: var(--color-code-bg);
+ color: var(--color-code-text);
+ padding: 1rem;
+ border-radius: 0.375rem;
+ overflow-x: auto;
+ font-size: 0.875rem;
+}
+
+.note-viewer-content code:not(pre code) {
+ background: var(--color-surface-muted);
+ padding: 0.1rem 0.3rem;
+ border-radius: 0.25rem;
+ font-size: 0.875rem;
+}
+
+.note-viewer-content blockquote {
+ border-left: 4px solid var(--color-primary);
+ padding-left: 1rem;
+ color: var(--color-text-muted);
+ font-style: italic;
+ margin: 1rem 0;
+}
+
+.note-viewer-content table {
+ width: 100%;
+ border-collapse: collapse;
+ margin-bottom: 1rem;
+}
+
+.note-viewer-content th,
+.note-viewer-content td {
+ border: 1px solid var(--color-border);
+ padding: 0.5rem 0.75rem;
+ text-align: left;
+}
+
+.note-viewer-content th {
+ background: var(--color-surface-muted);
+ font-weight: 600;
+}
+
+.note-viewer-content img {
+ max-width: 100%;
+ height: auto;
+ border-radius: 0.375rem;
+}
+
+.task-inline-link {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.25rem;
+ padding: 0.1rem 0.5rem;
+ border-radius: 1rem;
+ border: 1px solid currentColor;
+ font-size: 0.8rem;
+ text-decoration: none;
+ font-weight: 500;
+ cursor: pointer;
+}
+
+.task-suggestions-dropdown {
+ position: absolute;
+ bottom: 100%;
+ left: 0;
+ right: 0;
+ background: var(--bs-body-bg);
+ border: 1px solid var(--color-border);
+ border-radius: 0.375rem;
+ z-index: 1050;
+ max-height: 200px;
+ overflow-y: auto;
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
+}
+
+/* ========================================
+ TASK BOARD
+ ======================================== */
+
+.task-board {
+ max-width: 100%;
+}
+
+.task-status-section {
+ background: var(--color-surface);
+ border: 1px solid var(--color-border);
+ border-radius: 0.5rem;
+ padding: 1rem;
+}
+
+.task-row {
+ border-radius: 0.375rem;
+ transition: background 0.15s;
+}
+
+.task-row:hover {
+ background: var(--color-surface-muted) !important;
+}
+
+.task-row.level-0 {
+ font-size: 0.95rem;
+}
+.task-row.level-1 {
+ font-size: 0.9rem;
+}
+.task-row.level-2 {
+ font-size: 0.85rem;
+ opacity: 0.9;
+}
+
+/* ========================================
+ RICH TEXT EDITOR (TipTap)
+ ======================================== */
+
+.rte-wrapper {
+ border: 1px solid var(--color-border);
+ border-radius: 0.5rem;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ background: var(--bs-body-bg);
+}
+
+.rte-wrapper.rte-readonly {
+ border: none;
+ background: transparent;
+}
+
+/* ── Toolbar ── */
+.rte-toolbar {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 2px;
+ padding: 6px 8px;
+ border-bottom: 1px solid var(--color-border);
+ background: var(--color-surface-muted);
+}
+
+.rte-tool-btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ min-width: 30px;
+ height: 30px;
+ padding: 0 6px;
+ border: none;
+ border-radius: 4px;
+ background: transparent;
+ color: var(--color-text);
+ cursor: pointer;
+ font-size: 0.95rem;
+ font-weight: 500;
+ transition:
+ background 0.12s,
+ color 0.12s;
+}
+
+.rte-tool-btn:hover:not(:disabled) {
+ background: var(--color-border);
+}
+
+.rte-tool-btn.is-active {
+ background: var(--color-primary);
+ color: #fff;
+}
+
+.rte-tool-btn:disabled {
+ opacity: 0.35;
+ cursor: default;
+}
+
+.rte-toolbar-sep {
+ width: 1px;
+ height: 20px;
+ background: var(--color-border);
+ margin: 0 4px;
+ flex-shrink: 0;
+}
+
+/* Heading dropdown */
+.rte-heading-btn {
+ min-width: 52px;
+ font-size: 0.8rem;
+}
+
+.rte-dropdown-wrap {
+ position: relative;
+}
+
+.rte-dropdown-menu {
+ position: absolute;
+ top: calc(100% + 4px);
+ left: 0;
+ z-index: 1200;
+ background: var(--bs-body-bg);
+ border: 1px solid var(--color-border);
+ border-radius: 6px;
+ min-width: 100px;
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
+ overflow: hidden;
+}
+
+.rte-dropdown-item {
+ display: block;
+ width: 100%;
+ padding: 6px 14px;
+ border: none;
+ background: transparent;
+ color: var(--color-text);
+ text-align: left;
+ cursor: pointer;
+ font-size: 0.875rem;
+}
+
+.rte-dropdown-item:hover {
+ background: var(--color-surface-muted);
+}
+
+/* ── Link dialog ── */
+.rte-link-dialog-wrap {
+ padding: 6px 10px;
+ border-bottom: 1px solid var(--color-border);
+ background: var(--color-surface-muted);
+ display: flex;
+ align-items: center;
+}
+
+/* ── Editor area ── */
+.rte-editor-area {
+ flex: 1;
+ overflow-y: auto;
+}
+
+.rte-content {
+ padding: 14px 16px;
+ outline: none;
+ color: var(--color-text);
+ font-size: 1rem;
+ line-height: 1.7;
+}
+
+.rte-content p.is-editor-empty:first-child::before {
+ content: attr(data-placeholder);
+ color: var(--color-text-muted);
+ pointer-events: none;
+ float: left;
+ height: 0;
+}
+
+/* Typography in editor */
+.rte-content h1 {
+ font-size: 1.8rem;
+ font-weight: 700;
+ margin: 1.2rem 0 0.5rem;
+}
+.rte-content h2 {
+ font-size: 1.4rem;
+ font-weight: 600;
+ margin: 1rem 0 0.4rem;
+}
+.rte-content h3 {
+ font-size: 1.15rem;
+ font-weight: 600;
+ margin: 0.9rem 0 0.3rem;
+}
+.rte-content p {
+ margin: 0 0 0.75rem;
+}
+.rte-content p:last-child {
+ margin-bottom: 0;
+}
+
+.rte-content ul,
+.rte-content ol {
+ padding-left: 1.5rem;
+ margin-bottom: 0.75rem;
+}
+
+.rte-content li {
+ margin-bottom: 0.2rem;
+}
+
+.rte-content blockquote {
+ border-left: 4px solid var(--color-primary);
+ padding-left: 1rem;
+ color: var(--color-text-muted);
+ font-style: italic;
+ margin: 0.75rem 0;
+}
+
+.rte-content pre {
+ background: var(--color-code-bg);
+ color: var(--color-code-text);
+ padding: 0.9rem 1rem;
+ border-radius: 0.375rem;
+ overflow-x: auto;
+ font-size: 0.875rem;
+ font-family: "Fira Code", "Cascadia Code", monospace;
+ margin-bottom: 0.75rem;
+}
+
+.rte-content code:not(pre code) {
+ background: var(--color-surface-muted);
+ padding: 0.1rem 0.35rem;
+ border-radius: 0.25rem;
+ font-size: 0.875rem;
+}
+
+.rte-content hr {
+ border: none;
+ border-top: 1px solid var(--color-border);
+ margin: 1rem 0;
+}
+
+/* Task list (checkboxes) */
+.rte-content ul[data-type="taskList"] {
+ list-style: none;
+ padding-left: 0;
+}
+
+.rte-content ul[data-type="taskList"] li {
+ display: flex;
+ align-items: flex-start;
+ gap: 0.5rem;
+}
+
+.rte-content ul[data-type="taskList"] li > label {
+ margin-top: 2px;
+ flex-shrink: 0;
+}
+
+.rte-content ul[data-type="taskList"] li > div {
+ flex: 1;
+}
+
+/* Tables */
+.rte-content table {
+ width: 100%;
+ border-collapse: collapse;
+ margin-bottom: 1rem;
+}
+
+.rte-content th,
+.rte-content td {
+ border: 1px solid var(--color-border);
+ padding: 0.4rem 0.75rem;
+ text-align: left;
+ min-width: 60px;
+}
+
+.rte-content th {
+ background: var(--color-surface-muted);
+ font-weight: 600;
+}
+
+.rte-content .selectedCell {
+ background: rgba(102, 126, 234, 0.12);
+}
+
+/* ── @TaskList mention pill ── */
+.tasklist-mention-node {
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
+ padding: 1px 10px 1px 8px;
+ border-radius: 1rem;
+ background: rgba(102, 126, 234, 0.14);
+ border: 1px solid var(--color-primary);
+ color: var(--color-primary);
+ font-size: 0.8rem;
+ font-weight: 600;
+ white-space: nowrap;
+ cursor: pointer;
+ user-select: none;
+ transition: background 0.12s;
+}
+
+.rte-readonly .tasklist-mention-node:hover {
+ background: rgba(102, 126, 234, 0.28);
+}
+
+/* ── Mention suggestion dropdown ── */
+.rte-mention-dropdown {
+ background: var(--bs-body-bg);
+ border: 1px solid var(--color-border);
+ border-radius: 6px;
+ min-width: 200px;
+ overflow: hidden;
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
+}
+
+.rte-mention-item {
+ display: flex;
+ align-items: center;
+ width: 100%;
+ padding: 7px 14px;
+ border: none;
+ background: transparent;
+ color: var(--color-text);
+ text-align: left;
+ cursor: pointer;
+ font-size: 0.875rem;
+ transition: background 0.1s;
+}
+
+.rte-mention-item:hover,
+.rte-mention-item.is-selected {
+ background: var(--color-primary);
+ color: #fff;
+}
+
+.rte-mention-header {
+ padding: 5px 14px;
+ font-size: 0.72rem;
+ font-weight: 700;
+ letter-spacing: 0.06em;
+ text-transform: uppercase;
+ color: var(--color-text-muted, #8b92a9);
+ border-bottom: 1px solid var(--color-border);
+ background: var(--color-bg-subtle, rgba(0, 0, 0, 0.04));
+}
+
+.rte-mention-back {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 20px;
+ height: 20px;
+ padding: 0;
+ border: none;
+ background: transparent;
+ color: inherit;
+ cursor: pointer;
+ border-radius: 3px;
+ flex-shrink: 0;
+}
+.rte-mention-back:hover {
+ background: rgba(0, 0, 0, 0.1);
+}
+
+.rte-mention-loading,
+.rte-mention-empty {
+ padding: 10px 14px;
+ font-size: 0.875rem;
+ color: var(--color-text-muted, #8b92a9);
+ display: flex;
+ align-items: center;
+}
+
+.rte-mention-status-label {
+ font-size: 0.72rem;
+ opacity: 0.65;
+ margin-left: auto;
+ white-space: nowrap;
+}
+
+.rte-status-dot {
+ display: inline-block;
+ width: 9px;
+ height: 9px;
+ border-radius: 50%;
+ flex-shrink: 0;
+}
diff --git a/frontend_new/tsconfig.json b/frontend_new/tsconfig.json
new file mode 100644
index 0000000..c35a01b
--- /dev/null
+++ b/frontend_new/tsconfig.json
@@ -0,0 +1,27 @@
+{
+ "compilerOptions": {
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "react-jsx",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./src/*"]
+ },
+ "target": "ES2017"
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", ".next/dev/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}
diff --git a/frontend_new/tsconfig.tsbuildinfo b/frontend_new/tsconfig.tsbuildinfo
new file mode 100644
index 0000000..a555d7b
--- /dev/null
+++ b/frontend_new/tsconfig.tsbuildinfo
@@ -0,0 +1 @@
+{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.es2023.d.ts","./node_modules/typescript/lib/lib.es2024.d.ts","./node_modules/typescript/lib/lib.esnext.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.es2023.array.d.ts","./node_modules/typescript/lib/lib.es2023.collection.d.ts","./node_modules/typescript/lib/lib.es2023.intl.d.ts","./node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2024.collection.d.ts","./node_modules/typescript/lib/lib.es2024.object.d.ts","./node_modules/typescript/lib/lib.es2024.promise.d.ts","./node_modules/typescript/lib/lib.es2024.regexp.d.ts","./node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2024.string.d.ts","./node_modules/typescript/lib/lib.esnext.array.d.ts","./node_modules/typescript/lib/lib.esnext.collection.d.ts","./node_modules/typescript/lib/lib.esnext.intl.d.ts","./node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/typescript/lib/lib.esnext.promise.d.ts","./node_modules/typescript/lib/lib.esnext.decorators.d.ts","./node_modules/typescript/lib/lib.esnext.iterator.d.ts","./node_modules/typescript/lib/lib.esnext.float16.d.ts","./node_modules/typescript/lib/lib.esnext.error.d.ts","./node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/@types/react/global.d.ts","./node_modules/csstype/index.d.ts","./node_modules/@types/react/index.d.ts","./node_modules/next/dist/styled-jsx/types/css.d.ts","./node_modules/next/dist/styled-jsx/types/macro.d.ts","./node_modules/next/dist/styled-jsx/types/style.d.ts","./node_modules/next/dist/styled-jsx/types/global.d.ts","./node_modules/next/dist/styled-jsx/types/index.d.ts","./node_modules/next/dist/server/get-page-files.d.ts","./node_modules/@types/node/compatibility/disposable.d.ts","./node_modules/@types/node/compatibility/indexable.d.ts","./node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/@types/node/compatibility/index.d.ts","./node_modules/@types/node/globals.typedarray.d.ts","./node_modules/@types/node/buffer.buffer.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/@types/node/web-globals/events.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/file.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/undici-types/connector.d.ts","./node_modules/undici-types/client.d.ts","./node_modules/undici-types/errors.d.ts","./node_modules/undici-types/dispatcher.d.ts","./node_modules/undici-types/global-dispatcher.d.ts","./node_modules/undici-types/global-origin.d.ts","./node_modules/undici-types/pool-stats.d.ts","./node_modules/undici-types/pool.d.ts","./node_modules/undici-types/handlers.d.ts","./node_modules/undici-types/balanced-pool.d.ts","./node_modules/undici-types/agent.d.ts","./node_modules/undici-types/mock-interceptor.d.ts","./node_modules/undici-types/mock-agent.d.ts","./node_modules/undici-types/mock-client.d.ts","./node_modules/undici-types/mock-pool.d.ts","./node_modules/undici-types/mock-errors.d.ts","./node_modules/undici-types/proxy-agent.d.ts","./node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/undici-types/retry-handler.d.ts","./node_modules/undici-types/retry-agent.d.ts","./node_modules/undici-types/api.d.ts","./node_modules/undici-types/interceptors.d.ts","./node_modules/undici-types/util.d.ts","./node_modules/undici-types/cookies.d.ts","./node_modules/undici-types/patch.d.ts","./node_modules/undici-types/websocket.d.ts","./node_modules/undici-types/eventsource.d.ts","./node_modules/undici-types/filereader.d.ts","./node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/undici-types/content-type.d.ts","./node_modules/undici-types/cache.d.ts","./node_modules/undici-types/index.d.ts","./node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/@types/node/web-globals/navigator.d.ts","./node_modules/@types/node/web-globals/storage.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.d.ts","./node_modules/@types/node/inspector.generated.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/readline/promises.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/sea.d.ts","./node_modules/@types/node/sqlite.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/web.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/test.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/index.d.ts","./node_modules/@types/react/canary.d.ts","./node_modules/@types/react/experimental.d.ts","./node_modules/@types/react-dom/index.d.ts","./node_modules/@types/react-dom/canary.d.ts","./node_modules/@types/react-dom/experimental.d.ts","./node_modules/next/dist/lib/fallback.d.ts","./node_modules/next/dist/compiled/webpack/webpack.d.ts","./node_modules/next/dist/shared/lib/modern-browserslist-target.d.ts","./node_modules/next/dist/shared/lib/entry-constants.d.ts","./node_modules/next/dist/shared/lib/constants.d.ts","./node_modules/next/dist/lib/bundler.d.ts","./node_modules/next/dist/server/config.d.ts","./node_modules/next/dist/lib/load-custom-routes.d.ts","./node_modules/next/dist/shared/lib/image-config.d.ts","./node_modules/next/dist/build/webpack/plugins/subresource-integrity-plugin.d.ts","./node_modules/next/dist/server/body-streams.d.ts","./node_modules/next/dist/server/request/search-params.d.ts","./node_modules/next/dist/shared/lib/segment-cache/vary-params-decoding.d.ts","./node_modules/next/dist/server/app-render/vary-params.d.ts","./node_modules/next/dist/server/request/params.d.ts","./node_modules/next/dist/server/route-kind.d.ts","./node_modules/next/dist/server/route-definitions/route-definition.d.ts","./node_modules/next/dist/server/route-matches/route-match.d.ts","./node_modules/next/dist/client/components/app-router-headers.d.ts","./node_modules/next/dist/server/lib/cache-control.d.ts","./node_modules/next/dist/shared/lib/app-router-types.d.ts","./node_modules/next/dist/server/lib/cache-handlers/types.d.ts","./node_modules/next/dist/server/use-cache/use-cache-wrapper.d.ts","./node_modules/next/dist/server/resume-data-cache/cache-store.d.ts","./node_modules/next/dist/server/resume-data-cache/resume-data-cache.d.ts","./node_modules/next/dist/lib/constants.d.ts","./node_modules/next/dist/server/render-result.d.ts","./node_modules/next/dist/server/response-cache/types.d.ts","./node_modules/next/dist/server/response-cache/index.d.ts","./node_modules/@types/react/jsx-runtime.d.ts","./node_modules/next/dist/next-devtools/userspace/pages/pages-dev-overlay-setup.d.ts","./node_modules/next/dist/build/static-paths/types.d.ts","./node_modules/next/dist/server/route-definitions/app-page-route-definition.d.ts","./node_modules/next/dist/build/adapter/setup-node-env.external.d.ts","./node_modules/next/dist/server/instrumentation/types.d.ts","./node_modules/next/dist/lib/setup-exception-listeners.d.ts","./node_modules/next/dist/lib/worker.d.ts","./node_modules/next/dist/server/lib/experimental/ppr.d.ts","./node_modules/next/dist/lib/page-types.d.ts","./node_modules/next/dist/build/segment-config/app/app-segment-config.d.ts","./node_modules/next/dist/build/segment-config/pages/pages-segment-config.d.ts","./node_modules/next/dist/build/analysis/get-page-static-info.d.ts","./node_modules/next/dist/build/webpack/loaders/get-module-build-info.d.ts","./node_modules/next/dist/build/webpack/plugins/middleware-plugin.d.ts","./node_modules/next/dist/server/require-hook.d.ts","./node_modules/next/dist/server/node-polyfill-crypto.d.ts","./node_modules/next/dist/server/node-environment-baseline.d.ts","./node_modules/next/dist/server/node-environment-extensions/error-inspect.d.ts","./node_modules/next/dist/server/node-environment-extensions/console-file.d.ts","./node_modules/next/dist/server/node-environment-extensions/console-exit.d.ts","./node_modules/next/dist/server/node-environment-extensions/console-dim.external.d.ts","./node_modules/next/dist/server/node-environment-extensions/unhandled-rejection.external.d.ts","./node_modules/next/dist/server/node-environment-extensions/random.d.ts","./node_modules/next/dist/server/node-environment-extensions/date.d.ts","./node_modules/next/dist/server/node-environment-extensions/web-crypto.d.ts","./node_modules/next/dist/server/node-environment-extensions/node-crypto.d.ts","./node_modules/next/dist/server/node-environment-extensions/fast-set-immediate.external.d.ts","./node_modules/next/dist/server/node-environment.d.ts","./node_modules/next/dist/build/page-extensions-type.d.ts","./node_modules/next/dist/server/route-modules/app-page/module.compiled.d.ts","./node_modules/next/dist/server/route-definitions/app-route-route-definition.d.ts","./node_modules/next/dist/server/lib/i18n-provider.d.ts","./node_modules/next/dist/server/web/next-url.d.ts","./node_modules/next/dist/compiled/@edge-runtime/cookies/index.d.ts","./node_modules/next/dist/server/web/spec-extension/cookies.d.ts","./node_modules/next/dist/server/web/spec-extension/request.d.ts","./node_modules/next/dist/shared/lib/deep-readonly.d.ts","./node_modules/next/dist/server/lib/incremental-cache/index.d.ts","./node_modules/next/dist/shared/lib/router/utils/middleware-route-matcher.d.ts","./node_modules/next/dist/build/webpack/plugins/flight-manifest-plugin.d.ts","./node_modules/next/dist/build/webpack/plugins/next-font-manifest-plugin.d.ts","./node_modules/next/dist/server/route-definitions/locale-route-definition.d.ts","./node_modules/next/dist/server/route-definitions/pages-route-definition.d.ts","./node_modules/next/dist/shared/lib/mitt.d.ts","./node_modules/next/dist/client/with-router.d.ts","./node_modules/next/dist/client/router.d.ts","./node_modules/next/dist/client/route-loader.d.ts","./node_modules/next/dist/client/page-loader.d.ts","./node_modules/next/dist/shared/lib/bloom-filter.d.ts","./node_modules/next/dist/shared/lib/router/router.d.ts","./node_modules/next/dist/shared/lib/router-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/loadable-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/loadable.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/image-config-context.shared-runtime.d.ts","./node_modules/next/dist/client/components/readonly-url-search-params.d.ts","./node_modules/next/dist/shared/lib/hooks-client-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/head-manager-context.shared-runtime.d.ts","./node_modules/next/dist/client/flight-data-helpers.d.ts","./node_modules/next/dist/client/components/segment-cache/cache-key.d.ts","./node_modules/next/dist/client/components/router-reducer/fetch-server-response.d.ts","./node_modules/next/dist/client/components/segment-cache/types.d.ts","./node_modules/next/dist/shared/lib/segment-cache/segment-value-encoding.d.ts","./node_modules/next/dist/client/components/segment-cache/scheduler.d.ts","./node_modules/next/dist/client/components/segment-cache/cache-map.d.ts","./node_modules/next/dist/client/components/segment-cache/vary-path.d.ts","./node_modules/next/dist/client/components/segment-cache/cache.d.ts","./node_modules/next/dist/client/components/router-reducer/ppr-navigations.d.ts","./node_modules/next/dist/client/components/segment-cache/navigation.d.ts","./node_modules/next/dist/client/components/router-reducer/router-reducer-types.d.ts","./node_modules/next/dist/shared/lib/app-router-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/server-inserted-html.shared-runtime.d.ts","./node_modules/next/dist/server/route-modules/pages/vendored/contexts/entrypoints.d.ts","./node_modules/next/dist/server/route-modules/pages/module.compiled.d.ts","./node_modules/next/dist/build/templates/pages.d.ts","./node_modules/next/dist/server/route-modules/pages/module.d.ts","./node_modules/next/dist/server/render.d.ts","./node_modules/next/dist/build/webpack/plugins/pages-manifest-plugin.d.ts","./node_modules/next/dist/server/route-definitions/pages-api-route-definition.d.ts","./node_modules/next/dist/server/route-matches/pages-api-route-match.d.ts","./node_modules/next/dist/server/route-matchers/route-matcher.d.ts","./node_modules/next/dist/server/route-matcher-providers/route-matcher-provider.d.ts","./node_modules/next/dist/server/route-matcher-managers/route-matcher-manager.d.ts","./node_modules/next/dist/server/normalizers/normalizer.d.ts","./node_modules/next/dist/server/normalizers/locale-route-normalizer.d.ts","./node_modules/next/dist/server/normalizers/request/pathname-normalizer.d.ts","./node_modules/next/dist/server/normalizers/request/suffix.d.ts","./node_modules/next/dist/server/normalizers/request/rsc.d.ts","./node_modules/next/dist/server/normalizers/request/next-data.d.ts","./node_modules/next/dist/server/after/builtin-request-context.d.ts","./node_modules/next/dist/server/normalizers/request/segment-prefix-rsc.d.ts","./node_modules/next/dist/server/route-modules/pages/builtin/_error.d.ts","./node_modules/next/dist/server/load-default-error-components.d.ts","./node_modules/next/dist/server/base-server.d.ts","./node_modules/next/dist/server/after/after.d.ts","./node_modules/next/dist/server/after/after-context.d.ts","./node_modules/next/dist/server/use-cache/cache-life.d.ts","./node_modules/next/dist/server/app-render/work-async-storage-instance.d.ts","./node_modules/next/dist/server/lib/lazy-result.d.ts","./node_modules/next/dist/server/app-render/create-error-handler.d.ts","./node_modules/next/dist/shared/lib/action-revalidation-kind.d.ts","./node_modules/next/dist/server/app-render/work-async-storage.external.d.ts","./node_modules/next/dist/server/async-storage/work-store.d.ts","./node_modules/next/dist/server/web/http.d.ts","./node_modules/next/dist/client/components/hooks-server-context.d.ts","./node_modules/next/dist/server/route-modules/app-route/shared-modules.d.ts","./node_modules/next/dist/client/components/redirect-status-code.d.ts","./node_modules/next/dist/client/components/redirect-error.d.ts","./node_modules/next/dist/server/web/spec-extension/adapters/request-cookies.d.ts","./node_modules/next/dist/server/async-storage/draft-mode-provider.d.ts","./node_modules/next/dist/server/web/spec-extension/adapters/headers.d.ts","./node_modules/next/dist/server/app-render/cache-signal.d.ts","./node_modules/next/dist/server/app-render/instant-validation/boundary-tracking.d.ts","./node_modules/next/dist/server/app-render/instant-validation/instant-validation-error.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-relative-url.d.ts","./node_modules/next/dist/server/app-render/instant-validation/instant-samples.d.ts","./node_modules/next/dist/server/app-render/dynamic-rendering.d.ts","./node_modules/next/dist/server/app-render/work-unit-async-storage-instance.d.ts","./node_modules/next/dist/server/lib/implicit-tags.d.ts","./node_modules/next/dist/server/app-render/staged-rendering.d.ts","./node_modules/next/dist/server/app-render/work-unit-async-storage.external.d.ts","./node_modules/next/dist/build/templates/app-route.d.ts","./node_modules/next/dist/server/app-render/action-async-storage-instance.d.ts","./node_modules/next/dist/server/app-render/action-async-storage.external.d.ts","./node_modules/next/dist/server/route-modules/app-route/module.d.ts","./node_modules/next/dist/server/route-modules/app-route/module.compiled.d.ts","./node_modules/next/dist/build/segment-config/app/app-segments.d.ts","./node_modules/next/dist/build/get-supported-browsers.d.ts","./node_modules/next/dist/build/utils.d.ts","./node_modules/next/dist/build/rendering-mode.d.ts","./node_modules/next/dist/server/lib/router-utils/build-prefetch-segment-data-route.d.ts","./node_modules/next/dist/server/lib/cpu-profile.d.ts","./node_modules/next/dist/build/turborepo-access-trace/types.d.ts","./node_modules/next/dist/build/turborepo-access-trace/result.d.ts","./node_modules/next/dist/build/turborepo-access-trace/helpers.d.ts","./node_modules/next/dist/build/turborepo-access-trace/index.d.ts","./node_modules/next/dist/export/routes/types.d.ts","./node_modules/next/dist/export/types.d.ts","./node_modules/next/dist/export/worker.d.ts","./node_modules/next/dist/build/worker.d.ts","./node_modules/next/dist/build/index.d.ts","./node_modules/next/dist/lib/coalesced-function.d.ts","./node_modules/next/dist/server/lib/router-utils/types.d.ts","./node_modules/next/dist/trace/types.d.ts","./node_modules/next/dist/trace/trace.d.ts","./node_modules/next/dist/trace/shared.d.ts","./node_modules/next/dist/trace/index.d.ts","./node_modules/next/dist/build/load-jsconfig.d.ts","./node_modules/@next/env/dist/index.d.ts","./node_modules/next/dist/build/webpack/plugins/telemetry-plugin/use-cache-tracker-utils.d.ts","./node_modules/next/dist/build/webpack/plugins/telemetry-plugin/telemetry-plugin.d.ts","./node_modules/next/dist/telemetry/storage.d.ts","./node_modules/next/dist/build/build-context.d.ts","./node_modules/next/dist/build/webpack-config.d.ts","./node_modules/next/dist/build/swc/generated-native.d.ts","./node_modules/next/dist/build/define-env.d.ts","./node_modules/next/dist/build/swc/index.d.ts","./node_modules/next/dist/build/swc/types.d.ts","./node_modules/next/dist/server/dev/parse-version-info.d.ts","./node_modules/next/dist/next-devtools/shared/types.d.ts","./node_modules/next/dist/server/dev/dev-indicator-server-state.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/cache-indicator.d.ts","./node_modules/next/dist/server/lib/parse-stack.d.ts","./node_modules/next/dist/next-devtools/server/shared.d.ts","./node_modules/next/dist/next-devtools/shared/stack-frame.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/utils/get-error-by-type.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/container/runtime-error/render-error.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/shared.d.ts","./node_modules/next/dist/server/dev/debug-channel.d.ts","./node_modules/next/dist/server/dev/hot-reloader-types.d.ts","./node_modules/next/dist/server/web/spec-extension/fetch-event.d.ts","./node_modules/next/dist/server/web/spec-extension/response.d.ts","./node_modules/next/dist/build/segment-config/middleware/middleware-config.d.ts","./node_modules/next/dist/server/web/types.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-url.d.ts","./node_modules/next/dist/server/base-http/node.d.ts","./node_modules/next/dist/server/lib/async-callback-set.d.ts","./node_modules/next/dist/shared/lib/router/utils/route-regex.d.ts","./node_modules/next/dist/shared/lib/router/utils/route-matcher.d.ts","./node_modules/sharp/lib/index.d.ts","./node_modules/next/dist/server/image-optimizer.d.ts","./node_modules/next/dist/server/next-server.d.ts","./node_modules/next/dist/server/lib/types.d.ts","./node_modules/next/dist/server/lib/lru-cache.d.ts","./node_modules/next/dist/server/lib/dev-bundler-service.d.ts","./node_modules/next/dist/server/dev/static-paths-worker.d.ts","./node_modules/next/dist/server/dev/next-dev-server.d.ts","./node_modules/next/dist/server/next.d.ts","./node_modules/next/dist/server/lib/render-server.d.ts","./node_modules/next/dist/server/lib/router-server.d.ts","./node_modules/next/dist/shared/lib/router/utils/path-match.d.ts","./node_modules/next/dist/server/lib/router-utils/filesystem.d.ts","./node_modules/next/dist/server/lib/router-utils/setup-dev-bundler.d.ts","./node_modules/next/dist/server/lib/router-utils/router-server-context.d.ts","./node_modules/next/dist/server/route-modules/route-module.d.ts","./node_modules/next/dist/server/load-components.d.ts","./node_modules/next/dist/server/web/adapter.d.ts","./node_modules/next/dist/server/app-render/types.d.ts","./node_modules/next/dist/build/webpack/loaders/metadata/types.d.ts","./node_modules/next/dist/build/webpack/loaders/next-app-loader/index.d.ts","./node_modules/next/dist/server/lib/app-dir-module.d.ts","./node_modules/next/dist/server/app-render/app-render.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/contexts/entrypoints.d.ts","./node_modules/next/dist/client/components/error-boundary.d.ts","./node_modules/next/dist/client/components/layout-router.d.ts","./node_modules/next/dist/client/components/render-from-template-context.d.ts","./node_modules/next/dist/client/components/client-page.d.ts","./node_modules/next/dist/client/components/client-segment.d.ts","./node_modules/next/dist/client/components/http-access-fallback/error-boundary.d.ts","./node_modules/next/dist/lib/metadata/types/alternative-urls-types.d.ts","./node_modules/next/dist/lib/metadata/types/extra-types.d.ts","./node_modules/next/dist/lib/metadata/types/metadata-types.d.ts","./node_modules/next/dist/lib/metadata/types/manifest-types.d.ts","./node_modules/next/dist/lib/metadata/types/opengraph-types.d.ts","./node_modules/next/dist/lib/metadata/types/twitter-types.d.ts","./node_modules/next/dist/lib/metadata/types/metadata-interface.d.ts","./node_modules/next/dist/lib/metadata/types/resolvers.d.ts","./node_modules/next/dist/lib/metadata/types/icons.d.ts","./node_modules/next/dist/lib/metadata/resolve-metadata.d.ts","./node_modules/next/dist/lib/metadata/metadata.d.ts","./node_modules/next/dist/lib/framework/boundary-components.d.ts","./node_modules/next/dist/server/app-render/rsc/preloads.d.ts","./node_modules/next/dist/server/app-render/rsc/postpone.d.ts","./node_modules/next/dist/server/app-render/rsc/taint.d.ts","./node_modules/next/dist/server/app-render/collect-segment-data.d.ts","./node_modules/next/dist/server/app-render/instant-validation/instant-validation.d.ts","./node_modules/next/dist/next-devtools/userspace/app/segment-explorer-node.d.ts","./node_modules/next/dist/server/app-render/entry-base.d.ts","./node_modules/next/dist/build/templates/app-page.d.ts","./node_modules/next/dist/server/route-modules/app-page/helpers/prerender-manifest-matcher.d.ts","./node_modules/@types/react/jsx-dev-runtime.d.ts","./node_modules/@types/react/compiler-runtime.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/rsc/entrypoints.d.ts","./node_modules/@types/react-dom/client.d.ts","./node_modules/@types/react-dom/static.d.ts","./node_modules/@types/react-dom/server.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/ssr/entrypoints.d.ts","./node_modules/next/dist/server/route-modules/app-page/module.d.ts","./node_modules/next/dist/server/request/fallback-params.d.ts","./node_modules/next/dist/server/web/spec-extension/image-response.d.ts","./node_modules/next/dist/server/web/spec-extension/user-agent.d.ts","./node_modules/next/dist/server/web/spec-extension/url-pattern.d.ts","./node_modules/next/dist/server/after/index.d.ts","./node_modules/next/dist/server/request/connection.d.ts","./node_modules/next/dist/server/web/exports/index.d.ts","./node_modules/next/dist/server/request-meta.d.ts","./node_modules/next/dist/cli/next-test.d.ts","./node_modules/next/dist/shared/lib/size-limit.d.ts","./node_modules/next/dist/server/config-shared.d.ts","./node_modules/next/dist/server/base-http/index.d.ts","./node_modules/next/dist/server/api-utils/index.d.ts","./node_modules/next/dist/build/adapter/build-complete.d.ts","./node_modules/next/dist/types.d.ts","./node_modules/next/dist/shared/lib/html-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/utils.d.ts","./node_modules/next/dist/pages/_app.d.ts","./node_modules/next/app.d.ts","./node_modules/next/dist/server/web/spec-extension/unstable-cache.d.ts","./node_modules/next/dist/server/web/spec-extension/revalidate.d.ts","./node_modules/next/dist/server/web/spec-extension/unstable-no-store.d.ts","./node_modules/next/dist/server/use-cache/cache-tag.d.ts","./node_modules/next/cache.d.ts","./node_modules/next/dist/pages/_document.d.ts","./node_modules/next/document.d.ts","./node_modules/next/dist/shared/lib/dynamic.d.ts","./node_modules/next/dynamic.d.ts","./node_modules/next/dist/pages/_error.d.ts","./node_modules/next/dist/client/components/catch-error.d.ts","./node_modules/next/dist/api/error.d.ts","./node_modules/next/error.d.ts","./node_modules/next/dist/shared/lib/head.d.ts","./node_modules/next/head.d.ts","./node_modules/next/dist/server/request/cookies.d.ts","./node_modules/next/dist/server/request/headers.d.ts","./node_modules/next/dist/server/request/draft-mode.d.ts","./node_modules/next/headers.d.ts","./node_modules/next/dist/shared/lib/get-img-props.d.ts","./node_modules/next/dist/client/image-component.d.ts","./node_modules/next/dist/shared/lib/image-external.d.ts","./node_modules/next/image.d.ts","./node_modules/next/dist/client/link.d.ts","./node_modules/next/link.d.ts","./node_modules/next/dist/client/components/unrecognized-action-error.d.ts","./node_modules/next/dist/client/components/redirect.d.ts","./node_modules/next/dist/client/components/not-found.d.ts","./node_modules/next/dist/client/components/forbidden.d.ts","./node_modules/next/dist/client/components/unauthorized.d.ts","./node_modules/next/dist/client/components/unstable-rethrow.server.d.ts","./node_modules/next/dist/client/components/unstable-rethrow.d.ts","./node_modules/next/dist/client/components/navigation.react-server.d.ts","./node_modules/next/dist/client/components/navigation.d.ts","./node_modules/next/navigation.d.ts","./node_modules/next/router.d.ts","./node_modules/next/dist/client/script.d.ts","./node_modules/next/script.d.ts","./node_modules/next/dist/compiled/@edge-runtime/primitives/url.d.ts","./node_modules/next/dist/compiled/@vercel/og/satori/index.d.ts","./node_modules/next/dist/compiled/@vercel/og/types.d.ts","./node_modules/next/server.d.ts","./node_modules/next/types/global.d.ts","./node_modules/next/types/compiled.d.ts","./node_modules/next/types.d.ts","./node_modules/next/index.d.ts","./node_modules/next/image-types/global.d.ts","./.next/types/routes.d.ts","./next-env.d.ts","./next.config.ts","./node_modules/axios/index.d.ts","./node_modules/zustand/esm/vanilla.d.mts","./node_modules/zustand/esm/react.d.mts","./node_modules/zustand/esm/index.d.mts","./src/stores/authstore.ts","./src/lib/apiclient.ts","./node_modules/marked/lib/marked.d.ts","./node_modules/marked-highlight/src/index.d.ts","./node_modules/highlight.js/types/index.d.ts","./node_modules/highlight.js/es/common.d.ts","./src/lib/markdown.ts","./src/stores/settingsstore.ts","./src/stores/spacestore.ts","./src/app/layout.tsx","./src/app/page.tsx","./src/components/navbar.tsx","./src/app/admin/layout.tsx","./src/components/adminusermodal.tsx","./src/components/admingroupmodal.tsx","./src/components/confirmactionmodal.tsx","./src/components/adminspacemodal.tsx","./src/components/adminprovidermodal.tsx","./src/app/admin/page.tsx","./src/components/sidebar.tsx","./src/components/spacesettingsmodal.tsx","./src/app/dashboard/layout.tsx","./src/app/dashboard/page.tsx","./node_modules/orderedmap/dist/index.d.ts","./node_modules/prosemirror-model/dist/index.d.ts","./node_modules/prosemirror-transform/dist/index.d.ts","./node_modules/prosemirror-view/dist/index.d.ts","./node_modules/prosemirror-state/dist/index.d.ts","./node_modules/@tiptap/pm/dist/state/index.d.ts","./node_modules/@tiptap/pm/dist/model/index.d.ts","./node_modules/@tiptap/pm/dist/view/index.d.ts","./node_modules/@tiptap/pm/dist/transform/index.d.ts","./node_modules/@tiptap/core/dist/index.d.ts","./node_modules/@tiptap/react/dist/index.d.ts","./node_modules/@tiptap/extension-blockquote/dist/index.d.ts","./node_modules/@tiptap/extension-bold/dist/index.d.ts","./node_modules/@tiptap/extension-code/dist/index.d.ts","./node_modules/@tiptap/extension-code-block/dist/index.d.ts","./node_modules/@tiptap/extension-hard-break/dist/index.d.ts","./node_modules/@tiptap/extension-heading/dist/index.d.ts","./node_modules/@tiptap/extension-horizontal-rule/dist/index.d.ts","./node_modules/@tiptap/extension-italic/dist/index.d.ts","./node_modules/@tiptap/extension-link/dist/index.d.ts","./node_modules/@tiptap/extension-list/dist/index.d.ts","./node_modules/@tiptap/extension-paragraph/dist/index.d.ts","./node_modules/@tiptap/extension-strike/dist/index.d.ts","./node_modules/@tiptap/extension-underline/dist/index.d.ts","./node_modules/@tiptap/extensions/dist/index.d.ts","./node_modules/@tiptap/starter-kit/dist/index.d.ts","./node_modules/@tiptap/extension-task-list/dist/index.d.ts","./node_modules/@tiptap/extension-task-item/dist/index.d.ts","./node_modules/@tiptap/extension-table/dist/index.d.ts","./node_modules/@tiptap/extension-placeholder/dist/index.d.ts","./node_modules/@tiptap/suggestion/dist/index.d.ts","./node_modules/@tiptap/extension-mention/dist/index.d.ts","./node_modules/@popperjs/core/lib/enums.d.ts","./node_modules/@popperjs/core/lib/modifiers/popperoffsets.d.ts","./node_modules/@popperjs/core/lib/modifiers/flip.d.ts","./node_modules/@popperjs/core/lib/modifiers/hide.d.ts","./node_modules/@popperjs/core/lib/modifiers/offset.d.ts","./node_modules/@popperjs/core/lib/modifiers/eventlisteners.d.ts","./node_modules/@popperjs/core/lib/modifiers/computestyles.d.ts","./node_modules/@popperjs/core/lib/modifiers/arrow.d.ts","./node_modules/@popperjs/core/lib/modifiers/preventoverflow.d.ts","./node_modules/@popperjs/core/lib/modifiers/applystyles.d.ts","./node_modules/@popperjs/core/lib/types.d.ts","./node_modules/@popperjs/core/lib/modifiers/index.d.ts","./node_modules/@popperjs/core/lib/utils/detectoverflow.d.ts","./node_modules/@popperjs/core/lib/createpopper.d.ts","./node_modules/@popperjs/core/lib/popper-lite.d.ts","./node_modules/@popperjs/core/lib/popper.d.ts","./node_modules/@popperjs/core/lib/index.d.ts","./node_modules/@popperjs/core/index.d.ts","./node_modules/tippy.js/index.d.ts","./src/components/richtexteditor.tsx","./src/app/dashboard/spaces/[spaceid]/notes/[noteid]/notepageclient.tsx","./src/app/dashboard/spaces/[spaceid]/notes/[noteid]/page.tsx","./src/app/dashboard/spaces/[spaceid]/notes/new/newnotepageclient.tsx","./src/app/dashboard/spaces/[spaceid]/notes/new/page.tsx","./src/app/dashboard/spaces/[spaceid]/tasklists/[tasklistid]/tasklistpageclient.tsx","./src/app/dashboard/spaces/[spaceid]/tasklists/[tasklistid]/page.tsx","./src/app/dashboard/spaces/[spaceid]/tasklists/new/newtasklistpageclient.tsx","./src/app/dashboard/spaces/[spaceid]/tasklists/new/page.tsx","./src/app/login/page.tsx","./src/app/register/page.tsx","./.next/types/cache-life.d.ts","./.next/types/validator.ts","./node_modules/@types/trusted-types/lib/index.d.ts","./node_modules/@types/trusted-types/index.d.ts","./node_modules/@types/dompurify/index.d.ts","./node_modules/@types/use-sync-external-store/index.d.ts"],"fileIdsList":[[97,145,162,163,487,488,489,490],[97,145,162,163],[97,145,162,163,230,531,534,550,551,553,559,562,563,617,621,624,625],[97,145,162,163,532,533,534],[97,145,162,163,230,532],[97,145,162,163,612],[97,145,162,163,606,608],[97,145,162,163,596,606,607,609,610,611],[97,145,162,163,606],[97,145,162,163,596,606],[97,145,162,163,597,598,599,600,601,602,603,604,605],[97,145,162,163,597,601,602,605,606,609],[97,145,162,163,597,598,599,600,601,602,603,604,605,606,607,609,610],[97,145,162,163,596,597,598,599,600,601,602,603,604,605],[97,145,162,163,569,570,571,572,575,576,577,578,579,580,581,582,583,584,585,586,587,588,592],[97,145,162,163,573,575,576,577,578,579,580,581,582,583,584,585,586,587,588,592],[97,145,162,163,565,569,570,573,575,576,577,578,579,580,581,582,583,584,585,586,587,588,592],[97,145,162,163,570,573,575,576,577,578,579,580,581,582,583,584,585,586,587,588,592,594],[97,145,162,163,588],[97,145,162,163,570,571,573,575,576,577,578,579,580,581,582,583,584,585,586,587,588,592],[97,145,162,163,584],[97,145,162,163,569,570,573,575,576,577,578,579,580,581,582,583,584,585,586,587,588,592],[97,145,162,163,565],[97,145,162,163,568],[97,145,162,163,566],[97,145,162,163,567],[85,97,145,162,163,230,570,571,573,575,576,577,578,579,580,581,582,583,584,585,586,587,588,592],[97,145,162,163,569,570,571,573,575,576,577,578,579,580,581,582,583,584,585,586,587,588,592],[97,145,162,163,629],[97,142,143,145,162,163],[97,144,145,162,163],[145,162,163],[97,145,150,162,163,180],[97,145,146,151,156,162,163,165,177,188],[97,145,146,147,156,162,163,165],[92,93,94,97,145,162,163],[97,145,148,162,163,189],[97,145,149,150,157,162,163,166],[97,145,150,162,163,177,185],[97,145,151,153,156,162,163,165],[97,144,145,152,162,163],[97,145,153,154,162,163],[97,145,155,156,162,163],[97,144,145,156,162,163],[97,145,156,157,158,162,163,177,188],[97,145,156,157,158,162,163,172,177,180],[97,138,145,153,156,159,162,163,165,177,188],[97,145,156,157,159,160,162,163,165,177,185,188],[97,145,159,161,162,163,177,185,188],[95,96,97,98,99,100,101,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194],[97,145,156,162,163],[97,145,162,163,164,188],[97,145,153,156,162,163,165,177],[97,145,162,163,166],[97,145,162,163,167],[97,144,145,162,163,168],[97,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194],[97,145,162,163,170],[97,145,162,163,171],[97,145,156,162,163,172,173],[97,145,162,163,172,174,189,191],[97,145,157,162,163],[97,145,156,162,163,177,178,180],[97,145,162,163,179,180],[97,145,162,163,177,178],[97,145,162,163,180],[97,145,162,163,181],[97,142,145,162,163,177,182,188],[97,145,156,162,163,183,184],[97,145,162,163,183,184],[97,145,150,162,163,165,177,185],[97,145,162,163,186],[97,145,162,163,165,187],[97,145,159,162,163,171,188],[97,145,150,162,163,189],[97,145,162,163,177,190],[97,145,162,163,164,191],[97,145,162,163,192],[97,138,145,162,163],[97,138,145,156,158,162,163,168,177,180,188,190,191,193],[97,145,162,163,177,194],[85,89,97,145,162,163,196,197,198,200,482,527],[85,97,145,162,163],[85,89,97,145,162,163,196,197,198,199,463,482,527],[85,89,97,145,162,163,196,197,199,200,482,527],[85,97,145,162,163,200,463,464],[85,97,145,162,163,200,463],[85,89,97,145,162,163,197,198,199,200,482,527],[85,89,97,145,162,163,196,198,199,200,482,527],[83,84,97,145,162,163],[97,145,162,163,628],[97,145,162,163,545],[97,145,162,163,543],[97,145,162,163,485],[97,145,162,163,433,496,497],[97,145,162,163,205,206,208,220,244,359,370,478],[97,145,162,163,208,239,240,241,243,478],[97,145,162,163,208,376,378,380,381,383,478,480],[97,145,162,163,208,242,279,478],[97,145,162,163,206,208,219,220,226,232,237,358,359,360,369,478,480],[97,145,162,163,478],[97,145,162,163,215,221,240,260,355],[97,145,162,163,208],[97,145,162,163,201,215,221],[97,145,162,163,387],[97,145,162,163,384,385,387],[97,145,162,163,384,386,478],[97,145,159,162,163,260,457,475],[97,145,159,162,163,331,334,350,355,475],[97,145,159,162,163,303,475],[97,145,162,163,363],[97,145,162,163,362,363,364],[97,145,162,163,362],[91,97,145,159,162,163,201,208,220,226,232,238,240,244,245,258,259,326,356,357,370,478,482],[97,145,162,163,205,208,242,279,376,377,382,478,530],[97,145,162,163,242,530],[97,145,162,163,205,259,428,478,530],[97,145,162,163,530],[97,145,162,163,208,242,243,530],[97,145,162,163,379,530],[97,145,162,163,245,358,361,368],[85,97,145,162,163,433],[97,145,162,163,171,215,230],[97,145,162,163,215,230],[85,97,145,162,163,300],[85,97,145,162,163,230],[85,97,145,162,163,221,230,433],[97,145,162,163,215,286,300,301,512,519],[97,145,162,163,285,513,514,515,516,518],[97,145,162,163,336],[97,145,162,163,336,337],[97,145,162,163,219,221,288,289],[97,145,162,163,221,295,296],[97,145,162,163,221,290,298],[97,145,162,163,295],[97,145,162,163,213,221,288,289,290,291,292,293,294,295,298],[97,145,162,163,221,288,295,296,297,299],[97,145,162,163,221,289,291,292],[97,145,162,163,289,291,294,296],[97,145,162,163,517],[97,145,162,163,221],[85,97,145,162,163,209,506],[85,97,145,162,163,188],[85,97,145,162,163,242,277],[85,97,145,162,163,242,370],[97,145,162,163,275,280],[85,97,145,162,163,276,484],[85,89,97,145,159,162,163,196,197,198,199,200,482,526],[97,145,159,162,163,221],[97,145,159,162,163,220,225,306,323,365,366,370,425,427,478,479],[97,145,162,163,258,367],[97,145,162,163,482],[97,145,162,163,207],[85,97,145,162,163,212,215,430,446,448],[97,145,162,163,171,215,430,445,446,447,529],[97,145,162,163,439,440,441,442,443,444],[97,145,162,163,441],[97,145,162,163,445],[97,145,162,163,230,394,395,397],[85,97,145,162,163,221,388,389,390,391,396],[97,145,162,163,394,396],[97,145,162,163,392],[97,145,162,163,393],[85,97,145,162,163,230,276,484],[85,97,145,162,163,230,483,484],[85,97,145,162,163,230,484],[97,145,162,163,323,324],[97,145,162,163,324],[97,145,159,162,163,479,484],[97,145,162,163,353],[97,144,145,162,163,352],[97,145,162,163,215,221,227,229,331,344,348,350,427,430,467,468,475,479],[97,145,162,163,221,270,292],[97,145,162,163,331,342,345,350],[85,97,145,162,163,212,215,331,334,350,353,387,434,435,436,437,438,449,450,451,452,453,454,455,456,530],[97,145,162,163,212,215,240,331,338,339,340,343,344],[97,145,162,163,177,221,240,342,349,430,431,475],[97,145,162,163,346],[97,145,159,162,163,171,209,221,225,235,267,268,271,323,326,391,425,426,467,478,479,480,482,530],[97,145,162,163,212,213,215],[97,145,162,163,331],[97,144,145,162,163,240,267,268,325,326,327,328,329,330,479],[97,145,162,163,350],[97,144,145,162,163,214,215,225,229,265,331,338,339,340,341,342,345,346,347,348,349,468],[97,145,159,162,163,265,266,338,479,480],[97,145,162,163,240,268,323,326,331,427,479],[97,145,159,162,163,478,480],[97,145,159,162,163,177,475,479,480],[97,145,159,162,163,171,201,215,220,227,229,232,235,242,262,267,268,269,270,271,306,307,309,312,314,317,318,319,320,322,370,425,427,475,478,479,480],[97,145,159,162,163,177],[97,145,162,163,208,209,210,238,475,476,477,482,484,530],[97,145,162,163,205,206,478],[97,145,162,163,399],[97,145,159,162,163,177,188,217,383,387,388,389,390,391,397,398,530],[97,145,162,163,171,188,201,215,217,229,232,268,307,312,322,323,376,403,404,405,411,414,415,425,427,475,478],[97,145,162,163,232,238,245,258,268,326,478],[97,145,159,162,163,188,209,220,229,268,409,475,478],[97,145,162,163,429],[97,145,159,162,163,399,412,413,422],[97,145,162,163,475,478],[97,145,162,163,328,468],[97,145,162,163,229,267,370,484],[97,145,159,162,163,171,207,312,372,376,405,411,414,417,475],[97,145,159,162,163,245,258,376,418],[97,145,162,163,208,269,370,420,478,480],[97,145,159,162,163,188,391,478],[97,145,159,162,163,242,269,370,371,372,381,399,419,421,478],[91,97,145,159,162,163,267,424,482,484],[97,145,162,163,321,425],[97,145,159,162,163,171,215,218,220,221,227,229,235,244,245,258,268,271,307,309,319,322,323,370,403,404,405,406,408,410,425,427,475,484],[97,145,159,162,163,177,245,411,416,422,475],[97,145,162,163,248,249,250,251,252,253,254,255,256,257],[97,145,162,163,262,313],[97,145,162,163,315],[97,145,162,163,313],[97,145,162,163,315,316],[97,145,159,162,163,219,220,221,225,226,479],[97,145,159,162,163,171,207,209,227,231,267,270,271,305,425,475,480,482,484],[97,145,159,162,163,171,188,211,218,219,229,231,268,423,468,474,479],[97,145,162,163,338],[97,145,162,163,339],[97,145,162,163,221,232,467],[97,145,162,163,340],[97,145,162,163,214],[97,145,162,163,216,228],[97,145,159,162,163,216,220,227],[97,145,162,163,223,228],[97,145,162,163,224],[97,145,162,163,216,217],[97,145,162,163,216,272],[97,145,162,163,216],[97,145,162,163,218,262,311],[97,145,162,163,310],[97,145,162,163,215,217,218],[97,145,162,163,218,308],[97,145,162,163,215,217],[97,145,162,163,267,370],[97,145,162,163,467],[97,145,159,162,163,188,227,229,233,267,370,424,427,430,431,432,458,459,462,466,468,475,479],[97,145,162,163,281,284,286,287,300,301],[85,97,145,162,163,198,200,230,460,461],[85,97,145,162,163,198,200,230,460,461,465],[97,145,162,163,354],[97,145,162,163,240,261,266,267,331,332,333,334,335,337,350,351,353,356,424,427,478,480],[97,145,162,163,300],[97,145,159,162,163,305,475],[97,145,162,163,305],[97,145,159,162,163,227,273,302,304,306,424,475,482,484],[97,145,162,163,281,282,283,284,286,287,300,301,483],[91,97,145,159,162,163,171,188,216,217,229,235,267,268,271,370,422,423,425,475,478,479,482],[97,145,162,163,212,215,222],[97,145,162,163,266,268,400,403],[97,145,162,163,266,401,469,470,471,472,473],[97,145,159,162,163,262,478],[97,145,159,162,163],[97,145,162,163,265,350],[97,145,162,163,264],[97,145,162,163,266,319],[97,145,162,163,263,265,478],[97,145,159,162,163,211,266,400,401,402,475,478,479],[85,97,145,162,163,215,221,299],[85,97,145,162,163,213],[97,145,162,163,203,204],[85,97,145,162,163,209],[85,97,145,162,163,215,285],[85,91,97,145,162,163,267,271,482,484],[97,145,162,163,209,506,507],[85,97,145,162,163,280],[85,97,145,162,163,171,188,207,274,276,278,279,484],[97,145,162,163,215,242,479],[97,145,162,163,215,407],[85,97,145,157,159,162,163,171,205,207,280,378,482,483],[85,97,145,162,163,196,197,198,199,200,482,527],[85,86,87,88,89,97,145,162,163],[97,145,150,162,163],[97,145,162,163,373,374,375],[97,145,162,163,373],[85,89,97,145,159,161,162,163,171,195,196,197,198,199,200,201,207,235,240,417,445,480,481,484,527],[97,145,162,163,492],[97,145,162,163,494],[97,145,162,163,498],[97,145,162,163,500],[97,145,162,163,502,503,504],[97,145,162,163,508],[90,97,145,162,163,486,491,493,495,499,501,505,509,511,521,522,524,528,529,530,531],[97,145,162,163,510],[97,145,162,163,520],[97,145,162,163,276],[97,145,162,163,523],[97,144,145,162,163,266,400,401,403,469,470,472,473,525,527],[97,145,162,163,195],[97,145,162,163,564],[97,145,162,163,565,566,567],[97,145,162,163,565,566,568],[97,145,162,163,177,195],[97,145,162,163,613],[97,110,114,145,162,163,188],[97,110,145,162,163,177,188],[97,105,145,162,163],[97,107,110,145,162,163,185,188],[97,145,162,163,165,185],[97,105,145,162,163,195],[97,107,110,145,162,163,165,188],[97,102,103,106,109,145,156,162,163,177,188],[97,110,117,145,162,163],[97,102,108,145,162,163],[97,110,131,132,145,162,163],[97,106,110,145,162,163,180,188,195],[97,131,145,162,163,195],[97,104,105,145,162,163,195],[97,110,145,162,163],[97,104,105,106,107,108,109,110,111,112,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,132,133,134,135,136,137,145,162,163],[97,110,125,145,162,163],[97,110,117,118,145,162,163],[97,108,110,118,119,145,162,163],[97,109,145,162,163],[97,102,105,110,145,162,163],[97,110,114,118,119,145,162,163],[97,114,145,162,163],[97,108,110,113,145,162,163,188],[97,102,107,110,117,145,162,163],[97,145,162,163,177],[97,105,110,131,145,162,163,193,195],[97,145,162,163,538,539],[97,145,162,163,538],[85,97,145,162,163,230,521,541,552],[85,97,145,162,163,230,521,542,554,555,556,557,558],[85,97,145,162,163,230,521,541,542,549,552,560,561],[85,97,145,162,163,230,521,541,542,549],[85,97,145,162,163,230,521,541,542,549,615],[97,145,162,163,230,616],[97,145,162,163,230,618],[97,145,162,163,230,620],[97,145,162,163,230,622],[97,145,162,163,230,529,532],[85,97,145,162,163,230,511,521,541,542,548],[85,97,145,162,163,230,521,541],[85,97,145,162,163,230,511,521,541,548],[85,97,145,162,163,230,542,556],[97,145,162,163,230],[85,97,145,162,163,230,521,541,549],[85,97,145,162,163,230,529,574,583,589,590,591,592,593,594,595,614],[85,97,145,162,163,230,541,542,549],[97,145,162,163,230,537,541],[97,145,162,163,230,543,544,546],[97,145,162,163,230,540,542]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"27bdc30a0e32783366a5abeda841bc22757c1797de8681bbe81fbc735eeb1c10","impliedFormat":1},{"version":"8fd575e12870e9944c7e1d62e1f5a73fcf23dd8d3a321f2a2c74c20d022283fe","impliedFormat":1},{"version":"2ab096661c711e4a81cc464fa1e6feb929a54f5340b46b0a07ac6bbf857471f0","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2e80ee7a49e8ac312cc11b77f1475804bee36b3b2bc896bead8b6e1266befb43","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"df83c2a6c73228b625b0beb6669c7ee2a09c914637e2d35170723ad49c0f5cd4","affectsGlobalScope":true,"impliedFormat":1},{"version":"436aaf437562f276ec2ddbee2f2cdedac7664c1e4c1d2c36839ddd582eeb3d0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e3c06ea092138bf9fa5e874a1fdbc9d54805d074bee1de31b99a11e2fec239d","affectsGlobalScope":true,"impliedFormat":1},{"version":"87dc0f382502f5bbce5129bdc0aea21e19a3abbc19259e0b43ae038a9fc4e326","affectsGlobalScope":true,"impliedFormat":1},{"version":"b1cb28af0c891c8c96b2d6b7be76bd394fddcfdb4709a20ba05a7c1605eea0f9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2fef54945a13095fdb9b84f705f2b5994597640c46afeb2ce78352fab4cb3279","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac77cb3e8c6d3565793eb90a8373ee8033146315a3dbead3bde8db5eaf5e5ec6","affectsGlobalScope":true,"impliedFormat":1},{"version":"56e4ed5aab5f5920980066a9409bfaf53e6d21d3f8d020c17e4de584d29600ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ece9f17b3866cc077099c73f4983bddbcb1dc7ddb943227f1ec070f529dedd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a6282c8827e4b9a95f4bf4f5c205673ada31b982f50572d27103df8ceb8013c","affectsGlobalScope":true,"impliedFormat":1},{"version":"1c9319a09485199c1f7b0498f2988d6d2249793ef67edda49d1e584746be9032","affectsGlobalScope":true,"impliedFormat":1},{"version":"e3a2a0cee0f03ffdde24d89660eba2685bfbdeae955a6c67e8c4c9fd28928eeb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811c71eee4aa0ac5f7adf713323a5c41b0cf6c4e17367a34fbce379e12bbf0a4","affectsGlobalScope":true,"impliedFormat":1},{"version":"51ad4c928303041605b4d7ae32e0c1ee387d43a24cd6f1ebf4a2699e1076d4fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"60037901da1a425516449b9a20073aa03386cce92f7a1fd902d7602be3a7c2e9","affectsGlobalScope":true,"impliedFormat":1},{"version":"d4b1d2c51d058fc21ec2629fff7a76249dec2e36e12960ea056e3ef89174080f","affectsGlobalScope":true,"impliedFormat":1},{"version":"22adec94ef7047a6c9d1af3cb96be87a335908bf9ef386ae9fd50eeb37f44c47","affectsGlobalScope":true,"impliedFormat":1},{"version":"196cb558a13d4533a5163286f30b0509ce0210e4b316c56c38d4c0fd2fb38405","affectsGlobalScope":true,"impliedFormat":1},{"version":"73f78680d4c08509933daf80947902f6ff41b6230f94dd002ae372620adb0f60","affectsGlobalScope":true,"impliedFormat":1},{"version":"c5239f5c01bcfa9cd32f37c496cf19c61d69d37e48be9de612b541aac915805b","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"7e29f41b158de217f94cb9676bf9cbd0cd9b5a46e1985141ed36e075c52bf6ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac51dd7d31333793807a6abaa5ae168512b6131bd41d9c5b98477fc3b7800f9f","impliedFormat":1},{"version":"bd7dee3446a5b94651d58000ddfda40296f073e9372891f65003a524b4620697","impliedFormat":1},{"version":"acd8fd5090ac73902278889c38336ff3f48af6ba03aa665eb34a75e7ba1dccc4","impliedFormat":1},{"version":"d6258883868fb2680d2ca96bc8b1352cab69874581493e6d52680c5ffecdb6cc","impliedFormat":1},{"version":"1b61d259de5350f8b1e5db06290d31eaebebc6baafd5f79d314b5af9256d7153","impliedFormat":1},{"version":"f258e3960f324a956fc76a3d3d9e964fff2244ff5859dcc6ce5951e5413ca826","impliedFormat":1},{"version":"643f7232d07bf75e15bd8f658f664d6183a0efaca5eb84b48201c7671a266979","impliedFormat":1},{"version":"21da358700a3893281ce0c517a7a30cbd46be020d9f0c3f2834d0a8ad1f5fc75","impliedFormat":1},{"version":"6c7176368037af28cb72f2392010fa1cef295d6d6744bca8cfb54985f3a18c3e","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"437e20f2ba32abaeb7985e0afe0002de1917bc74e949ba585e49feba65da6ca1","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"98cffbf06d6bab333473c70a893770dbe990783904002c4f1a960447b4b53dca","affectsGlobalScope":true,"impliedFormat":1},{"version":"3af97acf03cc97de58a3a4bc91f8f616408099bc4233f6d0852e72a8ffb91ac9","affectsGlobalScope":true,"impliedFormat":1},{"version":"808069bba06b6768b62fd22429b53362e7af342da4a236ed2d2e1c89fcca3b4a","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"b52476feb4a0cbcb25e5931b930fc73cb6643fb1a5060bf8a3dda0eeae5b4b68","affectsGlobalScope":true,"impliedFormat":1},{"version":"f9501cc13ce624c72b61f12b3963e84fad210fbdf0ffbc4590e08460a3f04eba","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0fa06ada475b910e2106c98c68b10483dc8811d0c14a8a8dd36efb2672485b29","impliedFormat":1},{"version":"33e5e9aba62c3193d10d1d33ae1fa75c46a1171cf76fef750777377d53b0303f","impliedFormat":1},{"version":"2b06b93fd01bcd49d1a6bd1f9b65ddcae6480b9a86e9061634d6f8e354c1468f","impliedFormat":1},{"version":"6a0cd27e5dc2cfbe039e731cf879d12b0e2dded06d1b1dedad07f7712de0d7f4","affectsGlobalScope":true,"impliedFormat":1},{"version":"13f5c844119c43e51ce777c509267f14d6aaf31eafb2c2b002ca35584cd13b29","impliedFormat":1},{"version":"e60477649d6ad21542bd2dc7e3d9ff6853d0797ba9f689ba2f6653818999c264","impliedFormat":1},{"version":"c2510f124c0293ab80b1777c44d80f812b75612f297b9857406468c0f4dafe29","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"4c829ab315f57c5442c6667b53769975acbf92003a66aef19bce151987675bd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"b2ade7657e2db96d18315694789eff2ddd3d8aea7215b181f8a0b303277cc579","impliedFormat":1},{"version":"9855e02d837744303391e5623a531734443a5f8e6e8755e018c41d63ad797db2","impliedFormat":1},{"version":"4d631b81fa2f07a0e63a9a143d6a82c25c5f051298651a9b69176ba28930756d","impliedFormat":1},{"version":"836a356aae992ff3c28a0212e3eabcb76dd4b0cc06bcb9607aeef560661b860d","impliedFormat":1},{"version":"1e0d1f8b0adfa0b0330e028c7941b5a98c08b600efe7f14d2d2a00854fb2f393","impliedFormat":1},{"version":"41670ee38943d9cbb4924e436f56fc19ee94232bc96108562de1a734af20dc2c","affectsGlobalScope":true,"impliedFormat":1},{"version":"c906fb15bd2aabc9ed1e3f44eb6a8661199d6c320b3aa196b826121552cb3695","impliedFormat":1},{"version":"22295e8103f1d6d8ea4b5d6211e43421fe4564e34d0dd8e09e520e452d89e659","impliedFormat":1},{"version":"58647d85d0f722a1ce9de50955df60a7489f0593bf1a7015521efe901c06d770","impliedFormat":1},{"version":"73b5fa37db36eeac90c4d752e39586f1b57187400c4f5280fd05f16437287a45","impliedFormat":1},{"version":"a10f0e1854f3316d7ee437b79649e5a6ae3ae14ffe6322b02d4987071a95362e","impliedFormat":1},{"version":"e208f73ef6a980104304b0d2ca5f6bf1b85de6009d2c7e404028b875020fa8f2","impliedFormat":1},{"version":"d163b6bc2372b4f07260747cbc6c0a6405ab3fbcea3852305e98ac43ca59f5bc","impliedFormat":1},{"version":"e6fa9ad47c5f71ff733744a029d1dc472c618de53804eae08ffc243b936f87ff","affectsGlobalScope":true,"impliedFormat":1},{"version":"a6f137d651076822d4fe884287e68fd61785a0d3d1fdb250a5059b691fa897db","impliedFormat":1},{"version":"24826ed94a78d5c64bd857570fdbd96229ad41b5cb654c08d75a9845e3ab7dde","impliedFormat":1},{"version":"8b479a130ccb62e98f11f136d3ac80f2984fdc07616516d29881f3061f2dd472","impliedFormat":1},{"version":"928af3d90454bf656a52a48679f199f64c1435247d6189d1caf4c68f2eaf921f","affectsGlobalScope":true,"impliedFormat":1},{"version":"bceb58df66ab8fb00170df20cd813978c5ab84be1d285710c4eb005d8e9d8efb","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f16a7e4deafa527ed9995a772bb380eb7d3c2c0fd4ae178c5263ed18394db2c","impliedFormat":1},{"version":"933921f0bb0ec12ef45d1062a1fc0f27635318f4d294e4d99de9a5493e618ca2","impliedFormat":1},{"version":"71a0f3ad612c123b57239a7749770017ecfe6b66411488000aba83e4546fde25","impliedFormat":1},{"version":"77fbe5eecb6fac4b6242bbf6eebfc43e98ce5ccba8fa44e0ef6a95c945ff4d98","impliedFormat":1},{"version":"4f9d8ca0c417b67b69eeb54c7ca1bedd7b56034bb9bfd27c5d4f3bc4692daca7","impliedFormat":1},{"version":"814118df420c4e38fe5ae1b9a3bafb6e9c2aa40838e528cde908381867be6466","impliedFormat":1},{"version":"a3fc63c0d7b031693f665f5494412ba4b551fe644ededccc0ab5922401079c95","impliedFormat":1},{"version":"80523c00b8544a2000ae0143e4a90a00b47f99823eb7926c1e03c494216fc363","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"746911b62b329587939560deb5c036aca48aece03147b021fa680223255d5183","affectsGlobalScope":true,"impliedFormat":1},{"version":"18fd40412d102c5564136f29735e5d1c3b455b8a37f920da79561f1fde068208","impliedFormat":1},{"version":"c8d3e5a18ba35629954e48c4cc8f11dc88224650067a172685c736b27a34a4dc","impliedFormat":1},{"version":"f0be1b8078cd549d91f37c30c222c2a187ac1cf981d994fb476a1adc61387b14","affectsGlobalScope":true,"impliedFormat":1},{"version":"0aaed1d72199b01234152f7a60046bc947f1f37d78d182e9ae09c4289e06a592","impliedFormat":1},{"version":"2b55d426ff2b9087485e52ac4bc7cfafe1dc420fc76dad926cd46526567c501a","impliedFormat":1},{"version":"66ba1b2c3e3a3644a1011cd530fb444a96b1b2dfe2f5e837a002d41a1a799e60","impliedFormat":1},{"version":"7e514f5b852fdbc166b539fdd1f4e9114f29911592a5eb10a94bb3a13ccac3c4","impliedFormat":1},{"version":"5b7aa3c4c1a5d81b411e8cb302b45507fea9358d3569196b27eb1a27ae3a90ef","affectsGlobalScope":true,"impliedFormat":1},{"version":"5987a903da92c7462e0b35704ce7da94d7fdc4b89a984871c0e2b87a8aae9e69","affectsGlobalScope":true,"impliedFormat":1},{"version":"ea08a0345023ade2b47fbff5a76d0d0ed8bff10bc9d22b83f40858a8e941501c","impliedFormat":1},{"version":"47613031a5a31510831304405af561b0ffaedb734437c595256bb61a90f9311b","impliedFormat":1},{"version":"ae062ce7d9510060c5d7e7952ae379224fb3f8f2dd74e88959878af2057c143b","impliedFormat":1},{"version":"8a1a0d0a4a06a8d278947fcb66bf684f117bf147f89b06e50662d79a53be3e9f","affectsGlobalScope":true,"impliedFormat":1},{"version":"358765d5ea8afd285d4fd1532e78b88273f18cb3f87403a9b16fef61ac9fdcfe","impliedFormat":1},{"version":"9f55299850d4f0921e79b6bf344b47c420ce0f507b9dcf593e532b09ea7eeea1","impliedFormat":1},{"version":"2beff543f6e9a9701df88daeee3cdd70a34b4a1c11cb4c734472195a5cb2af54","impliedFormat":1},{"version":"2e07abf27aa06353d46f4448c0bbac73431f6065eef7113128a5cd804d0c384d","impliedFormat":1},{"version":"be1cc4d94ea60cbe567bc29ed479d42587bf1e6cba490f123d329976b0fe4ee5","impliedFormat":1},{"version":"42bc0e1a903408137c3df2b06dfd7e402cdab5bbfa5fcfb871b22ebfdb30bd0b","impliedFormat":1},{"version":"9894dafe342b976d251aac58e616ac6df8db91fb9d98934ff9dd103e9e82578f","impliedFormat":1},{"version":"413df52d4ea14472c2fa5bee62f7a40abd1eb49be0b9722ee01ee4e52e63beb2","impliedFormat":1},{"version":"db6d2d9daad8a6d83f281af12ce4355a20b9a3e71b82b9f57cddcca0a8964a96","impliedFormat":1},{"version":"446a50749b24d14deac6f8843e057a6355dd6437d1fac4f9e5ce4a5071f34bff","impliedFormat":1},{"version":"182e9fcbe08ac7c012e0a6e2b5798b4352470be29a64fdc114d23c2bab7d5106","impliedFormat":1},{"version":"2f4e6b4d39426a1b85ecf4bdeb9dddbf4d9b3397d95d8555d46f925c9519ec7d","impliedFormat":1},{"version":"78a2869ad0cbf3f9045dda08c0d4562b7e1b2bfe07b19e0db072f5c3c56e9584","impliedFormat":1},{"version":"89d5d28d4f57e000b836ac273079be1b75710e28ce14750d081fb420d37e2ca5","impliedFormat":1},{"version":"fd4e24ccff3966390600d7f5d6aa1fed5a512e92ada735ea5fbc933d313ad3d3","impliedFormat":1},{"version":"b7cddfe1aa6b86b5fad3c9ccb30d05b3ccb165aebbf112f48d2d8a5f69dd98b1","impliedFormat":1},{"version":"a86f82d646a739041d6702101afa82dcb935c416dd93cbca7fd754fd0282ce1f","impliedFormat":1},{"version":"ad0d1d75d129b1c80f911be438d6b61bfa8703930a8ff2be2f0e1f8a91841c64","impliedFormat":1},{"version":"bd2c7ada3dee03653d3f601011d30072194bc3970cd93208f9588fbdc0c69347","impliedFormat":1},{"version":"e480da45d32313e7174b265674da504f075f59ef326852f0c5a5d863b438ae85","impliedFormat":1},{"version":"ad54850f61fcf5d014e11be80d2f46fea9265cfa7e77456da876f7833ef81769","impliedFormat":1},{"version":"6f7c9e8bd2b5b6a080b07080065f94900bd3c7e5ebbd3047bc33fcce2fab1dd8","impliedFormat":1},{"version":"3e7efde639c6a6c3edb9847b3f61e308bf7a69685b92f665048c45132f51c218","impliedFormat":1},{"version":"df45ca1176e6ac211eae7ddf51336dc075c5314bc5c253651bae639defd5eec5","impliedFormat":1},{"version":"8a0e762ceb20c7e72504feef83d709468a70af4abccb304f32d6b9bac1129b2c","impliedFormat":1},{"version":"da5950ee2a90721df6f3fba45f5d05308f7e4c35835392215dd2cd404505e2de","impliedFormat":1},{"version":"ce75b1aebb33d510ff28af960a9221410a3eaf7f18fc5f21f9404075fba77256","impliedFormat":1},{"version":"f42d5fed19610d485c646a0c430e768115567d078c7fc855c57b0c578b3d6cd3","impliedFormat":1},{"version":"ee8df1cb8d0faaca4013a1b442e99130769ce06f438d18d510fed95890067563","impliedFormat":1},{"version":"d5630f2ad9b4541e5ce891648121022f9412ecdca1820baa1f0104f70fd7eff7","impliedFormat":1},{"version":"4d15375ab13497104bc8fe56fdef2b5fd6853f29255737d23a33fa306ff7fd69","impliedFormat":1},{"version":"2cd3fc1d0d6a1e85baffd2d4f50f5efb192b5446eef567e97c94765402f0aad4","impliedFormat":1},{"version":"e4cbf2f1e89ecccaddd2c045e600ae41b732295953fb06247c7dcbc2d281ed30","impliedFormat":1},{"version":"6dcedaef57dff0d79a05ab0ab602cde74db803d1e765468bf91263786a383e1b","impliedFormat":1},{"version":"8c1697d90c394a6fd955b98eae01238eff628e129b987a68aea10f898a48e7da","impliedFormat":1},{"version":"7580e62139cb2b44a0270c8d01abcbfcba2819a02514a527342447fa69b34ef1","impliedFormat":1},{"version":"42c169fb8c2d42f4f668c624a9a11e719d5d07dacbebb63cbcf7ef365b0a75b3","impliedFormat":1},{"version":"f374cb24e93e7798c4d9e83ff872fa52d2cdb36306392b840a6ddf46cb925cb6","impliedFormat":1},{"version":"d10d63718e1646c2279e3b33831f82c60e31f622b2b7020f1196409ca4c09242","impliedFormat":1},{"version":"106c6025f1d99fd468fd8bf6e5bda724e11e5905a4076c5d29790b6c3745e50c","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"148679c6d0f449210a96e7d2e562d589e56fcde87f843a92808b3ff103f1a774","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"02436d7e9ead85e09a2f8e27d5f47d9464bced31738dec138ca735390815c9f0","impliedFormat":1},{"version":"f8d5ff8eafd37499f2b6a98659dd9b45a321de186b8db6b6142faed0fea3de77","impliedFormat":1},{"version":"c86fe861cf1b4c46a0fb7d74dffe596cf679a2e5e8b1456881313170f092e3fa","impliedFormat":1},{"version":"a22dd55aa4d39906252000ab8e8a1b83b195eef7f4274eb51e457c1f11cf6580","impliedFormat":1},{"version":"540cc83ab772a2c6bc509fe1354f314825b5dba3669efdfbe4693ecd3048e34f","impliedFormat":1},{"version":"121b0696021ab885c570bbeb331be8ad82c6efe2f3b93a6e63874901bebc13e3","impliedFormat":1},{"version":"612d9da66bb046a9c1e2e8d026245ded881fc4b9f98cbfae714415d57ee0ae0b","impliedFormat":1},{"version":"32c2ad9494dad5d11b0564a619fee18f388db6c1e9e2cd3c360b3122549691eb","impliedFormat":1},{"version":"6c301d40aec56a74ec7bd7324e31a728dadf9bfba3e96def02938d3d973534ec","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"aa14cee20aa0db79f8df101fc027d929aec10feb5b8a8da3b9af3895d05b7ba2","impliedFormat":1},{"version":"493c700ac3bd317177b2eb913805c87fe60d4e8af4fb39c41f04ba81fae7e170","impliedFormat":1},{"version":"aeb554d876c6b8c818da2e118d8b11e1e559adbe6bf606cc9a611c1b6c09f670","impliedFormat":1},{"version":"acf5a2ac47b59ca07afa9abbd2b31d001bf7448b041927befae2ea5b1951d9f9","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"d71291eff1e19d8762a908ba947e891af44749f3a2cbc5bd2ec4b72f72ea795f","impliedFormat":1},{"version":"c0480e03db4b816dff2682b347c95f2177699525c54e7e6f6aa8ded890b76be7","impliedFormat":1},{"version":"25a5f6fd3a2243c859eddc99ab5fba11d970af2fe7a5df9c32b7668f76f97b01","impliedFormat":1},{"version":"8d207e1f9d2c30d6f77dfa693f3827c3fbf0d89240297e10bdfe1041d433df68","impliedFormat":1},{"version":"b620391fe8060cf9bedc176a4d01366e6574d7a71e0ac0ab344a4e76576fcbb8","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"2652448ac55a2010a1f71dd141f828b682298d39728f9871e1cdf8696ef443fd","impliedFormat":1},{"version":"d682336018141807fb602709e2d95a192828fcb8d5ba06dda3833a8ea98f69e3","impliedFormat":1},{"version":"6124e973eab8c52cabf3c07575204efc1784aca6b0a30c79eb85fe240a857efa","impliedFormat":1},{"version":"0d891735a21edc75df51f3eb995e18149e119d1ce22fd40db2b260c5960b914e","impliedFormat":1},{"version":"3b414b99a73171e1c4b7b7714e26b87d6c5cb03d200352da5342ab4088a54c85","impliedFormat":1},{"version":"4fbd3116e00ed3a6410499924b6403cc9367fdca303e34838129b328058ede40","impliedFormat":1},{"version":"9c82171d836c47486074e4ca8e059735bf97b205e70b196535b5efd40cbe1bc5","impliedFormat":1},{"version":"8c70ddc0c22d85e56011d49fddfaae3405eb53d47b59327b9dd589e82df672e7","impliedFormat":1},{"version":"2f9c89cbb29d362290531b48880a4024f258c6033aaeb7e59fbc62db26819650","impliedFormat":1},{"version":"a365c4d3bed3be4e4e20793c999c51f5cd7e6792322f14650949d827fbcd170f","impliedFormat":1},{"version":"c5426dbfc1cf90532f66965a7aa8c1136a78d4d0f96d8180ecbfc11d7722f1a5","impliedFormat":1},{"version":"65a15fc47900787c0bd18b603afb98d33ede930bed1798fc984d5ebb78b26cf9","impliedFormat":1},{"version":"9d202701f6e0744adb6314d03d2eb8fc994798fc83d91b691b75b07626a69801","impliedFormat":1},{"version":"de9d2df7663e64e3a91bf495f315a7577e23ba088f2949d5ce9ec96f44fba37d","impliedFormat":1},{"version":"c7af78a2ea7cb1cd009cfb5bdb48cd0b03dad3b54f6da7aab615c2e9e9d570c5","impliedFormat":1},{"version":"1ee45496b5f8bdee6f7abc233355898e5bf9bd51255db65f5ff7ede617ca0027","impliedFormat":1},{"version":"273782b8454e78f6a8b30d2cfbf6860499c930595095fcc1689637115f0eddda","affectsGlobalScope":true,"impliedFormat":1},{"version":"3fbdd025f9d4d820414417eeb4107ffa0078d454a033b506e22d3a23bc3d9c41","affectsGlobalScope":true,"impliedFormat":1},{"version":"dba114fb6a32b355a9cfc26ca2276834d72fe0e94cd2c3494005547025015369","impliedFormat":1},{"version":"a8f8e6ab2fa07b45251f403548b78eaf2022f3c2254df3dc186cb2671fe4996d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa6c12a7c0f6b84d512f200690bfc74819e99efae69e4c95c4cd30f6884c526e","impliedFormat":1},{"version":"f1c32f9ce9c497da4dc215c3bc84b722ea02497d35f9134db3bb40a8d918b92b","impliedFormat":1},{"version":"b73c319af2cc3ef8f6421308a250f328836531ea3761823b4cabbd133047aefa","affectsGlobalScope":true,"impliedFormat":1},{"version":"e433b0337b8106909e7953015e8fa3f2d30797cea27141d1c5b135365bb975a6","impliedFormat":1},{"version":"9f9bb6755a8ce32d656ffa4763a8144aa4f274d6b69b59d7c32811031467216e","impliedFormat":1},{"version":"5c32bdfbd2d65e8fffbb9fbda04d7165e9181b08dad61154961852366deb7540","impliedFormat":1},{"version":"ddff7fc6edbdc5163a09e22bf8df7bef75f75369ebd7ecea95ba55c4386e2441","impliedFormat":1},{"version":"0c05e9842ec4f8b7bfebfd3ca61604bb8c914ba8da9b5337c4f25da427a005f2","impliedFormat":1},{"version":"faed7a5153215dbd6ebe76dfdcc0af0cfe760f7362bed43284be544308b114cf","impliedFormat":1},{"version":"7029e566b8df176f703fb59fd437a38670c7a0e02c58b2d66dfb5b2e2b2defdb","impliedFormat":1},{"version":"7f2aa4d4989a82530aaac3f72b3dceca90e9c25bee0b1a327e8a08a1262435ad","impliedFormat":1},{"version":"d96b39301d0ded3f1a27b47759676a33a02f6f5049bfcbde81e533fd10f50dcb","impliedFormat":1},{"version":"e9f147ecca73d9346a4c073432843c159ccbe50bdcb678a78f6da10eae2cecf4","impliedFormat":1},{"version":"de061f7d72bd65c06fc1419f841dfdcb29a8e22fe6fa527d1e6eb20b897d4de0","impliedFormat":1},{"version":"663beafc2446079574570cba86e9b15f986f908ddb1b01274509970126fee945","impliedFormat":1},{"version":"a3102887d5058bf4cb5b37fa6964c09e9527c42053b3b5c642b89878620748de","impliedFormat":1},{"version":"0aaaa1727edd29673d85c9b26d7ca4d54e5407a48586903c51b48b7f7d196f61","impliedFormat":1},{"version":"d35bca0b261bff02635758c48e8ab99c61c420d0dfabbcf467e847171d876b7d","impliedFormat":1},{"version":"3bc12c40d90c342ff88a3d876996c555ed5cbee5fe8c3308a240b321f401ee46","impliedFormat":1},{"version":"ba130768aae855a5477e9e148e5c879548e6e7ccbcc56fd1934c8a18ea5b7569","impliedFormat":1},{"version":"2e4f37ffe8862b14d8e24ae8763daaa8340c0df0b859d9a9733def0eee7562d9","impliedFormat":1},{"version":"d38530db0601215d6d767f280e3a3c54b2a83b709e8d9001acb6f61c67e965fc","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"b499af2054a037a162b3b72cd886f48bbf32a3502c865c6e29fac7d2ab3ce0b5","impliedFormat":1},{"version":"b83cb14474fa60c5f3ec660146b97d122f0735627f80d82dd03e8caa39b4388c","impliedFormat":1},{"version":"48773ca557b0319c2ee62ae249cf52a81709e8be139920d6479a66274de7c4ed","impliedFormat":1},{"version":"7274fbffbd7c9589d8d0ffba68157237afd5cecff1e99881ea3399127e60572f","impliedFormat":1},{"version":"b73cbf0a72c8800cf8f96a9acfe94f3ad32ca71342a8908b8ae484d61113f647","impliedFormat":1},{"version":"bae6dd176832f6423966647382c0d7ba9e63f8c167522f09a982f086cd4e8b23","impliedFormat":1},{"version":"20865ac316b8893c1a0cc383ccfc1801443fbcc2a7255be166cf90d03fac88c9","impliedFormat":1},{"version":"c9958eb32126a3843deedda8c22fb97024aa5d6dd588b90af2d7f2bfac540f23","impliedFormat":1},{"version":"461d0ad8ae5f2ff981778af912ba71b37a8426a33301daa00f21c6ccb27f8156","impliedFormat":1},{"version":"e927c2c13c4eaf0a7f17e6022eee8519eb29ef42c4c13a31e81a611ab8c95577","impliedFormat":1},{"version":"fcafff163ca5e66d3b87126e756e1b6dfa8c526aa9cd2a2b0a9da837d81bbd72","impliedFormat":1},{"version":"70246ad95ad8a22bdfe806cb5d383a26c0c6e58e7207ab9c431f1cb175aca657","impliedFormat":1},{"version":"f00f3aa5d64ff46e600648b55a79dcd1333458f7a10da2ed594d9f0a44b76d0b","impliedFormat":1},{"version":"772d8d5eb158b6c92412c03228bd9902ccb1457d7a705b8129814a5d1a6308fc","impliedFormat":1},{"version":"802e797bcab5663b2c9f63f51bdf67eff7c41bc64c0fd65e6da3e7941359e2f7","impliedFormat":1},{"version":"b01bd582a6e41457bc56e6f0f9de4cb17f33f5f3843a7cf8210ac9c18472fb0f","impliedFormat":1},{"version":"8b4327413e5af38cd8cb97c59f48c3c866015d5d642f28518e3a891c469f240e","impliedFormat":1},{"version":"4cceef18d7f088e797a463e90b7a9dad10c6bc667724b7686e3e740ae00122be","impliedFormat":1},{"version":"7ee86fbb3754388e004de0ef9e6505485ddfb3be7640783d6d015711c03d302d","impliedFormat":1},{"version":"cc1954b539604b1e562319119ac7e888172208b32ca873f9a357a92c826bd046","impliedFormat":1},{"version":"a67b87d0281c97dfc1197ef28dfe397fc2c865ccd41f7e32b53f647184cc7307","impliedFormat":1},{"version":"771ffb773f1ddd562492a6b9aaca648192ac3f056f0e1d997678ff97dbb6bf9b","impliedFormat":1},{"version":"43e96a3d5d1411ab40ba2f61d6a3192e58177bcf3b133a80ad2a16591611726d","impliedFormat":1},{"version":"232f70c0cf2b432f3a6e56a8dc3417103eb162292a9fd376d51a3a9ea5fbbf6f","impliedFormat":1},{"version":"bb8f2dbc03533abca2066ce4655c119bff353dd4514375beb93c08590c03e023","impliedFormat":1},{"version":"706dd95827e7ebaabda91d5db2b755233e0952d98570e9c032b0f066a15c1177","affectsGlobalScope":true,"impliedFormat":1},{"version":"0b103e9abfe82d14c0ad06a55d9f91d6747154ef7cacc73cf27ecad2bfb3afcf","impliedFormat":1},{"version":"cd9304972e6d616197fb44fce00540a904f38b54306a1951b5dbeaf3c01ab5bd","impliedFormat":1},{"version":"77438e2c397a3db78407621cfc57241a305b310ddea2c185f1d555248297f587","impliedFormat":1},{"version":"120599fd965257b1f4d0ff794bc696162832d9d8467224f4665f713a3119078b","impliedFormat":1},{"version":"43ba4f2fa8c698f5c304d21a3ef596741e8e85a810b7c1f9b692653791d8d97a","impliedFormat":1},{"version":"5433f33b0a20300cca35d2f229a7fc20b0e8477c44be2affeb21cb464af60c76","impliedFormat":1},{"version":"db036c56f79186da50af66511d37d9fe77fa6793381927292d17f81f787bb195","impliedFormat":1},{"version":"a6805fcafed712aea7759f8bc731014f9d22738c1d6ef9d43b8091d1d48346d5","impliedFormat":1},{"version":"c49469a5349b3cc1965710b5b0f98ed6c028686aa8450bcb3796728873eb923e","impliedFormat":1},{"version":"4a889f2c763edb4d55cb624257272ac10d04a1cad2ed2948b10ed4a7fda2a428","impliedFormat":1},{"version":"7bb79aa2fead87d9d56294ef71e056487e848d7b550c9a367523ee5416c44cfa","impliedFormat":1},{"version":"d88ea80a6447d7391f52352ec97e56b52ebec934a4a4af6e2464cfd8b39c3ba8","impliedFormat":1},{"version":"142617b3cdf902b69c6464c9fbd942b60ab3e733ca18c032b19e0f7e2adbefe8","impliedFormat":1},{"version":"0b603555f1881f87256ffd6344d3e3ed6d466c2e701eabf381f28be8c2125892","impliedFormat":1},{"version":"897e4f7662488e3ecc79e743bdd3b78f13bdb69a97851afa5b440c4211e32ea9","impliedFormat":1},{"version":"e2e1c6d3b2d93add5200bd7bc1a8cccb4e446836b2111ece45db8683a2c765de","impliedFormat":1},{"version":"251b03d5cd243854ce870d9a9a39f491faf69898c5d6b5eee28cc7649c57417b","impliedFormat":1},{"version":"27ff4196654e6373c9af16b6165120e2dd2169f9ad6abb5c935af5abd8c7938c","impliedFormat":1},{"version":"2c4de79f406d137390608e8c0a44fba2ff8e00bacfcae7c9d1781fef10e9440d","impliedFormat":1},{"version":"07ba23a10465791be5d22deaf5ef7de7658774ddff53721e5ea17fedea1bc721","impliedFormat":1},{"version":"dca8c645c5afeb03b1ecedbf16323f33e7d0afaa6256c8e047e6e38087a97f53","impliedFormat":1},{"version":"775f181bd4a533d6f8b5e55ec1d9f1624559720ae8a70e9432258da26b38d27c","impliedFormat":1},{"version":"796273b2edc72e78a04e86d7c58ae94d370ab93a0ddf40b1aa85a37a1c29ecd7","impliedFormat":1},{"version":"5df15a69187d737d6d8d066e189ae4f97e41f4d53712a46b2710ff9f8563ec9f","impliedFormat":1},{"version":"7715134a0cf07dd41a9da2895d708625a3a303a0385e355ecaaf0b8bfaef2550","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"622694a8522b46f6310c2a9b5d2530dde1e2854cb5829354e6d1ff8f371cf469","impliedFormat":1},{"version":"cd8ce8d68567f62dd580b3c3c37777ac3f5b81944c7417f5ea83030eab533385","impliedFormat":1},{"version":"e5c939d896565dcac0f6fbdbada11284e7728ef26a069561c09aa5aa4a788393","impliedFormat":1},{"version":"9e2739b32f741859263fdba0244c194ca8e96da49b430377930b8f721d77c000","impliedFormat":1},{"version":"a9e6c0ff3f8186fccd05752cf75fc94e147c02645087ac6de5cc16403323d870","impliedFormat":1},{"version":"49af4b52f0d4d2304c5f2c6fe5fab3e153e0acc38830d0202821b877c097dd02","impliedFormat":1},{"version":"49c346823ba6d4b12278c12c977fb3a31c06b9ca719015978cb145eb86da1c61","impliedFormat":1},{"version":"bfac6e50eaa7e73bb66b7e052c38fdc8ccfc8dbde2777648642af33cf349f7f1","impliedFormat":1},{"version":"92f7c1a4da7fbfd67a2228d1687d5c2e1faa0ba865a94d3550a3941d7527a45d","impliedFormat":1},{"version":"f53b120213a9289d9a26f5af90c4c686dd71d91487a0aa5451a38366c70dc64b","impliedFormat":1},{"version":"e68b8e5a1df7c1be2bc105141456ecba70215806e1c28bfbc5c12bfce4be6e68","impliedFormat":1},{"version":"511c8f02329808d47d00b859c532ae9115590048b17325a946c74dac48428650","impliedFormat":1},{"version":"57d67b72e06059adc5e9454de26bbfe567d412b962a501d263c75c2db430f40e","impliedFormat":1},{"version":"b5f9e66625783eefcbe3d2da074b2e7ba2066d61ce3fc6ef4f22805ad946cab4","impliedFormat":1},{"version":"e37115962d284b9f7a37c2bdd2add50f88365dde41f5e0ff591ffc48a8ec7575","impliedFormat":1},{"version":"6459054aabb306821a043e02b89d54da508e3a6966601a41e71c166e4ea1474f","impliedFormat":1},{"version":"bb37588926aba35c9283fe8d46ebf4e79ffe976343105f5c6d45f282793352b2","impliedFormat":1},{"version":"f89488602bec98a142072fae7ea5ba99431a569ff580c64b7be39896474799d8","impliedFormat":1},{"version":"bbbc47961f39a57df103cf4ca3bb8f8732b4b6678a18225a0aa76d59c466956c","impliedFormat":1},{"version":"2e6114a7dd6feeef85b2c80120fdbfb59a5529c0dcc5bfa8447b6996c97a69f5","impliedFormat":1},{"version":"2ffb043dc5163458e473b7010859f86e01dc4edffcae0a93d885d028b426a546","impliedFormat":1},{"version":"c8f004e6036aa1c764ad4ec543cf89a5c1893a9535c80ef3f2b653e370de45e6","impliedFormat":1},{"version":"dd80b1e600d00f5c6a6ba23f455b84a7db121219e68f89f10552c54ba46e4dc9","impliedFormat":1},{"version":"b064c36f35de7387d71c599bfcf28875849a1dbc733e82bd26cae3d1cd060521","impliedFormat":1},{"version":"05c7280d72f3ed26f346cbe7cbbbb002fb7f15739197cbbee6ab3fd1a6cb9347","impliedFormat":1},{"version":"8de9fe97fa9e00ec00666fa77ab6e91b35d25af8ca75dabcb01e14ad3299b150","impliedFormat":1},{"version":"04b7b2e0832dfd3c31e81df3975e8d8fda28e7ff999b0aa2932608a8f6661d5c","impliedFormat":1},{"version":"ca2d34c6ed5cbd3070b8b6f32f42ae54adcc6499c1e4b99f0a5798b3f27cc653","impliedFormat":1},{"version":"9ec68995e66dd6b9dac834bf5ae85fde802714ea2e82151a5d1d53ef01b463ef","impliedFormat":1},{"version":"5c4d626b4902f2ef8a1cc146d761d276cef988016dc674e3b98fbad70e64bc9f","impliedFormat":1},{"version":"fdfaa0aad899524962e2955287b5b991ffe3be50f64e02eb60c933ca44644a94","impliedFormat":1},{"version":"53c972a0f9bc3a4ec70fff7314123ea8cfcf75b3703046f767d2dc1eea87b2fb","impliedFormat":1},{"version":"f974e4a06953682a2c15d5bd5114c0284d5abf8bc0fe4da25cb9159427b70072","impliedFormat":1},{"version":"50256e9c31318487f3752b7ac12ff365c8949953e04568009c8705db802776fb","impliedFormat":1},{"version":"7d73b24e7bf31dfb8a931ca6c4245f6bb0814dfae17e4b60c9e194a631fe5f7b","impliedFormat":1},{"version":"d130c5f73768de51402351d5dc7d1b36eaec980ca697846e53156e4ea9911476","impliedFormat":1},{"version":"413586add0cfe7369b64979d4ec2ed56c3f771c0667fbde1bf1f10063ede0b08","impliedFormat":1},{"version":"06472528e998d152375ad3bd8ebcb69ff4694fd8d2effaf60a9d9f25a37a097a","impliedFormat":1},{"version":"7303b45138d2511035056a5901a1490ebdcbf055cbb1276f8629c5121cbe733e","impliedFormat":1},{"version":"27f874cd5327507eeff699a74567f60c1215b94509f4308633a7b01922471ed2","impliedFormat":1},{"version":"a401617604fa1f6ce437b81689563dfdc377069e4c58465dbd8d16069aede0a5","impliedFormat":1},{"version":"2c6cf04bc525caf6546e859e8ef10bfb9573837ec0bc5ec7b53a7b1b8ca72781","impliedFormat":1},{"version":"8695dec09ad439b0ceef3776ea68a232e381135b516878f0901ed2ea114fd0fe","impliedFormat":1},{"version":"304b44b1e97dd4c94697c3313df89a578dca4930a104454c99863f1784a54357","impliedFormat":1},{"version":"0a437ae178f999b46b6153d79095b60c42c996bc0458c04955f1c996dc68b971","impliedFormat":1},{"version":"74b2a5e5197bd0f2e0077a1ea7c07455bbea67b87b0869d9786d55104006784f","impliedFormat":1},{"version":"4a7baeb6325920044f66c0f8e5e6f1f52e06e6d87588d837bdf44feb6f35c664","impliedFormat":1},{"version":"87cc05fe13108f02e12da7e3efd8e360fef78d96a0c9e11408ea1b1b9fb3e03d","impliedFormat":1},{"version":"1abbf67c218d23c2ce76887caac2df6c7dab3d97ba2b65348432b876f510002a","impliedFormat":1},{"version":"1a82deef4c1d39f6882f28d275cad4c01f907b9b39be9cbc472fcf2cf051e05b","impliedFormat":1},{"version":"4b20fcf10a5413680e39f5666464859fc56b1003e7dfe2405ced82371ebd49b6","impliedFormat":1},{"version":"c06ef3b2569b1c1ad99fcd7fe5fba8d466e2619da5375dfa940a94e0feea899b","impliedFormat":1},{"version":"f7d628893c9fa52ba3ab01bcb5e79191636c4331ee5667ecc6373cbccff8ae12","impliedFormat":1},{"version":"1d879125d1ec570bf04bc1f362fdbe0cb538315c7ac4bcfcdf0c1e9670846aa6","impliedFormat":1},{"version":"dad97c99382889e9c7d1a9d8275500ff71235130fae9f8916fdbf3641d56e592","impliedFormat":1},{"version":"a6dba407fc287f1e25454e75028c91bbc00675f2d1c4e8b3edcc36c08611a486","impliedFormat":1},{"version":"d663134457d8d669ae0df34eabd57028bddc04fc444c4bc04bc5215afc91e1f4","impliedFormat":1},{"version":"e91f7b1344577a02f051b9b471f33044fef8334a76dc9e1de003d17595a5219b","impliedFormat":1},{"version":"c0723195c85e19656d6b5b9fdb81d3f3403c1ae4679e722c6ea058c516b38d12","impliedFormat":1},{"version":"b55eb9f72166093b5460d34b34f5d8699c968de3bc3fc696e40f2c93f2ebf650","impliedFormat":1},{"version":"71d9eb4c4e99456b78ae182fb20a5dfc20eb1667f091dbb9335b3c017dd1c783","impliedFormat":1},{"version":"cfa846a7b7847a1d973605fbb8c91f47f3a0f0643c18ac05c47077ebc72e71c7","impliedFormat":1},{"version":"1594da19968752a22b2ac48c2d0e60575700e745c577a8a4a676b841238ad5bb","impliedFormat":1},{"version":"e0cee12109e0a10a4c3d6769fcc7644b7c1ea7f52365bea51728f5af29f8a137","impliedFormat":1},{"version":"7d4254b4c6c67a29d5e7f65e67d72540480ac2cfb041ca484847f5ae70480b62","impliedFormat":1},{"version":"3536968defef8a75514f547ead5e2e9c1e984820290ec9b00c5fdfb6ef786535","impliedFormat":1},{"version":"d83773870080c30a230e322ce13a9c6f3398e8dacea4ea8a83e26370f3bac23e","impliedFormat":1},{"version":"dcfeaf98d66314fec29a9076c4290e45d0b196a65827becc19138e9c7b855f37","impliedFormat":1},{"version":"6849fe9210fe4946d5f085bfed36758f33dc6ae15a751338d178dd4daa017c46","impliedFormat":1},{"version":"888cda0fa66d7f74e985a3f7b1af1f64b8ff03eb3d5e80d051c3cbdeb7f32ab7","impliedFormat":1},{"version":"60681e13f3545be5e9477acb752b741eae6eaf4cc01658a25ec05bff8b82a2ef","impliedFormat":1},{"version":"ffae4e1e06aa848a1e4bcef162cd1c48e5909b26223515981310af9c036bdfc7","impliedFormat":1},{"version":"a57b1802794433adec9ff3fed12aa79d671faed86c49b09e02e1ac41b4f1d33a","impliedFormat":1},{"version":"34e16eb7c31768a11a08aebcfb3d70d7b8f0b016197e98d8419e566ceae6d6c8","impliedFormat":1},{"version":"f94ec1f7e4b709d26960306c9082a7a1b728a6e13089346aa48ba57c74cbf47e","impliedFormat":1},{"version":"9a11cb4033405e96c247cd5aa29790212aaffdd127869e8a5219103f0b389fd5","impliedFormat":1},{"version":"01479d9d5a5dda16d529b91811375187f61a06e74be294a35ecce77e0b9e8d6c","impliedFormat":1},{"version":"aff5213585cb72e94054dfe17250ff315f3569b3919d1ef1ad235f37c4ee894e","impliedFormat":1},{"version":"fb2ea35e1be6388d722d7725e2b49c697d34d9c890c3b96758faaeb86d35cef8","impliedFormat":1},{"version":"ce0df82a9ae6f914ba08409d4d883983cc08e6d59eb2df02d8e4d68309e7848b","impliedFormat":1},{"version":"1a4dc28334a926d90ba6a2d811ba0ff6c22775fcc13679521f034c124269fd40","impliedFormat":1},{"version":"f05315ff85714f0b87cc0b54bcd3dde2716e5a6b99aedcc19cad02bf2403e08c","impliedFormat":1},{"version":"5fad3b31fc17a5bc58095118a8b160f5260964787c52e7eb51e3d4fcf5d4a6f0","impliedFormat":1},{"version":"72105519d0390262cf0abe84cf41c926ade0ff475d35eb21307b2f94de985778","impliedFormat":1},{"version":"456006a6975b26c0a1785feddae165f6d307e2d601ffde27e21fc4a790e448a4","impliedFormat":1},{"version":"c857e0aae3f5f444abd791ec81206020fbcc1223e187316677e026d1c1d6fe08","impliedFormat":1},{"version":"ccf6dd45b708fb74ba9ed0f2478d4eb9195c9dfef0ff83a6092fa3cf2ff53b4f","impliedFormat":1},{"version":"1fe0d18b111e1145a7e7601855bccd4ca20f24e3b9a5aba6bb1fa9d1a7059170","impliedFormat":1},{"version":"5632c3c26d420c063eebe64c45b1248b9492a67bf44f1d0c57e9dc8f6cf449bb","impliedFormat":1},{"version":"0df5aa619ab12993a39ea6dae062ee46eadbb4d738916460e636ada52bced75b","impliedFormat":1},{"version":"8fca3039857709484e5893c05c1f9126ab7451fa6c29e19bb8c2411a2e937345","impliedFormat":1},{"version":"35069c2c417bd7443ae7c7cafd1de02f665bf015479fec998985ffbbf500628c","impliedFormat":1},{"version":"10ab7be91f87ebe8916b62cf28af2e45b5601fc7b0e311adf838f912c6b31dd8","impliedFormat":1},{"version":"bc636fbc08e0979ceb7eb0731a33000283d77a33b62e1f71ee65be50394e40ba","impliedFormat":1},{"version":"7e0b7f91c5ab6e33f511efc640d36e6f933510b11be24f98836a20a2dc914c2d","impliedFormat":1},{"version":"045b752f44bf9bbdcaffd882424ab0e15cb8d11fa94e1448942e338c8ef19fba","impliedFormat":1},{"version":"2894c56cad581928bb37607810af011764a2f511f575d28c9f4af0f2ef02d1ab","impliedFormat":1},{"version":"0a72186f94215d020cb386f7dca81d7495ab6c17066eb07d0f44a5bf33c1b21a","impliedFormat":1},{"version":"75bbd3be047d539988a0ff0b56384ef7a6a25f3b676ad96bee547d44c31622a7","impliedFormat":1},{"version":"42960001a776b089ade681ab5cfddc936e0afb0615133ec1841f3dee89d3e1bf","impliedFormat":1},{"version":"0aedb02516baf3e66b2c1db9fef50666d6ed257edac0f866ea32f1aa05aa474f","impliedFormat":1},{"version":"da47712b394d944328245482603bc6f416d3949b67c9392279caab595076b510","affectsGlobalScope":true,"impliedFormat":1},{"version":"37d0071d8f0a06dc55c2c5e0ec3391affd4fd107c53410bf358196ec0bf3923f","impliedFormat":1},{"version":"b213dad76ca37fd552274c9499056e1c0d9c1bd38a55bb7f68b22ba6b84c3ad7","impliedFormat":1},{"version":"56ccb49443bfb72e5952f7012f0de1a8679f9f75fc93a5c1ac0bafb28725fc5f","impliedFormat":1},{"version":"20fa37b636fdcc1746ea0738f733d0aed17890d1cd7cb1b2f37010222c23f13e","impliedFormat":1},{"version":"d90b9f1520366d713a73bd30c5a9eb0040d0fb6076aff370796bc776fd705943","impliedFormat":1},{"version":"bc03c3c352f689e38c0ddd50c39b1e65d59273991bfc8858a9e3c0ebb79c023b","impliedFormat":1},{"version":"19df3488557c2fc9b4d8f0bac0fd20fb59aa19dec67c81f93813951a81a867f8","affectsGlobalScope":true,"impliedFormat":1},{"version":"b25350193e103ae90423c5418ddb0ad1168dc9c393c9295ef34980b990030617","affectsGlobalScope":true,"impliedFormat":1},{"version":"bef86adb77316505c6b471da1d9b8c9e428867c2566270e8894d4d773a1c4dc2","impliedFormat":1},{"version":"5a49adaef698b7ad7e6127949fa1b0bbd3d46b7cbd11c54e392a4dcdd51f5190","impliedFormat":1},{"version":"6ee598cdfdd0fa52039dca135b3dfff7b49035dc13292143e0a93843e3861967","impliedFormat":1},{"version":"27be6622e2922a1b412eb057faa854831b95db9db5035c3f6d4b677b902ab3b7","impliedFormat":1},{"version":"5c634644d45a1b6bc7b05e71e05e52ec04f3d73d9ac85d5927f647a5f965181a","impliedFormat":1},{"version":"2489bf04d77dc025ba67f49f1a56eb24b9db477d5ff88123d887e163ed1776aa","impliedFormat":1},{"version":"63a7595a5015e65262557f883463f934904959da563b4f788306f699411e9bac","impliedFormat":1},{"version":"4ba137d6553965703b6b55fd2000b4e07ba365f8caeb0359162ad7247f9707a6","impliedFormat":1},{"version":"0b77b819b5417775fccb20c678293cf614c054a5b1a65421a5b933a9124ba998","impliedFormat":1},{"version":"eb5acb58487367e502d994b57e2c58255d8241f481ea8efa8e79af23af3f41c2","impliedFormat":1},{"version":"9252d498a77517aab5d8d4b5eb9d71e4b225bbc7123df9713e08181de63180f6","impliedFormat":1},{"version":"b1f1d57fde8247599731b24a733395c880a6561ec0c882efaaf20d7df968c5af","impliedFormat":1},{"version":"6715dc4eb59c8ea9abe2b78c235ed331dc710a06fe56798868dbc4d40cd1b707","impliedFormat":1},{"version":"35e6379c3f7cb27b111ad4c1aa69538fd8e788ab737b8ff7596a1b40e96f4f90","impliedFormat":1},{"version":"1fffe726740f9787f15b532e1dc870af3cd964dbe29e191e76121aa3dd8693f2","impliedFormat":1},{"version":"5a3ea721d03a361ccbdd7390ccd75f6e84cbca3a3f01f4b331ecc9af31890c49","impliedFormat":1},{"version":"e7dfaee4af38d45b1cab8a1ee0b3bc1f85ddcf64545ed391d675d78ae6526274","affectsGlobalScope":true,"impliedFormat":1},{"version":"e8daa443eaf9a27fd382cc1f8ebe30330c0f4d89511cfb469166874806751d35","impliedFormat":1},{"version":"af48e58339188d5737b608d41411a9c054685413d8ae88b8c1d0d9bfabdf6e7e","impliedFormat":1},{"version":"616775f16134fa9d01fc677ad3f76e68c051a056c22ab552c64cc281a9686790","impliedFormat":1},{"version":"65c24a8baa2cca1de069a0ba9fba82a173690f52d7e2d0f1f7542d59d5eb4db0","impliedFormat":1},{"version":"f9fe6af238339a0e5f7563acee3178f51db37f32a2e7c09f85273098cee7ec49","impliedFormat":1},{"version":"1de8c302fd35220d8f29dea378a4ae45199dc8ff83ca9923aca1400f2b28848a","impliedFormat":1},{"version":"77e71242e71ebf8528c5802993697878f0533db8f2299b4d36aa015bae08a79c","impliedFormat":1},{"version":"98a787be42bd92f8c2a37d7df5f13e5992da0d967fab794adbb7ee18370f9849","impliedFormat":1},{"version":"332248ee37cca52903572e66c11bef755ccc6e235835e63d3c3e60ddda3e9b93","impliedFormat":1},{"version":"94e8cc88ae2ef3d920bb3bdc369f48436db123aa2dc07f683309ad8c9968a1e1","impliedFormat":1},{"version":"4545c1a1ceca170d5d83452dd7c4994644c35cf676a671412601689d9a62da35","impliedFormat":1},{"version":"320f4091e33548b554d2214ce5fc31c96631b513dffa806e2e3a60766c8c49d9","impliedFormat":1},{"version":"a2d648d333cf67b9aeac5d81a1a379d563a8ffa91ddd61c6179f68de724260ff","impliedFormat":1},{"version":"d90d5f524de38889d1e1dbc2aeef00060d779f8688c02766ddb9ca195e4a713d","impliedFormat":1},{"version":"07ed3ddab975995eea41b22f3010506fb9f5fb301d04820b07d7a1aee5477d7c","impliedFormat":1},{"version":"969d8b0965849f4bae7cab0ba90bd1e1220e95999c2c6f01117fa7500901c017","impliedFormat":1},{"version":"6ec840ee5e2bc103f557fe38b1d585ee250540468713d7634ee066de372bf332","impliedFormat":1},{"version":"b0309e1eda99a9e76f87c18992d9c3689b0938266242835dd4611f2b69efe456","impliedFormat":1},{"version":"47699512e6d8bebf7be488182427189f999affe3addc1c87c882d36b7f2d0b0e","impliedFormat":1},{"version":"6ceb10ca57943be87ff9debe978f4ab73593c0c85ee802c051a93fc96aaf7a20","impliedFormat":1},{"version":"1de3ffe0cc28a9fe2ac761ece075826836b5a02f340b412510a59ba1d41a505a","impliedFormat":1},{"version":"e46d6cc08d243d8d0d83986f609d830991f00450fb234f5b2f861648c42dc0d8","impliedFormat":1},{"version":"1c0a98de1323051010ce5b958ad47bc1c007f7921973123c999300e2b7b0ecc0","impliedFormat":1},{"version":"ff863d17c6c659440f7c5c536e4db7762d8c2565547b2608f36b798a743606ca","impliedFormat":1},{"version":"5412ad0043cd60d1f1406fc12cb4fb987e9a734decbdd4db6f6acf71791e36fe","impliedFormat":1},{"version":"ad036a85efcd9e5b4f7dd5c1a7362c8478f9a3b6c3554654ca24a29aa850a9c5","impliedFormat":1},{"version":"fedebeae32c5cdd1a85b4e0504a01996e4a8adf3dfa72876920d3dd6e42978e7","impliedFormat":1},{"version":"e297c0a524edee7677939122f90027bfbe5f2698939d9a85728e5044b39c7124","impliedFormat":1},{"version":"cdf21eee8007e339b1b9945abf4a7b44930b1d695cc528459e68a3adc39a622e","impliedFormat":1},{"version":"bc9ee0192f056b3d5527bcd78dc3f9e527a9ba2bdc0a2c296fbc9027147df4b2","impliedFormat":1},{"version":"b62381cae176db34f003cc6172ee8f3e0122014889d66391aa73698105cf4934","impliedFormat":1},{"version":"1d9c0a9a6df4e8f29dc84c25c5aa0bb1da5456ebede7a03e03df08bb8b27bae6","impliedFormat":1},{"version":"84380af21da938a567c65ef95aefb5354f676368ee1a1cbb4cae81604a4c7d17","impliedFormat":1},{"version":"1af3e1f2a5d1332e136f8b0b95c0e6c0a02aaabd5092b36b64f3042a03debf28","impliedFormat":1},{"version":"30d8da250766efa99490fc02801047c2c6d72dd0da1bba6581c7e80d1d8842a4","impliedFormat":1},{"version":"03566202f5553bd2d9de22dfab0c61aa163cabb64f0223c08431fb3fc8f70280","impliedFormat":1},{"version":"41eb514d9ce0a6e87957f08a4b7af70d93f87637f37dee706e2d92a6601c25a9","impliedFormat":1},{"version":"e7765aa8bcb74a38b3230d212b4547686eb9796621ffb4367a104451c3f9614f","impliedFormat":1},{"version":"1de80059b8078ea5749941c9f863aa970b4735bdbb003be4925c853a8b6b4450","impliedFormat":1},{"version":"1d079c37fa53e3c21ed3fa214a27507bda9991f2a41458705b19ed8c2b61173d","impliedFormat":1},{"version":"5bf5c7a44e779790d1eb54c234b668b15e34affa95e78eada73e5757f61ed76a","impliedFormat":1},{"version":"5835a6e0d7cd2738e56b671af0e561e7c1b4fb77751383672f4b009f4e161d70","impliedFormat":1},{"version":"4b7f74b772140395e7af67c4841be1ab867c11b3b82a51b1aeb692822b76c872","impliedFormat":1},{"version":"7bd01f0f28cd3aeb2046274d85208e245965f6f2948edf4f7b2057bcf9f22ccc","impliedFormat":99},{"version":"d2f2cf2b8cc92bea913cda4a076e0f790b23a21e84f989d12f0116a7fe3906e0","impliedFormat":99},{"version":"6de125ea94866c736c6d58d68eb15272cf7d1020a5b459fea1c660027eca9a90","affectsGlobalScope":true,"impliedFormat":1},{"version":"f5b20bc288ee49989c95b20847fc93b96bf61cc0845598897a6a53a967dd7d07","affectsGlobalScope":true,"impliedFormat":1},{"version":"064ac1c2ac4b2867c2ceaa74bbdce0cb6a4c16e7c31a6497097159c18f74aa7c","impliedFormat":1},{"version":"3dc14e1ab45e497e5d5e4295271d54ff689aeae00b4277979fdd10fa563540ae","impliedFormat":1},{"version":"d3b315763d91265d6b0e7e7fa93cfdb8a80ce7cdd2d9f55ba0f37a22db00bdb8","impliedFormat":1},{"version":"b789bf89eb19c777ed1e956dbad0925ca795701552d22e68fd130a032008b9f9","impliedFormat":1},{"version":"8f7e8a2919c14adeca7f27ea82ac7b16f67d7649b993f0b161df2427421a89db","affectsGlobalScope":true},"4e4da12aa061aac172fb1bcb48e9b6e4b293080d2f494327925fdba8f39632ac","4ea12de029a703cd58d376340c588e9e1c5b73a934718f992f0878570dc4e54f",{"version":"da451e3a2dfc734b3c5e1988c71ed1d69e4de602a98e68610c0355da7a46c5ab","impliedFormat":99},{"version":"4d7d964609a07368d076ce943b07106c5ebee8138c307d3273ba1cf3a0c3c751","impliedFormat":99},{"version":"0e48c1354203ba2ca366b62a0f22fec9e10c251d9d6420c6d435da1d079e6126","impliedFormat":99},{"version":"0662a451f0584bb3026340c3661c3a89774182976cd373eca502a1d3b5c7b580","impliedFormat":99},"cf42087ee343fc22ffeb3e6d8ace902a834439e895d7708454ef5c8d248609b6","efda8ac97f7890d6fa05b72ea47ebc9572ec7fc45ff15a46b3189ec37bd8ed50",{"version":"951765f4db0f5a4fab97fd750625a6a27db1db7da44655bad300ce88805ab662","impliedFormat":99},{"version":"aa2eb17cca50d8ceb993feecfdb8e78062c11c9b8a182354ce6b4ae80dae28a3","impliedFormat":99},{"version":"6bd987ccf12886137d96b81e48f65a7a6fa940085753c4e212c91f51555f13e5","impliedFormat":1},{"version":"efd5880912518c51769cd75c84dce5fcfbc854e636e5166a66e582861c331eb0","impliedFormat":99},"986cbcf0c2573b1dbedbf51abf91444fb53551c4b5357c44e92d51cbf4276586","0a41a004a826bdbbf67793e1b7ce2d402db891d8bbf8f3899e941ff4f28f4d36","0512f5733d87ae3805982a3b01ef0f6287b7ac3af47af9ad50a8ebbee8260e16","6e340da28d1a270ec002cd5e7c4b8e98837db5ef66e69175e37c808da478659f","a8acef442925070d86ea09da7dcf6e46eaaf358e336ff80f8d56678bd1e4955d","c01b0a38996ff24f9a0234c8714a7398f5a6013a6d25804f79ea1f5cb76f203d","240ac3de4d3a43bcf7333bcea1aa5c96f7ca90b4181a072f7d6f8f954317a0a9","38dd7552d232da3efca9d53ab95828482d589c20f727d08ca73b5daa7f04827b","61ab6908ee3676677611bf81e7692c5f660812e4ffa1749d97febde1314f6445","f5da7de5f29046a237118e2a96cb4426016f8af387fe7164441f4de5538d6ed0","04fefee246bb409bacf9d40a79341f433a33d45d382bd2663cfc527be4a12a30","1b1b30d05d15906b53f68a2410caa4621067f35c22de99e5c5705834226748a8","b8c001887c276c21fc2f9fbbe675e95b550f2aca576e3270481481db93575287",{"version":"782687557549bda921b4b83b42dea46548eb04a6f685e66cc9cbd3ced29e9d75","signature":"02edc4a1bec3eb9b17c4c463e1f74c7c644706bdc88efb14eca15d13640eae21"},"55aa75119ac9800aa7ee4a5ba51f77ee5242cdf513f7e99f397fc9ed51b2635c","5f1f4cbd74f40adb3fe09a6b0951b6bcc705312a4f6874ce88c7748efb9d1110",{"version":"56e46f7eda8d7e83f8917319391bc1155dff0ea381928b798d028a6eac09c36b","signature":"7a4fe07bffc8b6ec1785aec2b011f454fc93828d90b9269ccdda995af85bf073"},{"version":"264f935450101e4b000eb351cf75c9d799ca20a278b260a9e5770303b5f2b6a3","impliedFormat":99},{"version":"3796eacce3d5d295e03cea1f7fc6d9c339de924f00764398108d4317411b90af","impliedFormat":99},{"version":"304e41926d3299c9b30bfd418c35fffd2bd9e5ac726d6f758fb4e0f40a738d51","impliedFormat":99},{"version":"7d3b1ddfce35445b76298090a9dcadee8acf20f4c281eb1f2ce14fc7232c9470","affectsGlobalScope":true,"impliedFormat":99},{"version":"02ab5dbcaa58da1d58c46c7cdfa7f94792c5ccf0fc7c0622ef33755fe415366c","impliedFormat":99},{"version":"e689cc8cd8a102d31c9d3a7b0db0028594202093c4aca25982b425e8ae744556","impliedFormat":99},{"version":"478e59ac0830a0f6360236632d0d589fb0211183aa1ab82292fbca529c0cce35","impliedFormat":99},{"version":"1b4ed9deaba72d4bc8495bf46db690dbf91040da0cb2401db10bad162732c0e2","impliedFormat":99},{"version":"3e94295f73335c9122308a858445d2348949842579ac2bacd30728ab46fe75a7","impliedFormat":99},{"version":"613724ae3c7b4eaa7ede7b385303636772ce6894d093d40c740497f252d73395","impliedFormat":99},{"version":"68d3226e0a729ad95f134ce3d036d2d3bca9d2bc5fac2cac07a0f5364042fb5d","impliedFormat":99},{"version":"63a1b5c2705fa04df65f55c60c6072e3c3635b66c616be9afa7dbec5d90d3311","impliedFormat":99},{"version":"933937379203e9c6793d5c9126b40635dc8966d47814dfc810b37a936dcaaaab","impliedFormat":99},{"version":"8e299dc4dbd4f5271b45e2cc206cc0a3a19537ce2cc8d4bba857319cd56d788f","impliedFormat":99},{"version":"00a808ec5c093d67d1ffe5db7618b79e9c22a3760a556e71a1e285fb99368b8d","impliedFormat":99},{"version":"7bf932c1c467a3b5ad8b7d550db440226fab3f7540c671e74548ca39c067e35c","impliedFormat":99},{"version":"1b953eb91785e08aa68b63dfe79c1cda63639788e5bebe1a22d82ab440364613","impliedFormat":99},{"version":"979a9bb46d5ed5d3644b24c559b1d5d6088a903ee2a45d904ecb9f505b9c748c","impliedFormat":99},{"version":"82df1ac808761d975cd24fefd505e5c8bb75ef28abdb88c03d6b623a6f12baaf","impliedFormat":99},{"version":"ab3522eb8d80d545a5f8860eaa6e69391d0e865aeb75461fd4e88b2a9f1519ae","impliedFormat":99},{"version":"5bd475590a3276366f4fabdcca4cfdeb459301dc5b7842d178253b105bdb7c1c","impliedFormat":99},{"version":"a2fb8c7a0f1d7f5c917e9db6681c06df15c99188e159addf9f738c0d2c6e9bfd","impliedFormat":99},{"version":"d6db56f62efaea506c81126a7ddecc7a7c3cc2808ea90a56e90ba894a539412d","impliedFormat":99},{"version":"365b3bc377ecedcf0aabbd873f63d9b0c5cc615330e1eb76fd5f83eeceb99a82","impliedFormat":99},{"version":"05a1e09fa345d63c8238dbc0a975c78fdde1643cfe2547e8ab04f410f7128aa9","impliedFormat":99},{"version":"9bccadb03167b34956f28ce0bd256bd362ca25dd313e240e8a84dfa21957ab39","impliedFormat":99},{"version":"b5730817f864ee7824eb71b9a023e36913cef332149170aa7e8a267450344535","impliedFormat":99},{"version":"632f43c98cd6cde24b36b4c5430ee2a578dcd2cb646e4b7e5f3425af33b76142","impliedFormat":99},{"version":"1b817f465a7d6d96e4cedd561d830f8bf16135834123e574d801977f0070a24e","impliedFormat":99},{"version":"99ac52685e8120d3427d84cd0c99e4b9fe93f648e53d6fefcd8ee778f6983edb","impliedFormat":99},{"version":"61dc55f4156f10954cd70ed557c98de2b85e22510ad9fec5f2b03dd3221fecef","impliedFormat":99},{"version":"08d28e00b1807c4a0c34ab606f5e84b86c80200f3f607a5143b6619da29e6564","impliedFormat":99},{"version":"70a29119482d358ab4f28d28ee2dcd05d6cbf8e678068855d016e10a9256ec12","impliedFormat":1},{"version":"869ac759ae8f304536d609082732cb025a08dcc38237fe619caf3fcdd41dde6f","impliedFormat":1},{"version":"0ea900fe6565f9133e06bce92e3e9a4b5a69234e83d40b7df2e1752b8d2b5002","impliedFormat":1},{"version":"e5408f95ca9ac5997c0fea772d68b1bf390e16c2a8cad62858553409f2b12412","impliedFormat":1},{"version":"3c1332a48695617fc5c8a1aead8f09758c2e73018bd139882283fb5a5b8536a6","impliedFormat":1},{"version":"9260b03453970e98ce9b1ad851275acd9c7d213c26c7d86bae096e8e9db4e62b","impliedFormat":1},{"version":"083838d2f5fea0c28f02ce67087101f43bd6e8697c51fd48029261653095080c","impliedFormat":1},{"version":"969132719f0f5822e669f6da7bd58ea0eb47f7899c1db854f8f06379f753b365","impliedFormat":1},{"version":"94ca5d43ff6f9dc8b1812b0770b761392e6eac1948d99d2da443dc63c32b2ec1","impliedFormat":1},{"version":"2cbc88cf54c50e74ee5642c12217e6fd5415e1b35232d5666d53418bae210b3b","impliedFormat":1},{"version":"ccb226557417c606f8b1bba85d178f4bcea3f8ae67b0e86292709a634a1d389d","impliedFormat":1},{"version":"5ea98f44cc9de1fe05d037afe4813f3dcd3a8c5de43bdd7db24624a364fad8e6","impliedFormat":1},{"version":"5260a62a7d326565c7b42293ed427e4186b9d43d6f160f50e134a18385970d02","impliedFormat":1},{"version":"0b3fc2d2d41ad187962c43cb38117d0aee0d3d515c8a6750aaea467da76b42aa","impliedFormat":1},{"version":"ed219f328224100dad91505388453a8c24a97367d1bc13dcec82c72ab13012b7","impliedFormat":1},{"version":"6847b17c96eb44634daa112849db0c9ade344fe23e6ced190b7eeb862beca9f4","impliedFormat":1},{"version":"d479a5128f27f63b58d57a61e062bd68fa43b684271449a73a4d3e3666a599a7","impliedFormat":1},{"version":"6f308b141358ac799edc3e83e887441852205dc1348310d30b62c69438b93ca0","impliedFormat":1},{"version":"b2e451d7958fb4e559df8470e78cbabd17bcebdf694c3ac05440b00ae685aadb","impliedFormat":1},"5d2fd2175f0e4f9466d90deefde6c179761e6c07e05eb0799b249de19ae9a4ca",{"version":"8894980f4a2b584c382e24ca679283a92160124a21c3922ff8ca41238618adbd","signature":"537b42598915578aaad4f719a1cf0a8c9cc594adb20a3c8f48b3284692c63cde"},"5077ccd61a098e466a939d50402272bada959f627faf6f1251e16794ae3532a9",{"version":"48949b73c0e03450d05b22e6a841ef7c22ec6fb27fbbf645f7842ea6d4f71e29","signature":"3649c65db819dff661868b9dfae1377966669baaa37fd5cc12310019536acad3"},"38e3f69b06694ee8e38e4d98e278e3301ee47ca51d60ff89f4399ae482350970",{"version":"fe48a69c5ae084774822269f4b560e5b789bd9c9ae0044f61f51d7e9730b0b78","signature":"3fc01b7fbc45dcaddb50b67308e5bbfbeb0a177514b87f5652484275131de73f"},"fd46d89db0eb9fcc291c22d2f510fe35fd20a9128ffbcffb86bf1db5241a82de","d04feb6ba2612060fe74bc7f108adcef65948ac111036401cbbdb6f14ae53edf","0f2c29569092381944c92fede6c49eef74e874f3a435d5c1f0dde686fa7c93c6",{"version":"af0f0932c2085842b86df6615a4e945e4fcfc5dcec4ebdea53ce79834f1f50a5","signature":"26ba71f79eb1ccc526399c703ceb9595712a793b7d939584ca92df4a3ce4fea8"},"b4c5de19c58b076da3024237b9333a2f8df27f98c275dab73458d7a1e3170944","d1986184a09a52db8228cb2bb2a61a8c05c9354e5b93cec8e2628d8579c892d7","23d0aadc703ebf380db93111a35f964d544d8949460099e07541d41407ca3521",{"version":"15fe687c59d62741b4494d5e623d497d55eb38966ecf5bea7f36e48fc3fbe15e","impliedFormat":1},{"version":"2c3b8be03577c98530ef9cb1a76e2c812636a871f367e9edf4c5f3ce702b77f8","affectsGlobalScope":true,"impliedFormat":1},{"version":"ecec8f82ddf42db544c8320eddf7f09a9a788723f7473e82e903401a3d14d488","impliedFormat":1},{"version":"7fa8d75d229eeaee235a801758d9c694e94405013fe77d5d1dd8e3201fc414f1","impliedFormat":1}],"root":[[534,536],541,542,[547,563],[615,627]],"options":{"allowJs":true,"esModuleInterop":true,"jsx":4,"module":99,"skipLibCheck":true,"strict":true,"target":4},"referencedMap":[[626,1],[534,2],[627,3],[535,4],[536,5],[378,2],[613,6],[609,7],[596,2],[612,8],[605,9],[603,10],[602,10],[601,9],[598,10],[599,9],[607,11],[600,10],[597,9],[604,10],[610,12],[611,13],[606,14],[608,10],[573,15],[575,16],[576,16],[578,16],[577,16],[579,16],[580,16],[581,16],[582,16],[583,16],[584,17],[595,18],[585,16],[593,19],[586,16],[592,20],[591,21],[590,21],[587,16],[588,22],[570,23],[569,24],[572,25],[571,26],[574,27],[589,16],[594,28],[630,29],[142,30],[143,30],[144,31],[97,32],[145,33],[146,34],[147,35],[92,2],[95,36],[93,2],[94,2],[148,37],[149,38],[150,39],[151,40],[152,41],[153,42],[154,42],[155,43],[156,44],[157,45],[158,46],[98,2],[96,2],[159,47],[160,48],[161,49],[195,50],[162,51],[163,2],[164,52],[165,53],[166,54],[167,55],[168,56],[169,57],[170,58],[171,59],[172,60],[173,60],[174,61],[175,2],[176,62],[177,63],[179,64],[178,65],[180,66],[181,67],[182,68],[183,69],[184,70],[185,71],[186,72],[187,73],[188,74],[189,75],[190,76],[191,77],[192,78],[99,2],[100,2],[101,2],[139,79],[140,2],[141,2],[193,80],[194,81],[199,82],[463,83],[200,84],[198,85],[465,86],[464,87],[196,88],[461,2],[197,89],[83,2],[85,90],[460,83],[230,83],[629,91],[628,2],[631,2],[537,2],[84,2],[546,92],[545,92],[544,93],[543,2],[486,94],[491,1],[498,95],[481,96],[234,2],[242,97],[382,98],[385,99],[357,2],[370,100],[377,101],[259,2],[359,2],[240,2],[356,102],[402,103],[241,2],[232,104],[384,105],[386,106],[387,107],[458,108],[351,109],[304,110],[364,111],[365,112],[363,113],[362,2],[358,114],[383,115],[243,116],[428,2],[429,117],[270,118],[244,119],[271,118],[307,118],[210,118],[380,120],[379,2],[369,121],[476,2],[219,2],[497,122],[436,123],[437,124],[433,125],[515,2],[334,2],[438,126],[434,127],[520,128],[519,129],[514,2],[285,2],[337,130],[336,2],[513,131],[435,83],[290,132],[297,133],[299,134],[289,2],[294,135],[296,136],[298,137],[293,138],[291,2],[295,139],[516,2],[512,2],[518,140],[517,2],[288,141],[507,142],[510,143],[278,144],[277,145],[276,146],[523,83],[275,147],[264,2],[525,2],[526,83],[527,148],[202,2],[366,149],[367,150],[368,151],[206,2],[371,2],[226,152],[201,2],[450,83],[208,153],[449,154],[448,155],[439,2],[440,2],[447,2],[442,2],[445,156],[441,2],[443,157],[446,158],[444,157],[239,2],[236,2],[237,118],[391,2],[396,159],[397,160],[395,161],[393,162],[394,163],[389,2],[456,126],[231,126],[485,164],[492,165],[496,166],[325,167],[324,2],[319,2],[472,168],[480,169],[352,170],[353,171],[431,172],[341,2],[454,173],[329,83],[346,174],[457,175],[342,2],[345,176],[343,2],[455,177],[452,178],[451,2],[453,2],[349,2],[427,179],[214,180],[327,181],[331,182],[347,183],[350,184],[339,185],[332,186],[479,187],[405,188],[323,189],[211,190],[478,191],[207,192],[398,193],[390,2],[399,194],[416,195],[388,2],[415,196],[91,2],[410,197],[235,2],[430,198],[406,2],[220,2],[222,2],[361,2],[414,199],[238,2],[262,200],[348,201],[268,202],[328,2],[413,2],[392,2],[418,203],[419,204],[360,2],[421,205],[423,206],[422,207],[372,2],[412,190],[425,208],[322,209],[411,210],[417,211],[247,2],[251,2],[250,2],[249,2],[254,2],[248,2],[257,2],[256,2],[253,2],[252,2],[255,2],[258,212],[246,2],[314,213],[313,2],[318,214],[315,215],[317,216],[320,214],[316,215],[227,217],[306,218],[475,219],[473,2],[502,220],[504,221],[468,222],[503,223],[215,224],[212,224],[245,2],[229,225],[228,226],[224,227],[225,228],[233,229],[261,229],[272,229],[308,230],[273,230],[217,231],[216,2],[312,232],[311,233],[310,234],[309,235],[218,236],[459,237],[260,238],[467,239],[432,240],[462,241],[466,242],[355,243],[354,244],[335,245],[321,246],[303,247],[305,248],[302,249],[424,250],[326,2],[490,2],[223,251],[426,252],[474,253],[333,2],[263,254],[340,255],[338,256],[265,257],[400,258],[469,2],[266,259],[401,259],[488,2],[487,2],[489,2],[471,2],[470,2],[403,260],[330,2],[300,261],[221,262],[279,2],[205,263],[267,2],[494,83],[204,2],[506,264],[287,83],[500,126],[286,265],[483,266],[284,264],[209,2],[508,267],[282,83],[283,83],[274,2],[203,2],[281,268],[280,269],[269,270],[344,59],[404,59],[420,2],[408,271],[407,2],[292,141],[213,2],[301,83],[477,152],[484,272],[86,83],[89,273],[90,274],[87,83],[88,2],[381,275],[376,276],[375,2],[374,277],[373,2],[482,278],[493,279],[495,280],[499,281],[501,282],[505,283],[533,284],[509,284],[532,285],[511,286],[521,287],[522,288],[524,289],[528,290],[531,152],[530,2],[529,291],[564,2],[565,292],[568,293],[566,23],[567,294],[409,295],[614,296],[81,2],[82,2],[13,2],[14,2],[16,2],[15,2],[2,2],[17,2],[18,2],[19,2],[20,2],[21,2],[22,2],[23,2],[24,2],[3,2],[25,2],[26,2],[4,2],[27,2],[31,2],[28,2],[29,2],[30,2],[32,2],[33,2],[34,2],[5,2],[35,2],[36,2],[37,2],[38,2],[6,2],[42,2],[39,2],[40,2],[41,2],[43,2],[7,2],[44,2],[49,2],[50,2],[45,2],[46,2],[47,2],[48,2],[8,2],[54,2],[51,2],[52,2],[53,2],[55,2],[9,2],[56,2],[57,2],[58,2],[60,2],[59,2],[61,2],[62,2],[10,2],[63,2],[64,2],[65,2],[11,2],[66,2],[67,2],[68,2],[69,2],[70,2],[1,2],[71,2],[72,2],[12,2],[76,2],[74,2],[79,2],[78,2],[73,2],[77,2],[75,2],[80,2],[117,297],[127,298],[116,297],[137,299],[108,300],[107,301],[136,291],[130,302],[135,303],[110,304],[124,305],[109,306],[133,307],[105,308],[104,291],[134,309],[106,310],[111,311],[112,2],[115,311],[102,2],[138,312],[128,313],[119,314],[120,315],[122,316],[118,317],[121,318],[131,291],[113,319],[114,320],[123,321],[103,322],[126,313],[125,311],[129,2],[132,323],[540,324],[539,325],[538,2],[553,326],[559,327],[562,328],[563,329],[616,330],[617,331],[618,330],[619,332],[621,333],[620,330],[622,329],[623,334],[550,335],[624,336],[551,337],[625,338],[555,126],[558,126],[557,339],[554,126],[556,340],[552,341],[615,342],[560,341],[561,343],[542,344],[547,345],[541,346],[548,346],[549,346]],"affectedFilesPendingEmit":[627,536,553,559,562,563,616,617,618,619,621,620,622,623,550,624,551,625,555,558,557,554,556,552,615,560,561,542,547,541,548,549],"version":"5.9.3"}
\ No newline at end of file