From 5a50587a73828b621915458af85a3f1fb5cd52ec Mon Sep 17 00:00:00 2001 From: AG Date: Wed, 19 Nov 2025 21:55:51 +0000 Subject: [PATCH] Initial Commit This project is a Kanban-style task board with a Go backend and a Svelte frontend. Tasks are persisted to a local JSON file and exposed via REST endpoints. The UI supports columns (Kanban statuses), drag-and-drop, inline editing, and a calendar view for deadlines. --- .gitignore | 37 ++ README.md | 109 +++-- backend/db.json | 36 ++ backend/main.go | 269 ++++++++++- frontend/.gitignore | 24 - frontend/.nvmrc | 1 - frontend/README.md | 43 ++ frontend/index.html | 23 +- frontend/jsconfig.json | 33 ++ frontend/package-lock.json | 418 +++++++++--------- frontend/package.json | 16 +- frontend/public/sparklayer.svg | 24 - frontend/public/vite.svg | 1 + frontend/src/App.svelte | 101 ++--- frontend/src/app.css | 119 +++-- frontend/src/assets/svelte.svg | 1 + frontend/src/lib/Counter.svelte | 10 + frontend/src/lib/Todo.svelte | 57 --- frontend/src/lib/api/todo.ts | 19 + .../src/lib/components/CalendarView.svelte | 242 ++++++++++ frontend/src/lib/components/Kanban.svelte | 346 +++++++++++++++ frontend/src/lib/stores/taskboard.js | 107 +++++ frontend/src/lib/types.ts | 4 - frontend/src/{main.ts => main.js} | 2 +- frontend/src/reset.css | 48 -- frontend/svelte.config.js | 7 +- frontend/tsconfig.app.json | 21 - frontend/tsconfig.json | 7 - frontend/tsconfig.node.json | 26 -- frontend/{vite.config.ts => vite.config.js} | 0 30 files changed, 1563 insertions(+), 588 deletions(-) create mode 100644 backend/db.json delete mode 100644 frontend/.gitignore delete mode 100644 frontend/.nvmrc create mode 100644 frontend/README.md create mode 100644 frontend/jsconfig.json delete mode 100644 frontend/public/sparklayer.svg create mode 100644 frontend/public/vite.svg create mode 100644 frontend/src/assets/svelte.svg create mode 100644 frontend/src/lib/Counter.svelte delete mode 100644 frontend/src/lib/Todo.svelte create mode 100644 frontend/src/lib/api/todo.ts create mode 100644 frontend/src/lib/components/CalendarView.svelte create mode 100644 frontend/src/lib/components/Kanban.svelte create mode 100644 frontend/src/lib/stores/taskboard.js delete mode 100644 frontend/src/lib/types.ts rename frontend/src/{main.ts => main.js} (75%) delete mode 100644 frontend/src/reset.css delete mode 100644 frontend/tsconfig.app.json delete mode 100644 frontend/tsconfig.json delete mode 100644 frontend/tsconfig.node.json rename frontend/{vite.config.ts => vite.config.js} (100%) diff --git a/.gitignore b/.gitignore index e69de29..4a7914f 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1,37 @@ +# macOS +.DS_Store + +# Environment files +.env +.env.* +frontend/.env +backend/.env + +# Logs +*.log +logs/ + +# Node / Vite / Svelte +frontend/node_modules/ +frontend/dist/ +frontend/.vite/ +frontend/.svelte-kit/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Go build artifacts +backend/bin/ +backend/tmp/ +backend/coverage/ +backend/*.out + +# Local database files (keep committed sample db.json; ignore local variants) +backend/db.local.json +backend/data/*.json + +# IDE caches (keep tracked settings if any) +.idea/ +.pytest_cache/ +.cache/ \ No newline at end of file diff --git a/README.md b/README.md index a66f4cc..e887cf4 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,104 @@ -Create the foundation of a to-do list application, focusing on backend functionality and essential frontend interaction. Your task is implementing a RESTful API using Go and a simple TypeScript interface using [Svelte](https://svelte.dev/). The goal is to be able to be able to list and add todos. +# Kanban Taskboard — Go + Svelte -You are not expected to know Go, Svelte, or OpenAPI. Part of the challenge is to see how quickly you can adapt and pick new things up! +This project is a Kanban-style task board with a Go backend and a Svelte frontend. Tasks are persisted to a local JSON file and exposed via REST endpoints. The UI supports columns (Kanban statuses), drag-and-drop, inline editing, and a calendar view for deadlines. -The backend needs to meet the openapi spec which is within the backend folder. You need to create the list endpoint and the add todo endpoint. The storage system is in-memory. +## Overview -The frontend already has functionality to list the todos, your task is to complete the form which submits todo's to the backend system. +- Backend (Go): JSON file–backed store with REST endpoints for `kanban` (columns) and `tasks`. +- Frontend (Svelte): Loads columns and tasks from the backend, persists all edits (add/update/move/delete), and caches to `localStorage` as a fallback. +- Data models: + - `kanban`: `{ id, title }[]` + - `tasks`: `{ id, name, description, priority, deadline, status_id, position }[]` -Please use this repo as your base. You can click "Use this template" in the top right on GitHub, then "Create a new repository". Commit and push your code so it can reviewed by us. Please get as far as you can within 2 hours. +## Setup -If you find anything in the template you would like to improve, please feel free to! +Install: +- Go ([install instructions](https://go.dev/doc/install)) +- Node.js + npm (we recommend [NVM](https://github.com/nvm-sh/nvm)) +Tested with Go 1.25 and Node 20. -## Setup +## Running -If not already installed, please install the following: -1. Go ([install instructions](https://go.dev/doc/install)) -2. NPM/NodeJS. We recommend using [NVM](https://github.com/nvm-sh/nvm) +Open two terminals: one for the backend, one for the frontend. -We have tested this with Go 1.25 and Node 20. You may have issues if you try to use a different version. +### Backend (Go) -A good starting point would be to look at the following files: -- `backend/main.go` -- `frontend/src/App.svelte` +1. `cd backend` +2. `go run .` -## Running +Environment: +- `PORT` (optional): default `8080` +- `DB_FILE` (optional): path to JSON file, default `db.json` + +On first run, if `DB_FILE` is missing, sample columns are created and the file is saved. + +### Frontend (Svelte) + +1. `cd frontend` +2. `npm install` +3. `npm run dev` +4. Open `http://localhost:5174/` + +Configuration: +- `VITE_API_BASE_URL` (optional): API base, default `http://localhost:8080` + +## REST API + +Base URL: `http://localhost:8080` + +### Columns (Kanban) + +- `GET /api/kanban` → `[{ id, title }]` +- `POST /api/kanban` body: `{ title }` → created column +- `PATCH /api/kanban/{id}` body: `{ title }` → updated column +- `DELETE /api/kanban/{id}` → deletes column and cascades tasks in that column + +### Tasks + +- `GET /api/tasks` → `[{ id, name, description, priority, deadline, status_id, position }]` +- `POST /api/tasks` body: `{ name, description, priority, deadline, status_id }` → created task +- `PATCH /api/tasks/{id}` body: any subset of fields (partial update) +- `DELETE /api/tasks/{id}` → deletes task + +Notes: +- Task positions are maintained per column; new tasks are appended to the end. +- Partial updates do not overwrite omitted fields. + +## Frontend Behavior -Open two separate terminals - one for the Svelte app and one for the golang API. +- Kanban board: add/rename/delete columns; add/edit/move/delete tasks; drag to reorder. +- Calendar view: tasks appear on their `deadline` date; dragging a task to a new date updates only `deadline`. +- All changes persist to the backend and cache to `localStorage`. +## Quick Test (curl) -### Golang API +```bash +# List columns +curl http://localhost:8080/api/kanban -1. In the first terminal, change to the backend directory (`cd backend`) -2. Run `go run main.go` to start the API server +# Create a column +curl -X POST http://localhost:8080/api/kanban \ + -H 'Content-Type: application/json' \ + -d '{"title":"Backlog"}' -This must be running for the frontend to work. +# Create a task in column id "todo" +curl -X POST http://localhost:8080/api/tasks \ + -H 'Content-Type: application/json' \ + -d '{"name":"Write report","description":"Q4 summary","priority":"Medium","deadline":"2025-11-20","status_id":"todo"}' -When you make a change, you must stop the server (`ctrl-c` in the terminal), and restart it with `go run main.go`. +# Move task and change deadline (partial update) +curl -X PATCH http://localhost:8080/api/tasks/{task_id} \ + -H 'Content-Type: application/json' \ + -d '{"status_id":"inprogress","deadline":"2025-11-22"}' +``` +## Files of Interest -### Svelte App +- Backend: `backend/main.go` (HTTP handlers, JSON DB), `backend/db.json` (data file) +- Frontend: `frontend/src/lib/components/Kanban.svelte`, `frontend/src/lib/components/CalendarView.svelte`, `frontend/src/lib/stores/taskboard.js` -1. In the second terminal, change to the frontend directory (`cd frontend`) -2. Run `npm run dev` to start the Svelte app -3. If it doesn't open automatically, open [http://localhost:5173](http://localhost:5173) to view your website +## Troubleshooting -Leave this running. It will automatically update when you make any changes. +- Ensure the backend is running on `:8080` or set `VITE_API_BASE_URL` for the frontend. +- If task names disappear when changing the deadline, make sure you are running the patched backend that performs partial updates correctly. \ No newline at end of file diff --git a/backend/db.json b/backend/db.json new file mode 100644 index 0000000..24abdbf --- /dev/null +++ b/backend/db.json @@ -0,0 +1,36 @@ +{ + "kanban": [ + { + "id": "todo", + "title": "To Do" + }, + { + "id": "inprogress", + "title": "In Progresswef" + }, + { + "id": "done", + "title": "Done" + } + ], + "tasks": [ + { + "id": "20251119T215029.085279000", + "name": "", + "description": "", + "priority": "Medium", + "deadline": "", + "status_id": "todo", + "position": 0 + }, + { + "id": "20251119T215155.147601000", + "name": "qwef", + "description": "qwefeef", + "priority": "Medium", + "deadline": "2025-11-19", + "status_id": "done", + "position": 0 + } + ] +} \ No newline at end of file diff --git a/backend/main.go b/backend/main.go index bca2a29..968bd93 100644 --- a/backend/main.go +++ b/backend/main.go @@ -1,13 +1,272 @@ package main -import "net/http" +import ( + "encoding/json" + "errors" + "io/ioutil" + "log" + "net/http" + "os" + "path/filepath" + "strings" + "sync" + "time" +) + +type Column struct { + ID string `json:"id"` + Title string `json:"title"` +} + +type ApiTask struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Priority string `json:"priority"` + Deadline string `json:"deadline"` + StatusID string `json:"status_id"` + Position int `json:"position"` +} + +// TaskPatch uses pointer fields so we can tell which fields were provided +// in the incoming JSON and update only those. Absent fields remain unchanged. +type TaskPatch struct { + Name *string `json:"name"` + Description *string `json:"description"` + Priority *string `json:"priority"` + Deadline *string `json:"deadline"` + StatusID *string `json:"status_id"` + Position *int `json:"position"` +} + +type Database struct { + Kanban []Column `json:"kanban"` + Tasks []ApiTask `json:"tasks"` +} + +type FileDB struct { + mu sync.RWMutex + db Database + dbPath string +} + +func NewFileDB() *FileDB { + path := os.Getenv("DB_FILE") + if path == "" { path = "db.json" } + f := &FileDB{dbPath: path} + if err := f.load(); err != nil { + log.Printf("db load failed, initializing sample: %v", err) + f.db = Database{ + Kanban: []Column{{ID: "todo", Title: "To Do"}, {ID: "inprogress", Title: "In Progress"}, {ID: "done", Title: "Done"}}, + Tasks: []ApiTask{}, + } + _ = f.save() + } + return f +} + +func (f *FileDB) load() error { + f.mu.Lock(); defer f.mu.Unlock() + b, err := ioutil.ReadFile(f.dbPath) + if err != nil { return err } + var db Database + if err := json.Unmarshal(b, &db); err != nil { return err } + f.db = db + return nil +} + +func (f *FileDB) save() error { + // ensure directory exists + if dir := filepath.Dir(f.dbPath); dir != "." && dir != "" { _ = os.MkdirAll(dir, 0755) } + b, err := json.MarshalIndent(f.db, "", " ") + if err != nil { return err } + return ioutil.WriteFile(f.dbPath, b, 0644) +} + +// Columns +func (f *FileDB) ListColumns() []Column { f.mu.RLock(); defer f.mu.RUnlock(); out := make([]Column, len(f.db.Kanban)); copy(out, f.db.Kanban); return out } +func (f *FileDB) InsertColumn(title string) Column { + f.mu.Lock(); defer f.mu.Unlock() + id := randomID() + c := Column{ID: id, Title: strings.TrimSpace(title)} + f.db.Kanban = append(f.db.Kanban, c); _ = f.save(); return c +} +func (f *FileDB) UpdateColumn(id, title string) (Column, bool) { + f.mu.Lock(); defer f.mu.Unlock() + for i := range f.db.Kanban { + if f.db.Kanban[i].ID == id { + f.db.Kanban[i].Title = strings.TrimSpace(title) + _ = f.save(); return f.db.Kanban[i], true + } + } + return Column{}, false +} +func (f *FileDB) DeleteColumn(id string) bool { + f.mu.Lock(); defer f.mu.Unlock() + // remove column + idx := -1 + for i := range f.db.Kanban { if f.db.Kanban[i].ID == id { idx = i; break } } + if idx == -1 { return false } + f.db.Kanban = append(f.db.Kanban[:idx], f.db.Kanban[idx+1:]...) + // cascade tasks + tasks := f.db.Tasks[:0] + for _, t := range f.db.Tasks { if t.StatusID != id { tasks = append(tasks, t) } } + f.db.Tasks = tasks + _ = f.save(); return true +} + +// Tasks +func (f *FileDB) ListTasks() []ApiTask { f.mu.RLock(); defer f.mu.RUnlock(); out := make([]ApiTask, len(f.db.Tasks)); copy(out, f.db.Tasks); return out } +func (f *FileDB) InsertTask(in ApiTask) (ApiTask, error) { + f.mu.Lock(); defer f.mu.Unlock() + if strings.TrimSpace(in.Name) == "" { return ApiTask{}, errors.New("name required") } + if in.ID == "" { in.ID = randomID() } + // set position at end of column + max := -1 + for _, t := range f.db.Tasks { if t.StatusID == in.StatusID && t.Position > max { max = t.Position } } + in.Position = max + 1 + f.db.Tasks = append(f.db.Tasks, in) + return in, f.save() +} +func (f *FileDB) UpdateTask(id string, patch TaskPatch) (ApiTask, bool) { + f.mu.Lock(); defer f.mu.Unlock() + idx := -1 + for i := range f.db.Tasks { if f.db.Tasks[i].ID == id { idx = i; break } } + if idx == -1 { return ApiTask{}, false } + curr := f.db.Tasks[idx] + if patch.Name != nil { curr.Name = strings.TrimSpace(*patch.Name) } + if patch.Description != nil { curr.Description = *patch.Description } + if patch.Priority != nil { curr.Priority = *patch.Priority } + if patch.Deadline != nil { curr.Deadline = *patch.Deadline } + // status move + if patch.StatusID != nil { curr.StatusID = *patch.StatusID } + // position update + if patch.Position != nil { curr.Position = *patch.Position } + f.db.Tasks[idx] = curr + _ = f.save(); return curr, true +} +func (f *FileDB) DeleteTask(id string) bool { + f.mu.Lock(); defer f.mu.Unlock() + idx := -1 + for i := range f.db.Tasks { if f.db.Tasks[i].ID == id { idx = i; break } } + if idx == -1 { return false } + f.db.Tasks = append(f.db.Tasks[:idx], f.db.Tasks[idx+1:]...) + _ = f.save(); return true +} + +func randomID() string { return time.Now().UTC().Format("20060102T150405.000000000") } func main() { - // Your code here + repo := NewFileDB() + mux := http.NewServeMux() + // Legacy root To-Do per openAPI remains + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { ToDoListHandler(w, r, repo) }) + // Kanban columns + mux.HandleFunc("/api/kanban", func(w http.ResponseWriter, r *http.Request) { KanbanHandler(w, r, repo) }) + mux.HandleFunc("/api/kanban/", func(w http.ResponseWriter, r *http.Request) { KanbanItemHandler(w, r, repo) }) + // Tasks + mux.HandleFunc("/api/tasks", func(w http.ResponseWriter, r *http.Request) { TasksHandler(w, r, repo) }) + mux.HandleFunc("/api/tasks/", func(w http.ResponseWriter, r *http.Request) { TaskHandler(w, r, repo) }) + + addr := ":8080" + if v := os.Getenv("PORT"); v != "" { addr = ":" + v } + log.Printf("Starting backend on %s", addr) + if err := http.ListenAndServe(addr, withCORS(mux)); err != nil { log.Fatalf("server error: %v", err) } } -func ToDoListHandler(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") +func withCORS(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Methods", "GET,POST,PATCH,PUT,DELETE,OPTIONS") + w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") + if r.Method == http.MethodOptions { w.WriteHeader(http.StatusNoContent); return } + next.ServeHTTP(w, r) + }) +} + +// Legacy ToDo for root +type Todo struct { Title string `json:"title"`; Description string `json:"description"` } +func ToDoListHandler(w http.ResponseWriter, r *http.Request, repo *FileDB) { + w.Header().Set("Content-Type", "application/json") + switch r.Method { + case http.MethodGet: + repo.mu.RLock(); out := make([]Todo, 0); repo.mu.RUnlock() + writeJSON(w, http.StatusOK, out); return + case http.MethodPost: + var in Todo + dec := json.NewDecoder(r.Body); dec.DisallowUnknownFields() + if err := dec.Decode(&in); err != nil { http.Error(w, "invalid input", http.StatusBadRequest); return } + writeJSON(w, http.StatusOK, in); return + default: + http.Error(w, "method not allowed", http.StatusMethodNotAllowed); return + } +} + +// Columns handlers +func KanbanHandler(w http.ResponseWriter, r *http.Request, repo *FileDB) { + w.Header().Set("Content-Type", "application/json") + switch r.Method { + case http.MethodGet: + writeJSON(w, http.StatusOK, repo.ListColumns()); return + case http.MethodPost: + var in struct{ Title string `json:"title"` } + if err := json.NewDecoder(r.Body).Decode(&in); err != nil { http.Error(w, "invalid input", http.StatusBadRequest); return } + created := repo.InsertColumn(in.Title); writeJSON(w, http.StatusOK, created); return + default: + http.Error(w, "method not allowed", http.StatusMethodNotAllowed); return + } +} +func KanbanItemHandler(w http.ResponseWriter, r *http.Request, repo *FileDB) { + w.Header().Set("Content-Type", "application/json") + id := strings.TrimPrefix(r.URL.Path, "/api/kanban/") + if id == "" { http.Error(w, "not found", http.StatusNotFound); return } + switch r.Method { + case http.MethodPatch, http.MethodPut: + var in struct{ Title string `json:"title"` } + if err := json.NewDecoder(r.Body).Decode(&in); err != nil { http.Error(w, "invalid input", http.StatusBadRequest); return } + updated, ok := repo.UpdateColumn(id, in.Title); if !ok { http.Error(w, "not found", http.StatusNotFound); return } + writeJSON(w, http.StatusOK, updated); return + case http.MethodDelete: + if ok := repo.DeleteColumn(id); !ok { http.Error(w, "not found", http.StatusNotFound); return } + w.WriteHeader(http.StatusNoContent); return + default: + http.Error(w, "method not allowed", http.StatusMethodNotAllowed); return + } +} - // Your code here +// Tasks handlers +func TasksHandler(w http.ResponseWriter, r *http.Request, repo *FileDB) { + w.Header().Set("Content-Type", "application/json") + switch r.Method { + case http.MethodGet: + writeJSON(w, http.StatusOK, repo.ListTasks()); return + case http.MethodPost: + var in ApiTask + dec := json.NewDecoder(r.Body); dec.DisallowUnknownFields() + if err := dec.Decode(&in); err != nil { http.Error(w, "invalid input", http.StatusBadRequest); return } + created, err := repo.InsertTask(in); if err != nil { http.Error(w, err.Error(), http.StatusBadRequest); return } + writeJSON(w, http.StatusOK, created); return + default: + http.Error(w, "method not allowed", http.StatusMethodNotAllowed); return + } } +func TaskHandler(w http.ResponseWriter, r *http.Request, repo *FileDB) { + w.Header().Set("Content-Type", "application/json") + id := strings.TrimPrefix(r.URL.Path, "/api/tasks/") + if id == "" { http.Error(w, "not found", http.StatusNotFound); return } + switch r.Method { + case http.MethodPatch, http.MethodPut: + var patch TaskPatch + dec := json.NewDecoder(r.Body); dec.DisallowUnknownFields() + if err := dec.Decode(&patch); err != nil { http.Error(w, "invalid input", http.StatusBadRequest); return } + updated, ok := repo.UpdateTask(id, patch); if !ok { http.Error(w, "not found", http.StatusNotFound); return } + writeJSON(w, http.StatusOK, updated); return + case http.MethodDelete: + if ok := repo.DeleteTask(id); !ok { http.Error(w, "not found", http.StatusNotFound); return } + w.WriteHeader(http.StatusNoContent); return + default: + http.Error(w, "method not allowed", http.StatusMethodNotAllowed); return + } +} + +func writeJSON(w http.ResponseWriter, status int, v interface{}) { w.WriteHeader(status); _ = json.NewEncoder(w).Encode(v) } diff --git a/frontend/.gitignore b/frontend/.gitignore deleted file mode 100644 index a547bf3..0000000 --- a/frontend/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/frontend/.nvmrc b/frontend/.nvmrc deleted file mode 100644 index 9a2a0e2..0000000 --- a/frontend/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -v20 diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..54a2631 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,43 @@ +# Svelte + Vite + +This template should help get you started developing with Svelte in Vite. + +## Recommended IDE Setup + +[VS Code](https://code.visualstudio.com/) + [Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode). + +## Need an official Svelte framework? + +Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also powered by Vite. Deploy anywhere with its serverless-first approach and adapt to various platforms, with out of the box support for TypeScript, SCSS, and Less, and easily-added support for mdsvex, GraphQL, PostCSS, Tailwind CSS, and more. + +## Technical considerations + +**Why use this over SvelteKit?** + +- It brings its own routing solution which might not be preferable for some users. +- It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app. + +This template contains as little as possible to get started with Vite + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project. + +Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been structured similarly to SvelteKit so that it is easy to migrate. + +**Why include `.vscode/extensions.json`?** + +Other templates indirectly recommend extensions via the README, but this file allows VS Code to prompt the user to install the recommended extension upon opening the project. + +**Why enable `checkJs` in the JS template?** + +It is likely that most cases of changing variable types in runtime are likely to be accidental, rather than deliberate. This provides advanced typechecking out of the box. Should you like to take advantage of the dynamically-typed nature of JavaScript, it is trivial to change the configuration. + +**Why is HMR not preserving my local component state?** + +HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/sveltejs/svelte-hmr/tree/master/packages/svelte-hmr#preservation-of-local-state). + +If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR. + +```js +// store.js +// An extremely simple external store +import { writable } from 'svelte/store' +export default writable(0) +``` diff --git a/frontend/index.html b/frontend/index.html index a557289..7d082ee 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,16 +1,13 @@ - - - - - - Spark Code Test - - - -
- - - + + + + + frontend + + +
+ + diff --git a/frontend/jsconfig.json b/frontend/jsconfig.json new file mode 100644 index 0000000..c7a0b10 --- /dev/null +++ b/frontend/jsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "moduleResolution": "bundler", + "target": "ESNext", + "module": "ESNext", + /** + * svelte-preprocess cannot figure out whether you have + * a value or a type, so tell TypeScript to enforce using + * `import type` instead of `import` for Types. + */ + "verbatimModuleSyntax": true, + "isolatedModules": true, + "resolveJsonModule": true, + /** + * To have warnings / errors of the Svelte compiler at the + * correct position, enable source maps by default. + */ + "sourceMap": true, + "esModuleInterop": true, + "types": ["vite/client"], + "skipLibCheck": true, + /** + * Typecheck JS in `.svelte` and `.js` files by default. + * Disable this if you'd like to use dynamic types. + */ + "checkJs": true + }, + /** + * Use global.d.ts instead of compilerOptions.types + * to avoid limiting type declarations. + */ + "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"] +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 9bd3085..1061d1d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,20 +1,16 @@ { - "name": "spark-code-test", + "name": "frontend", "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "spark-code-test", + "name": "frontend", "version": "0.0.0", "devDependencies": { "@sveltejs/vite-plugin-svelte": "^6.2.1", - "@tsconfig/svelte": "^5.0.5", - "@types/node": "^24.6.0", - "svelte": "^5.39.6", - "svelte-check": "^4.3.2", - "typescript": "~5.9.3", - "vite": "^7.1.7" + "svelte": "^5.43.5", + "vite": "^7.2.2" } }, "node_modules/@esbuild/aix-ppc64": { @@ -25,6 +21,7 @@ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "aix" @@ -41,6 +38,7 @@ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -57,6 +55,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -73,6 +72,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -89,6 +89,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -105,6 +106,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -121,6 +123,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -137,6 +140,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -153,6 +157,7 @@ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -169,6 +174,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -185,6 +191,7 @@ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -201,6 +208,7 @@ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -217,6 +225,7 @@ "mips64el" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -233,6 +242,7 @@ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -249,6 +259,7 @@ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -265,6 +276,7 @@ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -281,6 +293,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -297,6 +310,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -313,6 +327,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -329,6 +344,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -345,6 +361,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -361,6 +378,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openharmony" @@ -377,6 +395,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" @@ -393,6 +412,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -409,6 +429,7 @@ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -425,6 +446,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -438,6 +460,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" @@ -448,6 +471,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -458,6 +482,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } @@ -466,309 +491,334 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz", - "integrity": "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.5.tgz", - "integrity": "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.5.tgz", - "integrity": "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.5.tgz", - "integrity": "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.5.tgz", - "integrity": "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.5.tgz", - "integrity": "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.5.tgz", - "integrity": "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.5.tgz", - "integrity": "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.5.tgz", - "integrity": "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.5.tgz", - "integrity": "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.5.tgz", - "integrity": "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", "cpu": [ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.5.tgz", - "integrity": "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.5.tgz", - "integrity": "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.5.tgz", - "integrity": "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.5.tgz", - "integrity": "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz", - "integrity": "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.5.tgz", - "integrity": "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.5.tgz", - "integrity": "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openharmony" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.5.tgz", - "integrity": "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.5.tgz", - "integrity": "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.5.tgz", - "integrity": "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.5.tgz", - "integrity": "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@sveltejs/acorn-typescript": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.6.tgz", - "integrity": "sha512-4awhxtMh4cx9blePWl10HRHj8Iivtqj+2QdDCSMDzxG+XKa9+VCNupQuCuvzEhYPzZSrX+0gC+0lHA/0fFKKQQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.7.tgz", + "integrity": "sha512-znp1A/Y1Jj4l/Zy7PX5DZKBE0ZNY+5QBngiE21NJkfSTyzzC5iKNWOtwFXKtIrn7MXEFBck4jD95iBNkGjK92Q==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^8.9.0" } @@ -778,6 +828,8 @@ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.2.1.tgz", "integrity": "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==", "dev": true, + "license": "MIT", + "peer": true, "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "debug": "^4.4.1", @@ -798,6 +850,7 @@ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-5.0.1.tgz", "integrity": "sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.4.1" }, @@ -810,32 +863,20 @@ "vite": "^6.3.0 || ^7.0.0" } }, - "node_modules/@tsconfig/svelte": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@tsconfig/svelte/-/svelte-5.0.5.tgz", - "integrity": "sha512-48fAnUjKye38FvMiNOj0J9I/4XlQQiZlpe9xaNPfe8vy2Y1hFBt8g1yqf2EGjVvHavo4jf2lC+TQyENCr4BJBQ==", - "dev": true - }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true - }, - "node_modules/@types/node": { - "version": "24.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.0.tgz", - "integrity": "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==", "dev": true, - "dependencies": { - "undici-types": "~7.16.0" - } + "license": "MIT" }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, + "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -848,6 +889,7 @@ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">= 0.4" } @@ -857,30 +899,17 @@ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">= 0.4" } }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -890,6 +919,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -907,6 +937,7 @@ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -917,6 +948,7 @@ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -956,13 +988,15 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/esrap": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.1.2.tgz", - "integrity": "sha512-DgvlIQeowRNyvLPWW4PT7Gu13WznY288Du086E751mwwbsgr29ytBiYeLzAGIo0qk3Ujob0SDk8TiSaM5WQzNg==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.1.3.tgz", + "integrity": "sha512-T/Dhhv/QH+yYmiaLz9SA3PW+YyenlnRKDNdtlYJrSOBmNsH4nvPux+mTwx7p+wAedlJrGoZtXNI0a0MjQ2QkVg==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } @@ -972,6 +1006,7 @@ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.0.0" }, @@ -990,6 +1025,7 @@ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -1003,6 +1039,7 @@ "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "^1.0.6" } @@ -1011,31 +1048,25 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, - "node_modules/mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "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==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/nanoid": { "version": "3.3.11", @@ -1048,6 +1079,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -1059,13 +1091,16 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, + "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -1092,6 +1127,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -1101,24 +1137,12 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/rollup": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz", - "integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "1.0.8" }, @@ -1130,57 +1154,48 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.52.5", - "@rollup/rollup-android-arm64": "4.52.5", - "@rollup/rollup-darwin-arm64": "4.52.5", - "@rollup/rollup-darwin-x64": "4.52.5", - "@rollup/rollup-freebsd-arm64": "4.52.5", - "@rollup/rollup-freebsd-x64": "4.52.5", - "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", - "@rollup/rollup-linux-arm-musleabihf": "4.52.5", - "@rollup/rollup-linux-arm64-gnu": "4.52.5", - "@rollup/rollup-linux-arm64-musl": "4.52.5", - "@rollup/rollup-linux-loong64-gnu": "4.52.5", - "@rollup/rollup-linux-ppc64-gnu": "4.52.5", - "@rollup/rollup-linux-riscv64-gnu": "4.52.5", - "@rollup/rollup-linux-riscv64-musl": "4.52.5", - "@rollup/rollup-linux-s390x-gnu": "4.52.5", - "@rollup/rollup-linux-x64-gnu": "4.52.5", - "@rollup/rollup-linux-x64-musl": "4.52.5", - "@rollup/rollup-openharmony-arm64": "4.52.5", - "@rollup/rollup-win32-arm64-msvc": "4.52.5", - "@rollup/rollup-win32-ia32-msvc": "4.52.5", - "@rollup/rollup-win32-x64-gnu": "4.52.5", - "@rollup/rollup-win32-x64-msvc": "4.52.5", + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" } }, - "node_modules/sade": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", - "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", - "dev": true, - "dependencies": { - "mri": "^1.1.0" - }, - "engines": { - "node": ">=6" - } - }, "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==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/svelte": { - "version": "5.43.4", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.43.4.tgz", - "integrity": "sha512-tPNp21nDWB0PSHE+VrTvEy9cFtDp2Q+ATxQoFomISEVdikZ1QZ69UqBPz/LlT+Oc8/LYS/COYwDQZrmZEUr+JQ==", + "version": "5.43.12", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.43.12.tgz", + "integrity": "sha512-d1R+3pFa39LXoHCsxHmV//D2pSFZlEMlnxCVQ54TlrQv+4o5pewJO0/Pc5MUp+j71PJrOrPJHTvREZJHn+ymDQ==", "dev": true, + "license": "MIT", + "peer": true, "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", @@ -1201,34 +1216,12 @@ "node": ">=18" } }, - "node_modules/svelte-check": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.3.3.tgz", - "integrity": "sha512-RYP0bEwenDXzfv0P1sKAwjZSlaRyqBn0Fz1TVni58lqyEiqgwztTpmodJrGzP6ZT2aHl4MbTvWP6gbmQ3FOnBg==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "chokidar": "^4.0.1", - "fdir": "^6.2.0", - "picocolors": "^1.0.0", - "sade": "^1.7.4" - }, - "bin": { - "svelte-check": "bin/svelte-check" - }, - "engines": { - "node": ">= 18.0.0" - }, - "peerDependencies": { - "svelte": "^4.0.0 || ^5.0.0-next.0", - "typescript": ">=5.0.0" - } - }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, + "license": "MIT", "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" @@ -1240,30 +1233,13 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "dev": true - }, "node_modules/vite": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz", "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", "dev": true, + "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -1338,6 +1314,7 @@ "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.1.tgz", "integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==", "dev": true, + "license": "MIT", "workspaces": [ "tests/deps/*", "tests/projects/*", @@ -1356,7 +1333,8 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", "integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==", - "dev": true + "dev": true, + "license": "MIT" } } } diff --git a/frontend/package.json b/frontend/package.json index 5a9f815..b434fcd 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,24 +1,16 @@ { - "name": "spark-code-test", + "name": "frontend", "private": true, "version": "0.0.0", "type": "module", "scripts": { "dev": "vite", "build": "vite build", - "preview": "vite preview", - "check": "svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json" + "preview": "vite preview" }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "^6.2.1", - "@tsconfig/svelte": "^5.0.5", - "@types/node": "^24.6.0", - "svelte": "^5.39.6", - "svelte-check": "^4.3.2", - "typescript": "~5.9.3", - "vite": "^7.1.7" - }, - "engines": { - "node": ">=20" + "svelte": "^5.43.5", + "vite": "^7.2.2" } } diff --git a/frontend/public/sparklayer.svg b/frontend/public/sparklayer.svg deleted file mode 100644 index 07c2285..0000000 --- a/frontend/public/sparklayer.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - -
-
- - - - -
-
- - -
- - - - - -
diff --git a/frontend/public/vite.svg b/frontend/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/frontend/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index a0ca453..6014d06 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -1,74 +1,49 @@ - -
-
-

TODO

+
+
+

Task Board

+
+ + +
-
- {#each todos as todo} - - {/each} -
- -

Add a Todo

-
- - - -
+ {#if view === 'kanban'} + + {:else} + + {/if}
diff --git a/frontend/src/app.css b/frontend/src/app.css index 42296a2..e552c37 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -1,43 +1,102 @@ -@import url('reset.css'); - -button { - background-color: #326ac7; - color: white; +:root { + --bg: #ffffff; + --text: #1f2937; + --muted: #64748b; + --border: #e5e7eb; + --surface: #ffffff; + --surface-alt: #f8fafc; + --warn: #facc15; + --danger: #ef4444; + --card-bg: #ffffff; + --card-border: #e5e7eb; + --primary: #0ea5e9; + --primary-700: #0284c7; + --accent: #93c5fd; + --shadow: 0 1px 2px rgba(0,0,0,0.06), 0 2px 8px rgba(0,0,0,0.06); + font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + color: var(--text); + background-color: var(--bg); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} - font-size: 20px; - padding: 10px 20px; +a { + font-weight: 500; + color: var(--primary); + text-decoration: inherit; +} +a:hover { + color: var(--primary-700); +} - border-style: solid; - border-color: #779ad4; - border-width: 1px; - border-radius: 10px; +body { + margin: 0; + min-width: 320px; + min-height: 100vh; + background: var(--bg); + color: var(--text); } -button:hover { - background-color: #4a83e0; - transition-duration: 150ms; +h1 { + font-size: 2rem; + line-height: 1.1; } -button:active { - background-color: #5a93f0; +.card { + padding: 2em; } -input { - margin-bottom: 10px; - font-size: 20px; +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: left; +} - padding: 2px 5px; - color: white; - background-color: #303540; +button { + border-radius: 8px; + border: 1px solid var(--border); + padding: 0.5rem 0.8rem; + font-size: 0.95rem; + font-weight: 500; + font-family: inherit; + background-color: var(--surface-alt); + cursor: pointer; + transition: background-color 0.2s, border-color 0.2s, box-shadow 0.2s; +} +button:hover { + border-color: var(--primary); + box-shadow: var(--shadow); +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} - border-style: solid; - border-color: #464e61; - border-width: 1px; - border-radius: 5px; +input, select, textarea { + border: 1px solid var(--border); + border-radius: 8px; + padding: 0.5rem 0.6rem; + font-size: 0.95rem; + background: var(--surface); + color: var(--text); } -form { - display: flex; - flex-direction: column; - align-items: center; +.card { + padding: 2em; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 12px; + box-shadow: var(--shadow); } + +.columns::-webkit-scrollbar { height: 10px; } +.columns::-webkit-scrollbar-track { background: var(--surface-alt); } +.columns::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 8px; } + +/* Force light theme for readability across components */ + diff --git a/frontend/src/assets/svelte.svg b/frontend/src/assets/svelte.svg new file mode 100644 index 0000000..c5e0848 --- /dev/null +++ b/frontend/src/assets/svelte.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/lib/Counter.svelte b/frontend/src/lib/Counter.svelte new file mode 100644 index 0000000..770c922 --- /dev/null +++ b/frontend/src/lib/Counter.svelte @@ -0,0 +1,10 @@ + + + diff --git a/frontend/src/lib/Todo.svelte b/frontend/src/lib/Todo.svelte deleted file mode 100644 index 473bec6..0000000 --- a/frontend/src/lib/Todo.svelte +++ /dev/null @@ -1,57 +0,0 @@ - - -
-
-

{title}

-

{description}

-
-
- - diff --git a/frontend/src/lib/api/todo.ts b/frontend/src/lib/api/todo.ts new file mode 100644 index 0000000..534f1ae --- /dev/null +++ b/frontend/src/lib/api/todo.ts @@ -0,0 +1,19 @@ +export type Todo = { title: string; description: string }; + +const BASE_URL = import.meta.env.VITE_API_BASE_URL || "http://localhost:8080"; + +export async function getTodos(): Promise { + const res = await fetch(`${BASE_URL}/`, { method: "GET" }); + if (!res.ok) throw new Error(`Failed to fetch todos: ${res.status}`); + return res.json(); +} + +export async function createTodo(todo: Todo): Promise { + const res = await fetch(`${BASE_URL}/`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(todo), + }); + if (!res.ok) throw new Error(`Failed to create todo: ${res.status}`); + return res.json(); +} \ No newline at end of file diff --git a/frontend/src/lib/components/CalendarView.svelte b/frontend/src/lib/components/CalendarView.svelte new file mode 100644 index 0000000..4d58a1c --- /dev/null +++ b/frontend/src/lib/components/CalendarView.svelte @@ -0,0 +1,242 @@ + + +
+
+ +

{current.toLocaleString(undefined, { month: 'long', year: 'numeric' })}

+ +
+ +
+ {#each ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'] as wd} +
{wd}
+ {/each} +
+ +
+ {#each cells as d} +
{ draggingOverDateISO = toISODate(d); }} + on:dragleave={() => (draggingOverDateISO = null)} + on:drop={() => { if (draggingId) { taskboard.updateTask(draggingId, { deadline: toISODate(d) }); draggingId = null; draggingOverDateISO = null; } }}> +
{d.getDate()}
+
+ {#each tasks.filter(t => t.deadline === toISODate(d)) as t} +
(draggingId = t.id)} + on:dragend={() => (draggingId = null)} + on:click={() => openDetails(t)} + on:keydown={(e) => { if (e.key === 'Enter' || e.key === ' ') openDetails(t); }}> + +
{t.name}
+
+ {/each} + + {#if addingDateISO === toISODate(d)} +
+ + +
+ + +
+
+ {/if} +
+
+ {/each} +
+
+ +{#if selectedTask} +
+ +{/if} + + diff --git a/frontend/src/lib/components/Kanban.svelte b/frontend/src/lib/components/Kanban.svelte new file mode 100644 index 0000000..642a132 --- /dev/null +++ b/frontend/src/lib/components/Kanban.svelte @@ -0,0 +1,346 @@ + + +
+ +
+ {#each columns as col} +
(draggingOverColumn = col.id)} + on:dragleave={() => (draggingOverColumn = null)} + on:dragover|preventDefault={(e) => { + draggingOverColumn = col.id; + if (e.dataTransfer) e.dataTransfer.dropEffect = 'move'; + }} + on:drop={() => { + if (draggingId) { + const list = tasks + .filter((x) => x.columnId === col.id) + .sort((a, b) => (a.position ?? 0) - (b.position ?? 0)); + moveTaskAtIndex(draggingId, col.id, list.length); + draggingId = null; + draggingOverColumn = null; + } + }} + > +
+ {#if renamingId === col.id} + e.key === 'Enter' && saveRenameColumn(col.id)} /> +
+ + +
+ {:else} +

{col.name}

+
+ + + +
+ {/if} +
+ + {#if showTaskFormFor === col.id} +
+ + +
+ + +
+
+ + +
+
+ {/if} + +
(draggingOverColumn = col.id)} on:dragleave={() => (draggingOverColumn = null)} on:dragover|preventDefault={(e) => { draggingOverColumn = col.id; if (e.dataTransfer) e.dataTransfer.dropEffect = 'move'; }} on:drop={() => { if (draggingId) { const list = tasks.filter((x) => x.columnId === col.id).sort((a,b)=> (a.position ?? 0) - (b.position ?? 0)); moveTaskAtIndex(draggingId, col.id, list.length); draggingId = null; draggingOverColumn = null; } }}> + {#each tasks.filter((t) => t.columnId === col.id).sort((a,b)=> (a.position ?? 0) - (b.position ?? 0)) as t (t.id)} +
{ if (editingId !== t.id) openEdit(t); }} + on:keydown={(e) => { if ((e.key === 'Enter' || e.key === ' ') && editingId !== t.id) openEdit(t); }} + on:dragstart={(e) => { draggingId = t.id; if (e.dataTransfer) { e.dataTransfer.setData('text/plain', t.id); e.dataTransfer.effectAllowed = 'move'; } }} + on:dragend={() => { draggingId = null; draggingOverColumn = null; }} + on:dragover|preventDefault={(e) => { if (e.dataTransfer) e.dataTransfer.dropEffect = 'move'; e.stopPropagation(); }} + on:drop={(e) => { e.stopPropagation(); const rect = e.currentTarget.getBoundingClientRect(); const before = (e.clientY - rect.top) < rect.height / 2; const list = tasks.filter((x) => x.columnId === col.id).sort((a,b)=> (a.position ?? 0) - (b.position ?? 0)); const idx = list.findIndex((x) => x.id === t.id); const targetIndex = before ? idx : idx + 1; if (draggingId) moveTaskAtIndex(draggingId, col.id, targetIndex); draggingId = null; draggingOverColumn = null; }}> + {#if editingId === t.id} +
+ + +
+ + +
+
+ + +
+
+ {:else} + +
+

{t.name}

+ {#if t.description} +

{t.description}

+ {/if} +
+
+ {t.priority} + {#if t.deadline} + {t.deadline} + {/if} +
+
+ {/if} +
+ {/each} +
+
+ {/each} + +
(draggingOverColumn = null)} + on:dragleave={() => (draggingOverColumn = null)} + on:dragover|preventDefault={(e) => { if (e.dataTransfer) e.dataTransfer.dropEffect = 'none'; }} + > + +
+
+ {#if columns.length === 0} +

No columns yet — add one to get started.

+ {/if} +
+ + diff --git a/frontend/src/lib/stores/taskboard.js b/frontend/src/lib/stores/taskboard.js new file mode 100644 index 0000000..0d5fcd6 --- /dev/null +++ b/frontend/src/lib/stores/taskboard.js @@ -0,0 +1,107 @@ +import { writable } from 'svelte/store'; + +const API_BASE = (import.meta?.env?.VITE_API_BASE_URL) || 'http://localhost:8080'; + +const DEFAULT = { columns: [], tasks: [] }; + +const persisted = (() => { + try { + const raw = localStorage.getItem('taskboard_v1'); + return raw ? JSON.parse(raw) : DEFAULT; + } catch { + return DEFAULT; + } +})(); + +function uid() { + return Math.random().toString(36).slice(2, 9) + Date.now().toString(36); +} + +function createStore() { + const { subscribe, update, set } = writable(persisted); + // Initial load from backend + (async () => { + try { + const [colsRes, tasksRes] = await Promise.all([ + fetch(`${API_BASE}/api/kanban`), + fetch(`${API_BASE}/api/tasks`) + ]); + const cols = await colsRes.json(); + const tasks = await tasksRes.json(); + set({ columns: cols.map(c=>({ id: c.id, name: c.title })), tasks: tasks.map(t=>({ id: t.id, name: t.name, description: t.description, priority: t.priority, deadline: t.deadline, columnId: t.status_id, position: t.position })) }); + } catch (e) { + // Fallback to persisted/local if backend not available + try { const raw = localStorage.getItem('taskboard_v1'); if (raw) set(JSON.parse(raw)); } catch {} + } + })(); + + // Persist to localStorage as a cache + subscribe((value) => { try { localStorage.setItem('taskboard_v1', JSON.stringify(value)); } catch {} }); + + return { + subscribe, + async addColumn(name) { + const trimmed = (name ?? '').trim(); + const res = await fetch(`${API_BASE}/api/kanban`, { method: 'POST', headers: { 'Content-Type':'application/json' }, body: JSON.stringify({ title: trimmed }) }); + const created = await res.json(); + update((s) => { s.columns = [...s.columns, { id: created.id, name: created.title }]; return s; }); + return created.id; + }, + async renameColumn(id, name) { + const trimmed = name?.trim(); + await fetch(`${API_BASE}/api/kanban/${id}`, { method: 'PATCH', headers: { 'Content-Type':'application/json' }, body: JSON.stringify({ title: trimmed }) }); + update((s) => { s.columns = s.columns.map((c) => (c.id === id ? { ...c, name: trimmed || c.name } : c)); return s; }); + }, + async removeColumn(id) { + await fetch(`${API_BASE}/api/kanban/${id}`, { method: 'DELETE' }); + update((s) => { s.columns = s.columns.filter((c) => c.id !== id); s.tasks = s.tasks.filter((t) => t.columnId !== id); return s; }); + }, + async addTask({ name, description, priority, deadline, columnId }) { + const res = await fetch(`${API_BASE}/api/tasks`, { method: 'POST', headers: { 'Content-Type':'application/json' }, body: JSON.stringify({ name, description, priority, deadline, status_id: columnId }) }); + const created = await res.json(); + update((s) => { s.tasks = [...s.tasks, { id: created.id, name: created.name, description: created.description, priority: created.priority, deadline: created.deadline, columnId: created.status_id, position: created.position }]; return s; }); + }, + async updateTask(id, patch) { + await fetch(`${API_BASE}/api/tasks/${id}`, { method: 'PATCH', headers: { 'Content-Type':'application/json' }, body: JSON.stringify({ name: patch.name, description: patch.description, priority: patch.priority, deadline: patch.deadline }) }); + update((s) => { s.tasks = s.tasks.map((t) => (t.id === id ? { ...t, ...patch } : t)); return s; }); + }, + async moveTask(id, columnId) { + await fetch(`${API_BASE}/api/tasks/${id}`, { method: 'PATCH', headers: { 'Content-Type':'application/json' }, body: JSON.stringify({ status_id: columnId }) }); + update((s) => { + const moving = s.tasks.find((t) => t.id === id); + if (!moving) return s; + const colTasks = s.tasks.filter((x) => x.columnId === columnId && x.id !== id); + const maxPos = colTasks.length ? Math.max(...colTasks.map((x) => x.position ?? 0)) : -1; + const updated = { ...moving, columnId, position: maxPos + 1 }; + s.tasks = s.tasks.map((t) => (t.id === id ? updated : t)); + return s; + }); + }, + async moveTaskToPosition(id, columnId, index) { + await fetch(`${API_BASE}/api/tasks/${id}`, { method: 'PATCH', headers: { 'Content-Type':'application/json' }, body: JSON.stringify({ status_id: columnId, position: index }) }); + update((s) => { + const moving = s.tasks.find((x) => x.id === id); + if (!moving) return s; + const ordered = s.tasks + .filter((x) => x.columnId === columnId && x.id !== id) + .sort((a, b) => (a.position ?? 0) - (b.position ?? 0)); + const clampedIndex = Math.max(0, Math.min(index, ordered.length)); + const moved = { ...moving, columnId }; + ordered.splice(clampedIndex, 0, moved); + ordered.forEach((x, i) => (x.position = i)); + const others = s.tasks.filter((x) => x.columnId !== columnId && x.id !== id); + s.tasks = others.concat(ordered); + return s; + }); + }, + async removeTask(id) { + await fetch(`${API_BASE}/api/tasks/${id}`, { method: 'DELETE' }); + update((s) => { s.tasks = s.tasks.filter((t) => t.id !== id); return s; }); + }, + clearAll() { + set(DEFAULT); + } + }; +} + +export const taskboard = createStore(); \ No newline at end of file diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts deleted file mode 100644 index 7dbff87..0000000 --- a/frontend/src/lib/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type TodoItem = { - title: string; - description: string; -}; diff --git a/frontend/src/main.ts b/frontend/src/main.js similarity index 75% rename from frontend/src/main.ts rename to frontend/src/main.js index 664a057..458c7a8 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.js @@ -3,7 +3,7 @@ import './app.css' import App from './App.svelte' const app = mount(App, { - target: document.getElementById('app')!, + target: document.getElementById('app'), }) export default app diff --git a/frontend/src/reset.css b/frontend/src/reset.css deleted file mode 100644 index ed11813..0000000 --- a/frontend/src/reset.css +++ /dev/null @@ -1,48 +0,0 @@ -/* http://meyerweb.com/eric/tools/css/reset/ - v2.0 | 20110126 - License: none (public domain) -*/ - -html, body, div, span, applet, object, iframe, -h1, h2, h3, h4, h5, h6, p, blockquote, pre, -a, abbr, acronym, address, big, cite, code, -del, dfn, em, img, ins, kbd, q, s, samp, -small, strike, strong, sub, sup, tt, var, -b, u, i, center, -dl, dt, dd, ol, ul, li, -fieldset, form, label, legend, -table, caption, tbody, tfoot, thead, tr, th, td, -article, aside, canvas, details, embed, -figure, figcaption, footer, header, hgroup, -menu, nav, output, ruby, section, summary, -time, mark, audio, video { - margin: 0; - padding: 0; - border: 0; - font-size: 100%; - font: inherit; - vertical-align: baseline; -} -/* HTML5 display-role reset for older browsers */ -article, aside, details, figcaption, figure, -footer, header, hgroup, menu, nav, section { - display: block; -} -body { - line-height: 1; -} -ol, ul { - list-style: none; -} -blockquote, q { - quotes: none; -} -blockquote:before, blockquote:after, -q:before, q:after { - content: ''; - content: none; -} -table { - border-collapse: collapse; - border-spacing: 0; -} diff --git a/frontend/svelte.config.js b/frontend/svelte.config.js index 96b3455..9e07a84 100644 --- a/frontend/svelte.config.js +++ b/frontend/svelte.config.js @@ -1,8 +1,3 @@ -import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' - -/** @type {import("@sveltejs/vite-plugin-svelte").SvelteConfig} */ export default { - // Consult https://svelte.dev/docs#compile-time-svelte-preprocess - // for more information about preprocessors - preprocess: vitePreprocess(), + preprocess: [], } diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json deleted file mode 100644 index 31c18cf..0000000 --- a/frontend/tsconfig.app.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "extends": "@tsconfig/svelte/tsconfig.json", - "compilerOptions": { - "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", - "target": "ES2022", - "useDefineForClassFields": true, - "module": "ESNext", - "types": ["svelte", "vite/client"], - "noEmit": true, - /** - * Typecheck JS in `.svelte` and `.js` files by default. - * Disable checkJs if you'd like to use dynamic types in JS. - * Note that setting allowJs false does not prevent the use - * of JS in `.svelte` files. - */ - "allowJs": true, - "checkJs": true, - "moduleDetection": "force" - }, - "include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte"] -} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json deleted file mode 100644 index 1ffef60..0000000 --- a/frontend/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "files": [], - "references": [ - { "path": "./tsconfig.app.json" }, - { "path": "./tsconfig.node.json" } - ] -} diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json deleted file mode 100644 index 8a67f62..0000000 --- a/frontend/tsconfig.node.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "compilerOptions": { - "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", - "target": "ES2023", - "lib": ["ES2023"], - "module": "ESNext", - "types": ["node"], - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, - "moduleDetection": "force", - "noEmit": true, - - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "erasableSyntaxOnly": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedSideEffectImports": true - }, - "include": ["vite.config.ts"] -} diff --git a/frontend/vite.config.ts b/frontend/vite.config.js similarity index 100% rename from frontend/vite.config.ts rename to frontend/vite.config.js