feat: release notes (#298)
This commit is contained in:
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
name: commit
|
||||||
|
description: Create git commits following Conventional Commits format. Use when committing changes, creating commits, or when the user asks to commit. Ensures commit messages conform to the project's changelog generation conventions.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Commit
|
||||||
|
|
||||||
|
Create a git commit following this project's Conventional Commits convention. These messages are used by git-cliff (`cliff.toml`) to auto-generate changelogs and release notes. PRs are squash-merged, so the PR title becomes the commit on `main` — CI validates it via `pr-title.yml`.
|
||||||
|
|
||||||
|
## Format
|
||||||
|
|
||||||
|
```
|
||||||
|
type: lowercase description
|
||||||
|
```
|
||||||
|
|
||||||
|
## Allowed Types
|
||||||
|
|
||||||
|
| Type | Use for | In changelog? |
|
||||||
|
|------|---------|---------------|
|
||||||
|
| `feat` | New features | Yes (Features) |
|
||||||
|
| `fix` | Bug fixes | Yes (Bug Fixes) |
|
||||||
|
| `perf` | Performance improvements | Yes (Performance) |
|
||||||
|
| `refactor` | Code refactoring | Yes (Refactoring) |
|
||||||
|
| `docs` | Documentation changes | Yes (Documentation) |
|
||||||
|
| `chore` | Maintenance, deps, configs | No |
|
||||||
|
| `ci` | CI/CD changes | No |
|
||||||
|
| `test` | Test-only changes | No |
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
- Subject **must** start with a lowercase letter
|
||||||
|
- Scope is optional: `feat(ui): add dialog` is valid
|
||||||
|
- Include PR/issue number when relevant: `feat: add release notes (#295)`
|
||||||
|
- Breaking changes: add `!` after type: `feat!: remove legacy API`
|
||||||
|
- Keep the first line under 72 characters
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
feat: add release notes dialog
|
||||||
|
fix: flaky test in orchestrator (#292)
|
||||||
|
refactor: extract session handler into separate module
|
||||||
|
chore: update dependencies
|
||||||
|
ci: add PR title linting workflow
|
||||||
|
```
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
1. Run `git status` and `git diff` to understand all changes
|
||||||
|
2. Review recent commits with `git log --oneline -10` to match project style
|
||||||
|
3. Stage relevant files (prefer specific files over `git add -A`)
|
||||||
|
4. Write a commit message following the format above
|
||||||
|
5. If changes span multiple concerns, consider separate commits
|
||||||
|
6. After committing, run `make fmt` then `make lint` to verify
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../.agents/skills
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
name: pr-title
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, edited, synchronize, reopened]
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
pull-requests: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Validate PR title follows Conventional Commits
|
||||||
|
uses: amannn/action-semantic-pull-request@v5
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
# Allowed conventional commit types (must match cliff.toml commit_parsers)
|
||||||
|
types: |
|
||||||
|
feat
|
||||||
|
fix
|
||||||
|
perf
|
||||||
|
refactor
|
||||||
|
docs
|
||||||
|
chore
|
||||||
|
ci
|
||||||
|
test
|
||||||
|
release
|
||||||
|
# Require lowercase subject
|
||||||
|
subjectPattern: ^[a-z].*$
|
||||||
|
subjectPatternError: |
|
||||||
|
The PR title subject must start with a lowercase letter.
|
||||||
|
Example: "feat: add release notes dialog"
|
||||||
|
# Do not require a scope
|
||||||
|
requireScope: false
|
||||||
@@ -23,6 +23,8 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm -C apps install --frozen-lockfile
|
run: pnpm -C apps install --frozen-lockfile
|
||||||
- name: Build web (standalone)
|
- name: Build web (standalone)
|
||||||
|
env:
|
||||||
|
KANDEV_VERSION: ${{ github.ref_name }}
|
||||||
run: pnpm -C apps --filter @kandev/web build
|
run: pnpm -C apps --filter @kandev/web build
|
||||||
- name: Package web bundle
|
- name: Package web bundle
|
||||||
run: bash scripts/release/package-web.sh
|
run: bash scripts/release/package-web.sh
|
||||||
@@ -116,6 +118,18 @@ jobs:
|
|||||||
needs: build-bundles
|
needs: build-bundles
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Generate release notes
|
||||||
|
uses: orhun/git-cliff-action@v4
|
||||||
|
with:
|
||||||
|
config: cliff.toml
|
||||||
|
args: --latest --strip header
|
||||||
|
env:
|
||||||
|
OUTPUT: RELEASE_NOTES.md
|
||||||
|
|
||||||
- name: Download bundle artifacts
|
- name: Download bundle artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
@@ -144,6 +158,7 @@ jobs:
|
|||||||
- name: Publish release assets
|
- name: Publish release assets
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
|
body_path: RELEASE_NOTES.md
|
||||||
files: |
|
files: |
|
||||||
dist/release-assets/kandev-*.tar.gz
|
dist/release-assets/kandev-*.tar.gz
|
||||||
dist/release-assets/kandev-*.tar.gz.sha256
|
dist/release-assets/kandev-*.tar.gz.sha256
|
||||||
|
|||||||
@@ -1,4 +1,14 @@
|
|||||||
repos:
|
repos:
|
||||||
|
# Commit message validation (Conventional Commits via commitlint)
|
||||||
|
- repo: local
|
||||||
|
hooks:
|
||||||
|
- id: commitlint
|
||||||
|
name: Validate commit message (Conventional Commits)
|
||||||
|
entry: bash -c 'cd apps && pnpm exec commitlint --edit "$1"' --
|
||||||
|
language: system
|
||||||
|
stages: [commit-msg]
|
||||||
|
always_run: true
|
||||||
|
|
||||||
# Backend (Go) hooks
|
# Backend (Go) hooks
|
||||||
- repo: local
|
- repo: local
|
||||||
hooks:
|
hooks:
|
||||||
|
|||||||
@@ -78,13 +78,21 @@ apps/backend/
|
|||||||
│ ├── editors/ # Editor integration
|
│ ├── editors/ # Editor integration
|
||||||
│ ├── events/ # Event bus for internal pub/sub
|
│ ├── events/ # Event bus for internal pub/sub
|
||||||
│ ├── gateway/ # WebSocket gateway
|
│ ├── gateway/ # WebSocket gateway
|
||||||
|
│ ├── github/ # GitHub API integration (PRs, reviews, webhooks)
|
||||||
│ ├── integration/ # External integrations
|
│ ├── integration/ # External integrations
|
||||||
│ ├── lsp/ # LSP server
|
│ ├── lsp/ # LSP server
|
||||||
│ ├── mcp/ # MCP protocol support
|
│ ├── mcp/ # MCP protocol support
|
||||||
│ ├── notifications/ # Notification system
|
│ ├── notifications/ # Notification system
|
||||||
│ ├── persistence/ # Persistence layer
|
│ ├── persistence/ # Persistence layer
|
||||||
│ ├── prompts/ # Prompt management
|
│ ├── prompts/ # Prompt management
|
||||||
|
│ ├── repoclone/ # Repository cloning for remote executors
|
||||||
|
│ ├── scriptengine/ # Script placeholder resolution and interpolation
|
||||||
|
│ ├── secrets/ # Secret management
|
||||||
|
│ ├── sprites/ # Sprites AI integration
|
||||||
│ ├── sysprompt/ # System prompt injection
|
│ ├── sysprompt/ # System prompt injection
|
||||||
|
│ ├── task/
|
||||||
|
│ │ └── ...
|
||||||
|
│ ├── tools/ # Tool integrations
|
||||||
│ ├── user/ # User management
|
│ ├── user/ # User management
|
||||||
│ ├── workflow/ # Workflow engine
|
│ ├── workflow/ # Workflow engine
|
||||||
│ │ ├── engine/ # Typed state-machine engine (trigger evaluation, action callbacks, transition store)
|
│ │ ├── engine/ # Typed state-machine engine (trigger evaluation, action callbacks, transition store)
|
||||||
@@ -173,7 +181,7 @@ Client (WS) Orchestrator Lifecycle Manager Runtime age
|
|||||||
|
|
||||||
**Worktrees:** `internal/worktree/Manager` provides workspace isolation. Each session can have its own worktree (branch) to prevent conflicts between concurrent agents.
|
**Worktrees:** `internal/worktree/Manager` provides workspace isolation. Each session can have its own worktree (branch) to prevent conflicts between concurrent agents.
|
||||||
|
|
||||||
**Executor default scripts:** Default prepare scripts are owned by executor lifecycle code (`internal/agent/lifecycle/default_scripts.go`), while `internal/scriptengine/` handles placeholder resolution and interpolation.
|
**Executor default scripts:** Default prepare scripts are in `internal/agent/lifecycle/default_scripts.go`; `internal/scriptengine/` handles placeholder resolution.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -256,15 +264,16 @@ lib/state/
|
|||||||
│ ├── session-runtime/ # shell, processes, git, context
|
│ ├── session-runtime/ # shell, processes, git, context
|
||||||
│ ├── workspace/ # workspaces, repos, branches
|
│ ├── workspace/ # workspaces, repos, branches
|
||||||
│ ├── settings/ # executors, agents, editors, prompts
|
│ ├── settings/ # executors, agents, editors, prompts
|
||||||
│ ├── diff-comments/ # code review diff comments
|
│ ├── comments/ # code review diff comments
|
||||||
|
│ ├── github/ # GitHub PRs, reviews
|
||||||
│ └── ui/ # preview, connection, active state
|
│ └── ui/ # preview, connection, active state
|
||||||
├── hydration/ # SSR merge strategies
|
├── hydration/ # SSR merge strategies
|
||||||
|
|
||||||
hooks/domains/{kanban,session,workspace,settings}/ # Domain-organized hooks
|
hooks/domains/{kanban,session,workspace,settings}/ # Domain-organized hooks
|
||||||
lib/api/domains/ # API clients
|
lib/api/domains/ # API clients
|
||||||
├── kanban-api, session-api, workspace-api, settings-api, process-api
|
├── kanban-api, session-api, workspace-api, settings-api, process-api
|
||||||
├── plan-api, queue-api, workflow-api, stats-api
|
├── plan-api, queue-api, workflow-api, stats-api, github-api
|
||||||
├── user-shell-api, debug-api
|
├── user-shell-api, debug-api, secrets-api, sprites-api, vscode-api
|
||||||
```
|
```
|
||||||
|
|
||||||
**Key State Paths:**
|
**Key State Paths:**
|
||||||
@@ -284,6 +293,10 @@ lib/api/domains/ # API clients
|
|||||||
|
|
||||||
## Best Practices
|
## Best Practices
|
||||||
|
|
||||||
|
### Commit Conventions (enforced by CI)
|
||||||
|
|
||||||
|
Commits to `main` **must** follow [Conventional Commits](https://www.conventionalcommits.org/) (`type: description`). PRs are squash-merged — the PR title becomes the commit, validated by CI. Changelog is auto-generated from these via git-cliff (`cliff.toml`). See `.agents/skills/commit/SKILL.md` for allowed types and examples.
|
||||||
|
|
||||||
### Code Quality (enforced by linters)
|
### Code Quality (enforced by linters)
|
||||||
|
|
||||||
Static analysis runs in CI and pre-commit. New code **must** stay within these limits:
|
Static analysis runs in CI and pre-commit. New code **must** stay within these limits:
|
||||||
@@ -331,4 +344,4 @@ This file is read by AI coding agents (Claude Code via `CLAUDE.md` symlink, Code
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Last Updated**: 2026-02-22
|
**Last Updated**: 2026-02-26
|
||||||
|
|||||||
+547
@@ -0,0 +1,547 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to Kandev.
|
||||||
|
|
||||||
|
## 0.7 - 2026-02-26
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- flaky test ([#292](https://github.com/kdlbs/kandev/pull/292))
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- improve vscode cleanup ([#294](https://github.com/kdlbs/kandev/pull/294))
|
||||||
|
- mermaid support ([#293](https://github.com/kdlbs/kandev/pull/293))
|
||||||
|
|
||||||
|
## 0.6 - 2026-02-26
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- duplicated start message ([#289](https://github.com/kdlbs/kandev/pull/289))
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- improve workflow auto start ([#291](https://github.com/kdlbs/kandev/pull/291))
|
||||||
|
- improve layout manager ([#290](https://github.com/kdlbs/kandev/pull/290))
|
||||||
|
|
||||||
|
## 0.5 - 2026-02-26
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- pr layout after start ([#288](https://github.com/kdlbs/kandev/pull/288))
|
||||||
|
- improve PR review watcher + PR info panel ([#281](https://github.com/kdlbs/kandev/pull/281))
|
||||||
|
- open plan panel if agent writes to it ([#287](https://github.com/kdlbs/kandev/pull/287))
|
||||||
|
- clean up on remote session failure ([#286](https://github.com/kdlbs/kandev/pull/286))
|
||||||
|
|
||||||
|
## 0.4 - 2026-02-25
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- vscode not being killed ([#284](https://github.com/kdlbs/kandev/pull/284))
|
||||||
|
- agents stuck on starting after restart ([#283](https://github.com/kdlbs/kandev/pull/283))
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- claude code auth setup for remote executors ([#285](https://github.com/kdlbs/kandev/pull/285))
|
||||||
|
- reduce sql queries amount ([#282](https://github.com/kdlbs/kandev/pull/282))
|
||||||
|
|
||||||
|
## 0.3 - 2026-02-25
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- improve cli startup wait
|
||||||
|
|
||||||
|
## 0.2 - 2026-02-25
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- use github.com for releases instead of api to avoid rate limiting
|
||||||
|
- add login verification to release script
|
||||||
|
|
||||||
|
## 0.1 - 2026-02-25
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- vscode ([#279](https://github.com/kdlbs/kandev/pull/279))
|
||||||
|
- layout switching messing panels ([#278](https://github.com/kdlbs/kandev/pull/278))
|
||||||
|
- worktree folder removal ([#277](https://github.com/kdlbs/kandev/pull/277))
|
||||||
|
- make dev use local db ([#276](https://github.com/kdlbs/kandev/pull/276))
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- improve release script
|
||||||
|
- add guard in workflow engine
|
||||||
|
- chat improvements ([#280](https://github.com/kdlbs/kandev/pull/280))
|
||||||
|
- default github runners ([#275](https://github.com/kdlbs/kandev/pull/275))
|
||||||
|
|
||||||
|
## 0.0.12 - 2026-02-24
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- make github release atomic
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- improve release script
|
||||||
|
|
||||||
|
## 0.0.11 - 2026-02-24
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- upgrade cli version
|
||||||
|
- add release version to cli
|
||||||
|
- improve docker logging
|
||||||
|
- remove unnecessary files
|
||||||
|
|
||||||
|
## 0.0.10 - 2026-02-24
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- enforce sidebar max-width via dockview group constraints ([#274](https://github.com/kdlbs/kandev/pull/274))
|
||||||
|
- resolve all web app ESLint linter warnings ([#246](https://github.com/kdlbs/kandev/pull/246))
|
||||||
|
- resolve all backend golangci-lint violations ([#245](https://github.com/kdlbs/kandev/pull/245))
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- add github integration (pr watcher) ([#262](https://github.com/kdlbs/kandev/pull/262))
|
||||||
|
- update and review main documentation ([#259](https://github.com/kdlbs/kandev/pull/259))
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- upgrade cli
|
||||||
|
- add session-state sections to task switcher sidebar ([#273](https://github.com/kdlbs/kandev/pull/273))
|
||||||
|
- several UX improvements ([#272](https://github.com/kdlbs/kandev/pull/272))
|
||||||
|
- improve workflows and agent resume ([#269](https://github.com/kdlbs/kandev/pull/269))
|
||||||
|
- improve claude code normalized messages + review ux ([#271](https://github.com/kdlbs/kandev/pull/271))
|
||||||
|
- pr watcher user or team review ([#270](https://github.com/kdlbs/kandev/pull/270))
|
||||||
|
- improve remote executor sprites.dev ([#267](https://github.com/kdlbs/kandev/pull/267))
|
||||||
|
- remove executor healthcheck ([#263](https://github.com/kdlbs/kandev/pull/263))
|
||||||
|
- refactor big repository ([#261](https://github.com/kdlbs/kandev/pull/261))
|
||||||
|
- improve sql queries ([#260](https://github.com/kdlbs/kandev/pull/260))
|
||||||
|
- remote executors + secrets ([#257](https://github.com/kdlbs/kandev/pull/257))
|
||||||
|
- opencode acp
|
||||||
|
- opencode improve sse
|
||||||
|
- improve amp
|
||||||
|
- e2e tests
|
||||||
|
- improve claude code
|
||||||
|
- vscode integration ([#256](https://github.com/kdlbs/kandev/pull/256))
|
||||||
|
- improve acp + tracing ([#258](https://github.com/kdlbs/kandev/pull/258))
|
||||||
|
- improve ux ([#254](https://github.com/kdlbs/kandev/pull/254))
|
||||||
|
- improved dockview layouts ([#253](https://github.com/kdlbs/kandev/pull/253))
|
||||||
|
- improve backend handlers ([#252](https://github.com/kdlbs/kandev/pull/252))
|
||||||
|
- tui agents db ([#251](https://github.com/kdlbs/kandev/pull/251))
|
||||||
|
- add SQLite single-writer/multi-reader connection pool ([#250](https://github.com/kdlbs/kandev/pull/250))
|
||||||
|
- improve plan comments ([#249](https://github.com/kdlbs/kandev/pull/249))
|
||||||
|
- command panel search files ([#248](https://github.com/kdlbs/kandev/pull/248))
|
||||||
|
- improve comment system ([#247](https://github.com/kdlbs/kandev/pull/247))
|
||||||
|
- import export workflows ([#244](https://github.com/kdlbs/kandev/pull/244))
|
||||||
|
- update readme & agent TUI reliability ([#239](https://github.com/kdlbs/kandev/pull/239))
|
||||||
|
- add search functionality ([#243](https://github.com/kdlbs/kandev/pull/243))
|
||||||
|
- add more file editor keybindings ([#242](https://github.com/kdlbs/kandev/pull/242))
|
||||||
|
- improve monaco comments ([#241](https://github.com/kdlbs/kandev/pull/241))
|
||||||
|
- improve db abstraction ([#238](https://github.com/kdlbs/kandev/pull/238))
|
||||||
|
- add support for adding files ([#240](https://github.com/kdlbs/kandev/pull/240))
|
||||||
|
- improved passthrough and workflow ([#237](https://github.com/kdlbs/kandev/pull/237))
|
||||||
|
- ci complexity linters ([#236](https://github.com/kdlbs/kandev/pull/236))
|
||||||
|
- homelab-runner ([#233](https://github.com/kdlbs/kandev/pull/233))
|
||||||
|
- command panel ([#235](https://github.com/kdlbs/kandev/pull/235))
|
||||||
|
- improve changes panel ([#234](https://github.com/kdlbs/kandev/pull/234))
|
||||||
|
- archive tasks ([#232](https://github.com/kdlbs/kandev/pull/232))
|
||||||
|
- improve agent sort ([#231](https://github.com/kdlbs/kandev/pull/231))
|
||||||
|
- improve onboarding dialog ([#230](https://github.com/kdlbs/kandev/pull/230))
|
||||||
|
- improve stepper ([#229](https://github.com/kdlbs/kandev/pull/229))
|
||||||
|
- improve workflows ([#228](https://github.com/kdlbs/kandev/pull/228))
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
|
||||||
|
- optimize settings page load ([#268](https://github.com/kdlbs/kandev/pull/268))
|
||||||
|
|
||||||
|
### Style
|
||||||
|
|
||||||
|
- fix format issues ([#255](https://github.com/kdlbs/kandev/pull/255))
|
||||||
|
|
||||||
|
## 0.0.9 - 2026-02-16
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- reduce bundle size
|
||||||
|
|
||||||
|
## 0.0.8 - 2026-02-16
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- release bundle
|
||||||
|
|
||||||
|
## 0.0.7 - 2026-02-16
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- bundle web assets
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- improved stats ([#227](https://github.com/kdlbs/kandev/pull/227))
|
||||||
|
|
||||||
|
## 0.0.6 - 2026-02-16
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- bundle all web assets
|
||||||
|
|
||||||
|
## 0.0.5 - 2026-02-16
|
||||||
|
|
||||||
|
## 0.0.4 - 2026-02-16
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- bundle
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- use tar for bundles
|
||||||
|
- use tar for bundles
|
||||||
|
- improve editors ([#226](https://github.com/kdlbs/kandev/pull/226))
|
||||||
|
|
||||||
|
## 0.0.3 - 2026-02-15
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- release build
|
||||||
|
|
||||||
|
## 0.0.2 - 2026-02-15
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- sha lowercase comparison
|
||||||
|
- github release download when github token is present
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- improve windows support
|
||||||
|
- fix cli org
|
||||||
|
|
||||||
|
## 0.0.1 - 2026-02-15
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- resolve session stuck issues and workflow transition bugs ([#206](https://github.com/kdlbs/kandev/pull/206))
|
||||||
|
- copilot mcp ([#217](https://github.com/kdlbs/kandev/pull/217))
|
||||||
|
- complete tool calls on turn end ([#216](https://github.com/kdlbs/kandev/pull/216))
|
||||||
|
- start ws disconnected
|
||||||
|
- make start without public folder
|
||||||
|
- 2 cumulative diff
|
||||||
|
- cumulative diff poll
|
||||||
|
- pass MCP configuration to Claude Code via --mcp-config flag ([#205](https://github.com/kdlbs/kandev/pull/205))
|
||||||
|
- prevent user shell terminals from prematurely completing agent tasks ([#199](https://github.com/kdlbs/kandev/pull/199))
|
||||||
|
- refetch git status when switching back to a previously viewed task ([#198](https://github.com/kdlbs/kandev/pull/198))
|
||||||
|
- resume failed task sessions instead of returning errors ([#197](https://github.com/kdlbs/kandev/pull/197))
|
||||||
|
- cancel button always disabled when agent is running ([#195](https://github.com/kdlbs/kandev/pull/195))
|
||||||
|
- diff bg lines and codex last message ([#186](https://github.com/kdlbs/kandev/pull/186))
|
||||||
|
- prevent duplicate agent messages in database ([#181](https://github.com/kdlbs/kandev/pull/181))
|
||||||
|
- prevent duplicate message submission while agent is working ([#180](https://github.com/kdlbs/kandev/pull/180))
|
||||||
|
- resolve notification ordering race condition ([#179](https://github.com/kdlbs/kandev/pull/179))
|
||||||
|
- use detached context for ask_user_question MCP tool ([#178](https://github.com/kdlbs/kandev/pull/178))
|
||||||
|
- rollback from review step works on simple boards ([#177](https://github.com/kdlbs/kandev/pull/177))
|
||||||
|
- wire permission handler to Copilot SDK ([#161](https://github.com/kdlbs/kandev/pull/161))
|
||||||
|
- workaround shiki Go grammar catastrophic backtracking ([#174](https://github.com/kdlbs/kandev/pull/174))
|
||||||
|
- db path + open workspace folder ([#169](https://github.com/kdlbs/kandev/pull/169))
|
||||||
|
- agent profile creation ([#170](https://github.com/kdlbs/kandev/pull/170))
|
||||||
|
- show approve button wrongly + improve plan ux ([#158](https://github.com/kdlbs/kandev/pull/158))
|
||||||
|
- git status tracking - detect staging changes and persist in snapshots ([#149](https://github.com/kdlbs/kandev/pull/149))
|
||||||
|
- hide 'Approval Required' badge when agent is working ([#148](https://github.com/kdlbs/kandev/pull/148))
|
||||||
|
- disable Cmd+Enter keyboard shortcut when chat input is disabled ([#146](https://github.com/kdlbs/kandev/pull/146))
|
||||||
|
- prevent workflow step regression on follow-up prompts ([#143](https://github.com/kdlbs/kandev/pull/143))
|
||||||
|
- Update session/task states when ask_user_question tool is used ([#142](https://github.com/kdlbs/kandev/pull/142))
|
||||||
|
- Fix cache keying bug and repository locks memory leak ([#138](https://github.com/kdlbs/kandev/pull/138))
|
||||||
|
- permission request ID mismatch, SSE duplicates, and subprocess cleanup ([#137](https://github.com/kdlbs/kandev/pull/137))
|
||||||
|
- improve ask_user_question MCP tool with clear options format ([#125](https://github.com/kdlbs/kandev/pull/125))
|
||||||
|
- session recovery after backend restart ([#126](https://github.com/kdlbs/kandev/pull/126))
|
||||||
|
- use correct sandbox_mode to enable file editing ([#129](https://github.com/kdlbs/kandev/pull/129))
|
||||||
|
- Multiple bug fixes for agent lifecycle and task creation ([#122](https://github.com/kdlbs/kandev/pull/122))
|
||||||
|
- WebSocket race condition in session hooks and chat input performance ([#119](https://github.com/kdlbs/kandev/pull/119))
|
||||||
|
- prevent approval button showing while agent is working ([#117](https://github.com/kdlbs/kandev/pull/117))
|
||||||
|
- fix alignment in board creation ([#112](https://github.com/kdlbs/kandev/pull/112))
|
||||||
|
- refetch messages and git status when switching between tasks ([#110](https://github.com/kdlbs/kandev/pull/110))
|
||||||
|
- prevent WebSocket timeout from canceling long-running agent operations ([#109](https://github.com/kdlbs/kandev/pull/109))
|
||||||
|
- synchronous event bus dispatch with regression tests ([#106](https://github.com/kdlbs/kandev/pull/106))
|
||||||
|
- git status not showing after page refresh ([#101](https://github.com/kdlbs/kandev/pull/101))
|
||||||
|
- increase prompt timeout to 60min, add error feedback, rename acp to agent API ([#97](https://github.com/kdlbs/kandev/pull/97))
|
||||||
|
- strip origin/ prefix from base branch for rebase/merge operations ([#94](https://github.com/kdlbs/kandev/pull/94))
|
||||||
|
- filter upstream commits, fix ahead/behind, and remove duplicate types ([#91](https://github.com/kdlbs/kandev/pull/91))
|
||||||
|
- model selector ([#90](https://github.com/kdlbs/kandev/pull/90))
|
||||||
|
- load most recent messages and fix lazy loading pagination ([#82](https://github.com/kdlbs/kandev/pull/82))
|
||||||
|
- use AGENTCTL_PORT env var for backend ControlClient ([#63](https://github.com/kdlbs/kandev/pull/63))
|
||||||
|
- bind KANDEV_AGENT_STANDALONE_PORT env var to config ([#61](https://github.com/kdlbs/kandev/pull/61))
|
||||||
|
- refactor shell streaming to use event bus pattern ([#50](https://github.com/kdlbs/kandev/pull/50))
|
||||||
|
- make Codex Prompt() synchronous to fix premature task state transition ([#35](https://github.com/kdlbs/kandev/pull/35))
|
||||||
|
- session state after restart
|
||||||
|
- improve session terminal and git status handling ([#34](https://github.com/kdlbs/kandev/pull/34))
|
||||||
|
- make dev ctrl+c cleanup
|
||||||
|
- remove extra state transitions during startup resume
|
||||||
|
- skip tool call update when no active session
|
||||||
|
- resolve deadlock in adapter by not holding mutex during RPC calls
|
||||||
|
- typescript errors
|
||||||
|
- address code review issues ([#16](https://github.com/kdlbs/kandev/pull/16))
|
||||||
|
- docker cleanup
|
||||||
|
- macos launcher
|
||||||
|
- standalone mode workspace path and worktree lookup ([#10](https://github.com/kdlbs/kandev/pull/10))
|
||||||
|
- go vet ci
|
||||||
|
- reconnect to agent streams after backend restart
|
||||||
|
- show worktree path in UI after agent starts
|
||||||
|
- only show active worktrees in task API response
|
||||||
|
- configure git safe.directory in agent containers
|
||||||
|
- mount entire .git directory for worktree support in containers
|
||||||
|
- mount git worktree metadata directory into container
|
||||||
|
- wire worktree manager to lifecycle manager for agent isolation
|
||||||
|
- recover agent state from Docker on backend restart
|
||||||
|
- filter out internal ACP messages from WebSocket broadcast
|
||||||
|
- fix real-time comments not appearing on first agent start
|
||||||
|
- go tests
|
||||||
|
- task page
|
||||||
|
- workspace migration
|
||||||
|
- Structure session_info event correctly for protocol.Message parsing
|
||||||
|
- Publish session_info event so session ID is stored in task metadata
|
||||||
|
- build
|
||||||
|
- publish session notifications to event bus for WebSocket streaming
|
||||||
|
- web linter issues
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- update WEBSOCKET_API.md with complete API reference
|
||||||
|
- Update AGENTS.md for WebSocket-only architecture
|
||||||
|
- Add comprehensive WebSocket API reference
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- improve windows support ([#225](https://github.com/kdlbs/kandev/pull/225))
|
||||||
|
- auggie dynamic model list ([#223](https://github.com/kdlbs/kandev/pull/223))
|
||||||
|
- better context files ([#222](https://github.com/kdlbs/kandev/pull/222))
|
||||||
|
- agents.json refactor ([#221](https://github.com/kdlbs/kandev/pull/221))
|
||||||
|
- improve task creation ([#220](https://github.com/kdlbs/kandev/pull/220))
|
||||||
|
- migrate agent operations from HTTP to WebSocket ([#218](https://github.com/kdlbs/kandev/pull/218))
|
||||||
|
- dockview new ui ([#219](https://github.com/kdlbs/kandev/pull/219))
|
||||||
|
- improve git pull ([#215](https://github.com/kdlbs/kandev/pull/215))
|
||||||
|
- remove blocking http call when creating the agent ([#214](https://github.com/kdlbs/kandev/pull/214))
|
||||||
|
- add agent boot message ([#213](https://github.com/kdlbs/kandev/pull/213))
|
||||||
|
- preventing process kill on port use
|
||||||
|
- increase agent boot timeout from 30s to 60s
|
||||||
|
- improve plan mode ([#212](https://github.com/kdlbs/kandev/pull/212))
|
||||||
|
- add ui debug on make start-debug
|
||||||
|
- favicon
|
||||||
|
- add make start-debug
|
||||||
|
- local executor and worktree + new task dialog ([#211](https://github.com/kdlbs/kandev/pull/211))
|
||||||
|
- review ux ([#210](https://github.com/kdlbs/kandev/pull/210))
|
||||||
|
- improve file tree ([#209](https://github.com/kdlbs/kandev/pull/209))
|
||||||
|
- mock agent ([#208](https://github.com/kdlbs/kandev/pull/208))
|
||||||
|
- queue messages ([#201](https://github.com/kdlbs/kandev/pull/201))
|
||||||
|
- file icons ([#207](https://github.com/kdlbs/kandev/pull/207))
|
||||||
|
- added message actions ([#203](https://github.com/kdlbs/kandev/pull/203))
|
||||||
|
- fix random port ([#202](https://github.com/kdlbs/kandev/pull/202))
|
||||||
|
- improve messages ux ([#193](https://github.com/kdlbs/kandev/pull/193))
|
||||||
|
- add Claude Opus 4.6 model support ([#194](https://github.com/kdlbs/kandev/pull/194))
|
||||||
|
- improve web fetch message ([#192](https://github.com/kdlbs/kandev/pull/192))
|
||||||
|
- Implement image paste functionality for Claude Code ([#188](https://github.com/kdlbs/kandev/pull/188))
|
||||||
|
- improve session terminals ([#185](https://github.com/kdlbs/kandev/pull/185))
|
||||||
|
- restore session when a worktree folder is deleted ([#184](https://github.com/kdlbs/kandev/pull/184))
|
||||||
|
- remove thinking selection ([#182](https://github.com/kdlbs/kandev/pull/182))
|
||||||
|
- improved chat input keybinding ([#173](https://github.com/kdlbs/kandev/pull/173))
|
||||||
|
- improve diff colors ([#171](https://github.com/kdlbs/kandev/pull/171))
|
||||||
|
- Add force push option to git push menu ([#167](https://github.com/kdlbs/kandev/pull/167))
|
||||||
|
- improve workspace file tree loading
|
||||||
|
- improve make start
|
||||||
|
- draft pr support ([#165](https://github.com/kdlbs/kandev/pull/165))
|
||||||
|
- improved file tree ([#164](https://github.com/kdlbs/kandev/pull/164))
|
||||||
|
- session mobile design ([#163](https://github.com/kdlbs/kandev/pull/163))
|
||||||
|
- custom commands ([#162](https://github.com/kdlbs/kandev/pull/162))
|
||||||
|
- add support for mcpToolCall item type ([#159](https://github.com/kdlbs/kandev/pull/159))
|
||||||
|
- mobile design ([#160](https://github.com/kdlbs/kandev/pull/160))
|
||||||
|
- Add built-in custom prompts for common workflows ([#156](https://github.com/kdlbs/kandev/pull/156))
|
||||||
|
- improve kanban board ui ([#155](https://github.com/kdlbs/kandev/pull/155))
|
||||||
|
- update deps
|
||||||
|
- remove outdated docs
|
||||||
|
- update docs to use mermaid ([#152](https://github.com/kdlbs/kandev/pull/152))
|
||||||
|
- update default board settings ([#153](https://github.com/kdlbs/kandev/pull/153))
|
||||||
|
- add make start ([#151](https://github.com/kdlbs/kandev/pull/151))
|
||||||
|
- Add file editor with diff-based save functionality ([#145](https://github.com/kdlbs/kandev/pull/145))
|
||||||
|
- pierre diffs lib ([#147](https://github.com/kdlbs/kandev/pull/147))
|
||||||
|
- Implement git discard changes functionality ([#144](https://github.com/kdlbs/kandev/pull/144))
|
||||||
|
- Improve task approval workflow and workflow templates ([#141](https://github.com/kdlbs/kandev/pull/141))
|
||||||
|
- all tasks page plus search ([#140](https://github.com/kdlbs/kandev/pull/140))
|
||||||
|
- improve task deletion ([#139](https://github.com/kdlbs/kandev/pull/139))
|
||||||
|
- slash commands from agents ([#136](https://github.com/kdlbs/kandev/pull/136))
|
||||||
|
- add GitHub Copilot CLI and Sourcegraph Amp agent support ([#130](https://github.com/kdlbs/kandev/pull/130))
|
||||||
|
- improve task creation dialog ([#135](https://github.com/kdlbs/kandev/pull/135))
|
||||||
|
- plan comment annotations, Kandev system prompt, and Standard workflow ([#134](https://github.com/kdlbs/kandev/pull/134))
|
||||||
|
- improve chat ui messages ([#132](https://github.com/kdlbs/kandev/pull/132))
|
||||||
|
- improve make dev shutdown ([#133](https://github.com/kdlbs/kandev/pull/133))
|
||||||
|
- implement task plans feature ([#131](https://github.com/kdlbs/kandev/pull/131))
|
||||||
|
- add debug toggle button to TaskTopBar ([#128](https://github.com/kdlbs/kandev/pull/128))
|
||||||
|
- chat messages normalized ([#121](https://github.com/kdlbs/kandev/pull/121))
|
||||||
|
- improve sidebar ([#127](https://github.com/kdlbs/kandev/pull/127))
|
||||||
|
- migrate MCP server from backend to agentctl ([#124](https://github.com/kdlbs/kandev/pull/124))
|
||||||
|
- Add ask_user_question MCP tool for agent clarifications ([#123](https://github.com/kdlbs/kandev/pull/123))
|
||||||
|
- improved chat input ([#120](https://github.com/kdlbs/kandev/pull/120))
|
||||||
|
- implement file referencing with @filename autocomplete in chat input ([#118](https://github.com/kdlbs/kandev/pull/118))
|
||||||
|
- add thinking/reasoning streaming support ([#113](https://github.com/kdlbs/kandev/pull/113))
|
||||||
|
- improve approval flow and step transitions ([#111](https://github.com/kdlbs/kandev/pull/111))
|
||||||
|
- Add file unstaging functionality ([#107](https://github.com/kdlbs/kandev/pull/107))
|
||||||
|
- implement workflow system with steps terminology ([#102](https://github.com/kdlbs/kandev/pull/102))
|
||||||
|
- improve logging ([#105](https://github.com/kdlbs/kandev/pull/105))
|
||||||
|
- remove npm warns from terminal + fix terminal render on refresh ([#104](https://github.com/kdlbs/kandev/pull/104))
|
||||||
|
- improved chat ux ([#103](https://github.com/kdlbs/kandev/pull/103))
|
||||||
|
- git pull before worktree creation ([#100](https://github.com/kdlbs/kandev/pull/100))
|
||||||
|
- opencode dynamic model loader ([#99](https://github.com/kdlbs/kandev/pull/99))
|
||||||
|
- improve task switch + cli passthrough resume ([#98](https://github.com/kdlbs/kandev/pull/98))
|
||||||
|
- cli passthrough state transitions ([#95](https://github.com/kdlbs/kandev/pull/95))
|
||||||
|
- cli passthrough setting ([#93](https://github.com/kdlbs/kandev/pull/93))
|
||||||
|
- per-task executor selection with multi-runtime support ([#92](https://github.com/kdlbs/kandev/pull/92))
|
||||||
|
- diff file multi line comment
|
||||||
|
- gemini and opencode
|
||||||
|
- claude code support ([#87](https://github.com/kdlbs/kandev/pull/87))
|
||||||
|
- Git status tracking refactor with persistent snapshots and commit history ([#86](https://github.com/kdlbs/kandev/pull/86))
|
||||||
|
- improved codex and auggie default permissions
|
||||||
|
- setup and cleanup script ([#80](https://github.com/kdlbs/kandev/pull/80))
|
||||||
|
- improve chat ui paddings ([#83](https://github.com/kdlbs/kandev/pull/83))
|
||||||
|
- random port support ([#79](https://github.com/kdlbs/kandev/pull/79))
|
||||||
|
- improve preview url loading ([#78](https://github.com/kdlbs/kandev/pull/78))
|
||||||
|
- refactor frontend hooks and store ([#76](https://github.com/kdlbs/kandev/pull/76))
|
||||||
|
- backend improved comments, logs and agents.md ([#77](https://github.com/kdlbs/kandev/pull/77))
|
||||||
|
- process runners ([#71](https://github.com/kdlbs/kandev/pull/71))
|
||||||
|
- http logging middleware + mcp random port ([#74](https://github.com/kdlbs/kandev/pull/74))
|
||||||
|
- add session turns with duration display and live timer ([#72](https://github.com/kdlbs/kandev/pull/72))
|
||||||
|
- refactored chat input panels + pie context ([#70](https://github.com/kdlbs/kandev/pull/70))
|
||||||
|
- dynamic model switching and session status fix ([#68](https://github.com/kdlbs/kandev/pull/68))
|
||||||
|
- add embedded MCP server with dual transport support ([#67](https://github.com/kdlbs/kandev/pull/67))
|
||||||
|
- add context window usage display to task session ([#66](https://github.com/kdlbs/kandev/pull/66))
|
||||||
|
- mcp servers + executors ([#60](https://github.com/kdlbs/kandev/pull/60))
|
||||||
|
- improve repository list setting ([#65](https://github.com/kdlbs/kandev/pull/65))
|
||||||
|
- implement turn cancellation for agent sessions ([#64](https://github.com/kdlbs/kandev/pull/64))
|
||||||
|
- custom branch prefix ([#58](https://github.com/kdlbs/kandev/pull/58))
|
||||||
|
- add system provider
|
||||||
|
- Add PR creation via gh CLI and improve git operations ([#57](https://github.com/kdlbs/kandev/pull/57))
|
||||||
|
- kanban page refactoring ([#52](https://github.com/kdlbs/kandev/pull/52))
|
||||||
|
- settings data loading per page ([#51](https://github.com/kdlbs/kandev/pull/51))
|
||||||
|
- preview panel option ([#49](https://github.com/kdlbs/kandev/pull/49))
|
||||||
|
- custom prompts ([#48](https://github.com/kdlbs/kandev/pull/48))
|
||||||
|
- refactor main, add providers ([#46](https://github.com/kdlbs/kandev/pull/46))
|
||||||
|
- remove dev db not used ([#45](https://github.com/kdlbs/kandev/pull/45))
|
||||||
|
- refactor sqlite usage ([#44](https://github.com/kdlbs/kandev/pull/44))
|
||||||
|
- improve list agents
|
||||||
|
- multiple editors support ([#41](https://github.com/kdlbs/kandev/pull/41))
|
||||||
|
- cli publish ([#40](https://github.com/kdlbs/kandev/pull/40))
|
||||||
|
- cli launcher npx kandev ([#39](https://github.com/kdlbs/kandev/pull/39))
|
||||||
|
- improve chat UX ([#38](https://github.com/kdlbs/kandev/pull/38))
|
||||||
|
- add typed event payloads for event bus messages ([#32](https://github.com/kdlbs/kandev/pull/32))
|
||||||
|
- start agents on boot
|
||||||
|
- improve landing page ([#33](https://github.com/kdlbs/kandev/pull/33))
|
||||||
|
- remove premature executor deletion
|
||||||
|
- session refactor ([#30](https://github.com/kdlbs/kandev/pull/30))
|
||||||
|
- updated agents.md ([#28](https://github.com/kdlbs/kandev/pull/28))
|
||||||
|
- shell selector ([#27](https://github.com/kdlbs/kandev/pull/27))
|
||||||
|
- task switcher column ([#26](https://github.com/kdlbs/kandev/pull/26))
|
||||||
|
- inline permission approval for tool calls ([#25](https://github.com/kdlbs/kandev/pull/25))
|
||||||
|
- notifications ([#24](https://github.com/kdlbs/kandev/pull/24))
|
||||||
|
- improved chat experience ([#22](https://github.com/kdlbs/kandev/pull/22))
|
||||||
|
- improve onboarding and task setup ([#21](https://github.com/kdlbs/kandev/pull/21))
|
||||||
|
- auto-respawn shell session on unexpected exit
|
||||||
|
- add graceful shutdown
|
||||||
|
- task_session_worktrees
|
||||||
|
- Interactive shell terminal for agent tasks ([#19](https://github.com/kdlbs/kandev/pull/19))
|
||||||
|
- flaky resume sessions
|
||||||
|
- rename agent_sessions to task_sessions
|
||||||
|
- golang linter
|
||||||
|
- right panel overlay
|
||||||
|
- tasks refactor
|
||||||
|
- improved chat ui topbar
|
||||||
|
- chat improved renderer and comments pagination
|
||||||
|
- tanstack virtual
|
||||||
|
- task comments SSR fetch
|
||||||
|
- cmd+enter in task chat
|
||||||
|
- protocol adapter abstraction with agent profile support ([#15](https://github.com/kdlbs/kandev/pull/15))
|
||||||
|
- replace agent_type with agent_profile_id ([#14](https://github.com/kdlbs/kandev/pull/14))
|
||||||
|
- improve cards
|
||||||
|
- changed theme to shadcn nova - less paddings
|
||||||
|
- data fetching refactor ssr -> ws updates
|
||||||
|
- File browser with syntax-highlighted viewer ([#13](https://github.com/kdlbs/kandev/pull/13))
|
||||||
|
- auto-launch agentctl subprocess in standalone mode ([#12](https://github.com/kdlbs/kandev/pull/12))
|
||||||
|
- add defaults to workspace: env, executor, agent
|
||||||
|
- simplify task creation
|
||||||
|
- display settings in kanban page
|
||||||
|
- semantic naming and branch cleanup on task deletion ([#11](https://github.com/kdlbs/kandev/pull/11))
|
||||||
|
- agents discovery
|
||||||
|
- add pre-commit hook
|
||||||
|
- environments and executors
|
||||||
|
- build agentctl to bin/ and use pre-built binary in Dockerfile ([#9](https://github.com/kdlbs/kandev/pull/9))
|
||||||
|
- Real-time Git Status Integration ([#8](https://github.com/kdlbs/kandev/pull/8))
|
||||||
|
- improved settings repos, boards
|
||||||
|
- landing page and pnpm workspaces
|
||||||
|
- return worktree info in orchestrator.start response
|
||||||
|
- worktrees at agent session level with random suffix
|
||||||
|
- cleanup worktree and branch on task deletion
|
||||||
|
- expose worktree path and branch in task API and UI
|
||||||
|
- implement Git worktrees for concurrent agent execution
|
||||||
|
- enhance tool call display with payload details and typing indicator
|
||||||
|
- make repository_url optional when launching agents
|
||||||
|
- add persistent agent session tracking
|
||||||
|
- enhance WebSocket handling and chat panel improvements
|
||||||
|
- tasks crud
|
||||||
|
- added repositories support
|
||||||
|
- enhance comment system and e2e testing
|
||||||
|
- implement ACP permission request flow
|
||||||
|
- add bidirectional comment system and agent input request flow
|
||||||
|
- web app support for boards and columns
|
||||||
|
- web app support for workspaces
|
||||||
|
- clean db command
|
||||||
|
- added workspaces
|
||||||
|
- ws state
|
||||||
|
- ws and zustand init
|
||||||
|
- add http handlers to backend
|
||||||
|
- implement persistent agent execution logs storage and retrieval
|
||||||
|
- improve homepage buttons
|
||||||
|
- settings page
|
||||||
|
- improved task page
|
||||||
|
- ui components reset
|
||||||
|
- task page
|
||||||
|
- add READY status and multi-turn conversation support
|
||||||
|
- complete acp-go-sdk integration with task state updates
|
||||||
|
- multi view kanban
|
||||||
|
- fix kanban ssr
|
||||||
|
- init kanban
|
||||||
|
- web app cleanup ([#2](https://github.com/kdlbs/kandev/pull/2))
|
||||||
|
- add build orchestration and architecture documentation ([#1](https://github.com/kdlbs/kandev/pull/1))
|
||||||
|
- web app init
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
|
||||||
|
- Handle untracked and new files in git discard operation ([#168](https://github.com/kdlbs/kandev/pull/168))
|
||||||
|
- Persist plan notification state across page refreshes ([#150](https://github.com/kdlbs/kandev/pull/150))
|
||||||
|
- Approval button not showing when navigating between sessions ([#116](https://github.com/kdlbs/kandev/pull/116))
|
||||||
|
|
||||||
|
### Refactoring
|
||||||
|
|
||||||
|
- Remove database migrations for clean dev-phase bootstrap ([#176](https://github.com/kdlbs/kandev/pull/176))
|
||||||
|
- consolidate system prompts into sysprompt package ([#166](https://github.com/kdlbs/kandev/pull/166))
|
||||||
|
- split monolithic sqlite.go into domain-specific files ([#84](https://github.com/kdlbs/kandev/pull/84))
|
||||||
|
- remove progress field from TaskSession and AgentExecution ([#75](https://github.com/kdlbs/kandev/pull/75))
|
||||||
|
- comprehensive code quality improvements ([#69](https://github.com/kdlbs/kandev/pull/69))
|
||||||
|
- remove auggie-specific code and use standard ACP protocol ([#47](https://github.com/kdlbs/kandev/pull/47))
|
||||||
|
- unify permission requests into agent event stream ([#31](https://github.com/kdlbs/kandev/pull/31))
|
||||||
|
- session resumption cleanup and AgentInstance → AgentExecution rename ([#23](https://github.com/kdlbs/kandev/pull/23))
|
||||||
|
- extract manager into focused components ([#20](https://github.com/kdlbs/kandev/pull/20))
|
||||||
|
- unify configuration and remove single-instance mode ([#18](https://github.com/kdlbs/kandev/pull/18))
|
||||||
|
- unify WebSocket patterns to Pattern A (handlers + controller + dto)
|
||||||
|
- Replace REST API with WebSocket-only architecture
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
- improved pre-commit linter config
|
||||||
|
|
||||||
|
### Cleanup
|
||||||
|
|
||||||
|
- remove dead FileListUpdate streaming path ([#204](https://github.com/kdlbs/kandev/pull/204))
|
||||||
|
|
||||||
|
### Merge
|
||||||
|
|
||||||
|
- resolve conflicts with main
|
||||||
|
|
||||||
|
|
||||||
@@ -44,20 +44,22 @@ func (c *Controller) GetUserSettings(ctx context.Context) (dto.UserSettingsRespo
|
|||||||
|
|
||||||
func (c *Controller) UpdateUserSettings(ctx context.Context, req dto.UpdateUserSettingsRequest) (dto.UserSettingsResponse, error) {
|
func (c *Controller) UpdateUserSettings(ctx context.Context, req dto.UpdateUserSettingsRequest) (dto.UserSettingsResponse, error) {
|
||||||
settings, err := c.svc.UpdateUserSettings(ctx, &service.UpdateUserSettingsRequest{
|
settings, err := c.svc.UpdateUserSettings(ctx, &service.UpdateUserSettingsRequest{
|
||||||
WorkspaceID: req.WorkspaceID,
|
WorkspaceID: req.WorkspaceID,
|
||||||
KanbanViewMode: req.KanbanViewMode,
|
KanbanViewMode: req.KanbanViewMode,
|
||||||
WorkflowFilterID: req.WorkflowFilterID,
|
WorkflowFilterID: req.WorkflowFilterID,
|
||||||
RepositoryIDs: req.RepositoryIDs,
|
RepositoryIDs: req.RepositoryIDs,
|
||||||
InitialSetupComplete: req.InitialSetupComplete,
|
InitialSetupComplete: req.InitialSetupComplete,
|
||||||
PreferredShell: req.PreferredShell,
|
PreferredShell: req.PreferredShell,
|
||||||
DefaultEditorID: req.DefaultEditorID,
|
DefaultEditorID: req.DefaultEditorID,
|
||||||
EnablePreviewOnClick: req.EnablePreviewOnClick,
|
EnablePreviewOnClick: req.EnablePreviewOnClick,
|
||||||
ChatSubmitKey: req.ChatSubmitKey,
|
ChatSubmitKey: req.ChatSubmitKey,
|
||||||
ReviewAutoMarkOnScroll: req.ReviewAutoMarkOnScroll,
|
ReviewAutoMarkOnScroll: req.ReviewAutoMarkOnScroll,
|
||||||
LspAutoStartLanguages: req.LspAutoStartLanguages,
|
ShowReleaseNotification: req.ShowReleaseNotification,
|
||||||
LspAutoInstallLanguages: req.LspAutoInstallLanguages,
|
ReleaseNotesLastSeenVersion: req.ReleaseNotesLastSeenVersion,
|
||||||
LspServerConfigs: req.LspServerConfigs,
|
LspAutoStartLanguages: req.LspAutoStartLanguages,
|
||||||
SavedLayouts: req.SavedLayouts,
|
LspAutoInstallLanguages: req.LspAutoInstallLanguages,
|
||||||
|
LspServerConfigs: req.LspServerConfigs,
|
||||||
|
SavedLayouts: req.SavedLayouts,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dto.UserSettingsResponse{}, err
|
return dto.UserSettingsResponse{}, err
|
||||||
|
|||||||
@@ -14,22 +14,24 @@ type UserDTO struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UserSettingsDTO struct {
|
type UserSettingsDTO struct {
|
||||||
UserID string `json:"user_id"`
|
UserID string `json:"user_id"`
|
||||||
WorkspaceID string `json:"workspace_id"`
|
WorkspaceID string `json:"workspace_id"`
|
||||||
KanbanViewMode string `json:"kanban_view_mode"`
|
KanbanViewMode string `json:"kanban_view_mode"`
|
||||||
WorkflowFilterID string `json:"workflow_filter_id"`
|
WorkflowFilterID string `json:"workflow_filter_id"`
|
||||||
RepositoryIDs []string `json:"repository_ids"`
|
RepositoryIDs []string `json:"repository_ids"`
|
||||||
InitialSetupComplete bool `json:"initial_setup_complete"`
|
InitialSetupComplete bool `json:"initial_setup_complete"`
|
||||||
PreferredShell string `json:"preferred_shell"`
|
PreferredShell string `json:"preferred_shell"`
|
||||||
DefaultEditorID string `json:"default_editor_id"`
|
DefaultEditorID string `json:"default_editor_id"`
|
||||||
EnablePreviewOnClick bool `json:"enable_preview_on_click"`
|
EnablePreviewOnClick bool `json:"enable_preview_on_click"`
|
||||||
ChatSubmitKey string `json:"chat_submit_key"`
|
ChatSubmitKey string `json:"chat_submit_key"`
|
||||||
ReviewAutoMarkOnScroll bool `json:"review_auto_mark_on_scroll"`
|
ReviewAutoMarkOnScroll bool `json:"review_auto_mark_on_scroll"`
|
||||||
LspAutoStartLanguages []string `json:"lsp_auto_start_languages"`
|
ShowReleaseNotification bool `json:"show_release_notification"`
|
||||||
LspAutoInstallLanguages []string `json:"lsp_auto_install_languages"`
|
ReleaseNotesLastSeenVersion string `json:"release_notes_last_seen_version"`
|
||||||
LspServerConfigs map[string]map[string]interface{} `json:"lsp_server_configs,omitempty"`
|
LspAutoStartLanguages []string `json:"lsp_auto_start_languages"`
|
||||||
SavedLayouts []models.SavedLayout `json:"saved_layouts"`
|
LspAutoInstallLanguages []string `json:"lsp_auto_install_languages"`
|
||||||
UpdatedAt string `json:"updated_at"`
|
LspServerConfigs map[string]map[string]interface{} `json:"lsp_server_configs,omitempty"`
|
||||||
|
SavedLayouts []models.SavedLayout `json:"saved_layouts"`
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserResponse struct {
|
type UserResponse struct {
|
||||||
@@ -48,20 +50,22 @@ type ShellOption struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UpdateUserSettingsRequest struct {
|
type UpdateUserSettingsRequest struct {
|
||||||
WorkspaceID *string `json:"workspace_id,omitempty"`
|
WorkspaceID *string `json:"workspace_id,omitempty"`
|
||||||
KanbanViewMode *string `json:"kanban_view_mode,omitempty"`
|
KanbanViewMode *string `json:"kanban_view_mode,omitempty"`
|
||||||
WorkflowFilterID *string `json:"workflow_filter_id,omitempty"`
|
WorkflowFilterID *string `json:"workflow_filter_id,omitempty"`
|
||||||
RepositoryIDs *[]string `json:"repository_ids,omitempty"`
|
RepositoryIDs *[]string `json:"repository_ids,omitempty"`
|
||||||
InitialSetupComplete *bool `json:"initial_setup_complete,omitempty"`
|
InitialSetupComplete *bool `json:"initial_setup_complete,omitempty"`
|
||||||
PreferredShell *string `json:"preferred_shell,omitempty"`
|
PreferredShell *string `json:"preferred_shell,omitempty"`
|
||||||
DefaultEditorID *string `json:"default_editor_id,omitempty"`
|
DefaultEditorID *string `json:"default_editor_id,omitempty"`
|
||||||
EnablePreviewOnClick *bool `json:"enable_preview_on_click,omitempty"`
|
EnablePreviewOnClick *bool `json:"enable_preview_on_click,omitempty"`
|
||||||
ChatSubmitKey *string `json:"chat_submit_key,omitempty"`
|
ChatSubmitKey *string `json:"chat_submit_key,omitempty"`
|
||||||
ReviewAutoMarkOnScroll *bool `json:"review_auto_mark_on_scroll,omitempty"`
|
ReviewAutoMarkOnScroll *bool `json:"review_auto_mark_on_scroll,omitempty"`
|
||||||
LspAutoStartLanguages *[]string `json:"lsp_auto_start_languages,omitempty"`
|
ShowReleaseNotification *bool `json:"show_release_notification,omitempty"`
|
||||||
LspAutoInstallLanguages *[]string `json:"lsp_auto_install_languages,omitempty"`
|
ReleaseNotesLastSeenVersion *string `json:"release_notes_last_seen_version,omitempty"`
|
||||||
LspServerConfigs *map[string]map[string]interface{} `json:"lsp_server_configs,omitempty"`
|
LspAutoStartLanguages *[]string `json:"lsp_auto_start_languages,omitempty"`
|
||||||
SavedLayouts *[]models.SavedLayout `json:"saved_layouts,omitempty"`
|
LspAutoInstallLanguages *[]string `json:"lsp_auto_install_languages,omitempty"`
|
||||||
|
LspServerConfigs *map[string]map[string]interface{} `json:"lsp_server_configs,omitempty"`
|
||||||
|
SavedLayouts *[]models.SavedLayout `json:"saved_layouts,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func FromUser(user *models.User) UserDTO {
|
func FromUser(user *models.User) UserDTO {
|
||||||
@@ -75,21 +79,23 @@ func FromUser(user *models.User) UserDTO {
|
|||||||
|
|
||||||
func FromUserSettings(settings *models.UserSettings) UserSettingsDTO {
|
func FromUserSettings(settings *models.UserSettings) UserSettingsDTO {
|
||||||
return UserSettingsDTO{
|
return UserSettingsDTO{
|
||||||
UserID: settings.UserID,
|
UserID: settings.UserID,
|
||||||
WorkspaceID: settings.WorkspaceID,
|
WorkspaceID: settings.WorkspaceID,
|
||||||
KanbanViewMode: settings.KanbanViewMode,
|
KanbanViewMode: settings.KanbanViewMode,
|
||||||
WorkflowFilterID: settings.WorkflowFilterID,
|
WorkflowFilterID: settings.WorkflowFilterID,
|
||||||
RepositoryIDs: settings.RepositoryIDs,
|
RepositoryIDs: settings.RepositoryIDs,
|
||||||
InitialSetupComplete: settings.InitialSetupComplete,
|
InitialSetupComplete: settings.InitialSetupComplete,
|
||||||
PreferredShell: settings.PreferredShell,
|
PreferredShell: settings.PreferredShell,
|
||||||
DefaultEditorID: settings.DefaultEditorID,
|
DefaultEditorID: settings.DefaultEditorID,
|
||||||
EnablePreviewOnClick: settings.EnablePreviewOnClick,
|
EnablePreviewOnClick: settings.EnablePreviewOnClick,
|
||||||
ChatSubmitKey: settings.ChatSubmitKey,
|
ChatSubmitKey: settings.ChatSubmitKey,
|
||||||
ReviewAutoMarkOnScroll: settings.ReviewAutoMarkOnScroll,
|
ReviewAutoMarkOnScroll: settings.ReviewAutoMarkOnScroll,
|
||||||
LspAutoStartLanguages: settings.LspAutoStartLanguages,
|
ShowReleaseNotification: settings.ShowReleaseNotification,
|
||||||
LspAutoInstallLanguages: settings.LspAutoInstallLanguages,
|
ReleaseNotesLastSeenVersion: settings.ReleaseNotesLastSeenVersion,
|
||||||
LspServerConfigs: settings.LspServerConfigs,
|
LspAutoStartLanguages: settings.LspAutoStartLanguages,
|
||||||
SavedLayouts: settings.SavedLayouts,
|
LspAutoInstallLanguages: settings.LspAutoInstallLanguages,
|
||||||
UpdatedAt: settings.UpdatedAt.Format(time.RFC3339),
|
LspServerConfigs: settings.LspServerConfigs,
|
||||||
|
SavedLayouts: settings.SavedLayouts,
|
||||||
|
UpdatedAt: settings.UpdatedAt.Format(time.RFC3339),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,23 +13,25 @@ type User struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UserSettings struct {
|
type UserSettings struct {
|
||||||
UserID string `json:"user_id"`
|
UserID string `json:"user_id"`
|
||||||
WorkspaceID string `json:"workspace_id"`
|
WorkspaceID string `json:"workspace_id"`
|
||||||
KanbanViewMode string `json:"kanban_view_mode"`
|
KanbanViewMode string `json:"kanban_view_mode"`
|
||||||
WorkflowFilterID string `json:"workflow_filter_id"`
|
WorkflowFilterID string `json:"workflow_filter_id"`
|
||||||
RepositoryIDs []string `json:"repository_ids"`
|
RepositoryIDs []string `json:"repository_ids"`
|
||||||
InitialSetupComplete bool `json:"initial_setup_complete"`
|
InitialSetupComplete bool `json:"initial_setup_complete"`
|
||||||
PreferredShell string `json:"preferred_shell"`
|
PreferredShell string `json:"preferred_shell"`
|
||||||
DefaultEditorID string `json:"default_editor_id"`
|
DefaultEditorID string `json:"default_editor_id"`
|
||||||
EnablePreviewOnClick bool `json:"enable_preview_on_click"`
|
EnablePreviewOnClick bool `json:"enable_preview_on_click"`
|
||||||
ChatSubmitKey string `json:"chat_submit_key"` // "enter" | "cmd_enter"
|
ChatSubmitKey string `json:"chat_submit_key"` // "enter" | "cmd_enter"
|
||||||
ReviewAutoMarkOnScroll bool `json:"review_auto_mark_on_scroll"`
|
ReviewAutoMarkOnScroll bool `json:"review_auto_mark_on_scroll"`
|
||||||
LspAutoStartLanguages []string `json:"lsp_auto_start_languages"`
|
ShowReleaseNotification bool `json:"show_release_notification"`
|
||||||
LspAutoInstallLanguages []string `json:"lsp_auto_install_languages"`
|
ReleaseNotesLastSeenVersion string `json:"release_notes_last_seen_version"`
|
||||||
LspServerConfigs map[string]map[string]interface{} `json:"lsp_server_configs"`
|
LspAutoStartLanguages []string `json:"lsp_auto_start_languages"`
|
||||||
SavedLayouts []SavedLayout `json:"saved_layouts"`
|
LspAutoInstallLanguages []string `json:"lsp_auto_install_languages"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
LspServerConfigs map[string]map[string]interface{} `json:"lsp_server_configs"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
SavedLayouts []SavedLayout `json:"saved_layouts"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SavedLayout represents a user-saved dockview layout configuration.
|
// SavedLayout represents a user-saved dockview layout configuration.
|
||||||
|
|||||||
@@ -26,20 +26,22 @@ type Service struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UpdateUserSettingsRequest struct {
|
type UpdateUserSettingsRequest struct {
|
||||||
WorkspaceID *string
|
WorkspaceID *string
|
||||||
KanbanViewMode *string
|
KanbanViewMode *string
|
||||||
WorkflowFilterID *string
|
WorkflowFilterID *string
|
||||||
RepositoryIDs *[]string
|
RepositoryIDs *[]string
|
||||||
InitialSetupComplete *bool
|
InitialSetupComplete *bool
|
||||||
PreferredShell *string
|
PreferredShell *string
|
||||||
DefaultEditorID *string
|
DefaultEditorID *string
|
||||||
EnablePreviewOnClick *bool
|
EnablePreviewOnClick *bool
|
||||||
ChatSubmitKey *string
|
ChatSubmitKey *string
|
||||||
ReviewAutoMarkOnScroll *bool
|
ReviewAutoMarkOnScroll *bool
|
||||||
LspAutoStartLanguages *[]string
|
ShowReleaseNotification *bool
|
||||||
LspAutoInstallLanguages *[]string
|
ReleaseNotesLastSeenVersion *string
|
||||||
LspServerConfigs *map[string]map[string]interface{}
|
LspAutoStartLanguages *[]string
|
||||||
SavedLayouts *[]models.SavedLayout
|
LspAutoInstallLanguages *[]string
|
||||||
|
LspServerConfigs *map[string]map[string]interface{}
|
||||||
|
SavedLayouts *[]models.SavedLayout
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(repo store.Repository, eventBus bus.EventBus, log *logger.Logger) *Service {
|
func NewService(repo store.Repository, eventBus bus.EventBus, log *logger.Logger) *Service {
|
||||||
@@ -129,6 +131,12 @@ func applyBasicSettings(settings *models.UserSettings, req *UpdateUserSettingsRe
|
|||||||
if req.ReviewAutoMarkOnScroll != nil {
|
if req.ReviewAutoMarkOnScroll != nil {
|
||||||
settings.ReviewAutoMarkOnScroll = *req.ReviewAutoMarkOnScroll
|
settings.ReviewAutoMarkOnScroll = *req.ReviewAutoMarkOnScroll
|
||||||
}
|
}
|
||||||
|
if req.ShowReleaseNotification != nil {
|
||||||
|
settings.ShowReleaseNotification = *req.ShowReleaseNotification
|
||||||
|
}
|
||||||
|
if req.ReleaseNotesLastSeenVersion != nil {
|
||||||
|
settings.ReleaseNotesLastSeenVersion = *req.ReleaseNotesLastSeenVersion
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,22 +199,24 @@ func (s *Service) publishUserSettingsEvent(ctx context.Context, settings *models
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
data := map[string]interface{}{
|
data := map[string]interface{}{
|
||||||
"user_id": settings.UserID,
|
"user_id": settings.UserID,
|
||||||
"workspace_id": settings.WorkspaceID,
|
"workspace_id": settings.WorkspaceID,
|
||||||
"kanban_view_mode": settings.KanbanViewMode,
|
"kanban_view_mode": settings.KanbanViewMode,
|
||||||
"workflow_filter_id": settings.WorkflowFilterID,
|
"workflow_filter_id": settings.WorkflowFilterID,
|
||||||
"repository_ids": settings.RepositoryIDs,
|
"repository_ids": settings.RepositoryIDs,
|
||||||
"initial_setup_complete": settings.InitialSetupComplete,
|
"initial_setup_complete": settings.InitialSetupComplete,
|
||||||
"preferred_shell": settings.PreferredShell,
|
"preferred_shell": settings.PreferredShell,
|
||||||
"default_editor_id": settings.DefaultEditorID,
|
"default_editor_id": settings.DefaultEditorID,
|
||||||
"enable_preview_on_click": settings.EnablePreviewOnClick,
|
"enable_preview_on_click": settings.EnablePreviewOnClick,
|
||||||
"chat_submit_key": settings.ChatSubmitKey,
|
"chat_submit_key": settings.ChatSubmitKey,
|
||||||
"review_auto_mark_on_scroll": settings.ReviewAutoMarkOnScroll,
|
"review_auto_mark_on_scroll": settings.ReviewAutoMarkOnScroll,
|
||||||
"lsp_auto_start_languages": settings.LspAutoStartLanguages,
|
"show_release_notification": settings.ShowReleaseNotification,
|
||||||
"lsp_auto_install_languages": settings.LspAutoInstallLanguages,
|
"release_notes_last_seen_version": settings.ReleaseNotesLastSeenVersion,
|
||||||
"lsp_server_configs": settings.LspServerConfigs,
|
"lsp_auto_start_languages": settings.LspAutoStartLanguages,
|
||||||
"saved_layouts": settings.SavedLayouts,
|
"lsp_auto_install_languages": settings.LspAutoInstallLanguages,
|
||||||
"updated_at": settings.UpdatedAt.Format(time.RFC3339),
|
"lsp_server_configs": settings.LspServerConfigs,
|
||||||
|
"saved_layouts": settings.SavedLayouts,
|
||||||
|
"updated_at": settings.UpdatedAt.Format(time.RFC3339),
|
||||||
}
|
}
|
||||||
if err := s.eventBus.Publish(ctx, events.UserSettingsUpdated, bus.NewEvent(events.UserSettingsUpdated, "user-service", data)); err != nil {
|
if err := s.eventBus.Publish(ctx, events.UserSettingsUpdated, bus.NewEvent(events.UserSettingsUpdated, "user-service", data)); err != nil {
|
||||||
s.logger.Error("failed to publish user settings event", zap.Error(err))
|
s.logger.Error("failed to publish user settings event", zap.Error(err))
|
||||||
|
|||||||
@@ -25,6 +25,69 @@ func makeLayouts(n int) []models.SavedLayout {
|
|||||||
return layouts
|
return layouts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestApplyBasicSettings_ReleaseNotes(t *testing.T) {
|
||||||
|
t.Run("nil fields leave settings unchanged", func(t *testing.T) {
|
||||||
|
settings := &models.UserSettings{
|
||||||
|
ShowReleaseNotification: true,
|
||||||
|
ReleaseNotesLastSeenVersion: "1.0.0",
|
||||||
|
}
|
||||||
|
req := &UpdateUserSettingsRequest{}
|
||||||
|
if err := applyBasicSettings(settings, req); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if settings.ShowReleaseNotification != true {
|
||||||
|
t.Fatalf("expected ShowReleaseNotification=true, got %v", settings.ShowReleaseNotification)
|
||||||
|
}
|
||||||
|
if settings.ReleaseNotesLastSeenVersion != "1.0.0" {
|
||||||
|
t.Fatalf("expected ReleaseNotesLastSeenVersion=1.0.0, got %s", settings.ReleaseNotesLastSeenVersion)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ShowReleaseNotification set to false", func(t *testing.T) {
|
||||||
|
settings := &models.UserSettings{ShowReleaseNotification: true}
|
||||||
|
req := &UpdateUserSettingsRequest{ShowReleaseNotification: ptr(false)}
|
||||||
|
if err := applyBasicSettings(settings, req); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if settings.ShowReleaseNotification != false {
|
||||||
|
t.Fatalf("expected ShowReleaseNotification=false, got %v", settings.ShowReleaseNotification)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ShowReleaseNotification set to true", func(t *testing.T) {
|
||||||
|
settings := &models.UserSettings{ShowReleaseNotification: false}
|
||||||
|
req := &UpdateUserSettingsRequest{ShowReleaseNotification: ptr(true)}
|
||||||
|
if err := applyBasicSettings(settings, req); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if settings.ShowReleaseNotification != true {
|
||||||
|
t.Fatalf("expected ShowReleaseNotification=true, got %v", settings.ShowReleaseNotification)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ReleaseNotesLastSeenVersion updated", func(t *testing.T) {
|
||||||
|
settings := &models.UserSettings{ReleaseNotesLastSeenVersion: "1.0.0"}
|
||||||
|
req := &UpdateUserSettingsRequest{ReleaseNotesLastSeenVersion: ptr("2.0.0")}
|
||||||
|
if err := applyBasicSettings(settings, req); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if settings.ReleaseNotesLastSeenVersion != "2.0.0" {
|
||||||
|
t.Fatalf("expected ReleaseNotesLastSeenVersion=2.0.0, got %s", settings.ReleaseNotesLastSeenVersion)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ReleaseNotesLastSeenVersion cleared with empty string", func(t *testing.T) {
|
||||||
|
settings := &models.UserSettings{ReleaseNotesLastSeenVersion: "1.0.0"}
|
||||||
|
req := &UpdateUserSettingsRequest{ReleaseNotesLastSeenVersion: ptr("")}
|
||||||
|
if err := applyBasicSettings(settings, req); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if settings.ReleaseNotesLastSeenVersion != "" {
|
||||||
|
t.Fatalf("expected empty ReleaseNotesLastSeenVersion, got %s", settings.ReleaseNotesLastSeenVersion)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestApplySavedLayouts(t *testing.T) {
|
func TestApplySavedLayouts(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
|||||||
@@ -130,20 +130,22 @@ func (r *sqliteRepository) UpsertUserSettings(ctx context.Context, settings *mod
|
|||||||
savedLayouts = []models.SavedLayout{}
|
savedLayouts = []models.SavedLayout{}
|
||||||
}
|
}
|
||||||
settingsPayload, err := json.Marshal(map[string]interface{}{
|
settingsPayload, err := json.Marshal(map[string]interface{}{
|
||||||
"workspace_id": settings.WorkspaceID,
|
"workspace_id": settings.WorkspaceID,
|
||||||
"kanban_view_mode": settings.KanbanViewMode,
|
"kanban_view_mode": settings.KanbanViewMode,
|
||||||
"workflow_filter_id": settings.WorkflowFilterID,
|
"workflow_filter_id": settings.WorkflowFilterID,
|
||||||
"repository_ids": settings.RepositoryIDs,
|
"repository_ids": settings.RepositoryIDs,
|
||||||
"initial_setup_complete": settings.InitialSetupComplete,
|
"initial_setup_complete": settings.InitialSetupComplete,
|
||||||
"preferred_shell": settings.PreferredShell,
|
"preferred_shell": settings.PreferredShell,
|
||||||
"default_editor_id": settings.DefaultEditorID,
|
"default_editor_id": settings.DefaultEditorID,
|
||||||
"enable_preview_on_click": settings.EnablePreviewOnClick,
|
"enable_preview_on_click": settings.EnablePreviewOnClick,
|
||||||
"chat_submit_key": settings.ChatSubmitKey,
|
"chat_submit_key": settings.ChatSubmitKey,
|
||||||
"review_auto_mark_on_scroll": settings.ReviewAutoMarkOnScroll,
|
"review_auto_mark_on_scroll": settings.ReviewAutoMarkOnScroll,
|
||||||
"lsp_auto_start_languages": lspAutoStart,
|
"show_release_notification": settings.ShowReleaseNotification,
|
||||||
"lsp_auto_install_languages": lspAutoInstall,
|
"release_notes_last_seen_version": settings.ReleaseNotesLastSeenVersion,
|
||||||
"lsp_server_configs": lspServerConfigs,
|
"lsp_auto_start_languages": lspAutoStart,
|
||||||
"saved_layouts": savedLayouts,
|
"lsp_auto_install_languages": lspAutoInstall,
|
||||||
|
"lsp_server_configs": lspServerConfigs,
|
||||||
|
"saved_layouts": savedLayouts,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -186,20 +188,22 @@ func scanUserSettings(scanner interface{ Scan(dest ...any) error }, userID strin
|
|||||||
return settings, nil
|
return settings, nil
|
||||||
}
|
}
|
||||||
var payload struct {
|
var payload struct {
|
||||||
WorkspaceID string `json:"workspace_id"`
|
WorkspaceID string `json:"workspace_id"`
|
||||||
KanbanViewMode string `json:"kanban_view_mode"`
|
KanbanViewMode string `json:"kanban_view_mode"`
|
||||||
WorkflowFilterID string `json:"workflow_filter_id"`
|
WorkflowFilterID string `json:"workflow_filter_id"`
|
||||||
RepositoryIDs []string `json:"repository_ids"`
|
RepositoryIDs []string `json:"repository_ids"`
|
||||||
InitialSetupComplete bool `json:"initial_setup_complete"`
|
InitialSetupComplete bool `json:"initial_setup_complete"`
|
||||||
PreferredShell string `json:"preferred_shell"`
|
PreferredShell string `json:"preferred_shell"`
|
||||||
DefaultEditorID string `json:"default_editor_id"`
|
DefaultEditorID string `json:"default_editor_id"`
|
||||||
EnablePreviewOnClick bool `json:"enable_preview_on_click"`
|
EnablePreviewOnClick bool `json:"enable_preview_on_click"`
|
||||||
ChatSubmitKey string `json:"chat_submit_key"`
|
ChatSubmitKey string `json:"chat_submit_key"`
|
||||||
ReviewAutoMarkOnScroll *bool `json:"review_auto_mark_on_scroll"`
|
ReviewAutoMarkOnScroll *bool `json:"review_auto_mark_on_scroll"`
|
||||||
LspAutoStartLanguages []string `json:"lsp_auto_start_languages"`
|
ShowReleaseNotification *bool `json:"show_release_notification"`
|
||||||
LspAutoInstallLanguages []string `json:"lsp_auto_install_languages"`
|
ReleaseNotesLastSeenVersion string `json:"release_notes_last_seen_version"`
|
||||||
LspServerConfigs map[string]map[string]interface{} `json:"lsp_server_configs"`
|
LspAutoStartLanguages []string `json:"lsp_auto_start_languages"`
|
||||||
SavedLayouts []models.SavedLayout `json:"saved_layouts"`
|
LspAutoInstallLanguages []string `json:"lsp_auto_install_languages"`
|
||||||
|
LspServerConfigs map[string]map[string]interface{} `json:"lsp_server_configs"`
|
||||||
|
SavedLayouts []models.SavedLayout `json:"saved_layouts"`
|
||||||
}
|
}
|
||||||
if err := json.Unmarshal([]byte(settingsRaw), &payload); err != nil {
|
if err := json.Unmarshal([]byte(settingsRaw), &payload); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -223,6 +227,12 @@ func scanUserSettings(scanner interface{ Scan(dest ...any) error }, userID strin
|
|||||||
} else {
|
} else {
|
||||||
settings.ReviewAutoMarkOnScroll = true
|
settings.ReviewAutoMarkOnScroll = true
|
||||||
}
|
}
|
||||||
|
if payload.ShowReleaseNotification != nil {
|
||||||
|
settings.ShowReleaseNotification = *payload.ShowReleaseNotification
|
||||||
|
} else {
|
||||||
|
settings.ShowReleaseNotification = true
|
||||||
|
}
|
||||||
|
settings.ReleaseNotesLastSeenVersion = payload.ReleaseNotesLastSeenVersion
|
||||||
settings.LspAutoStartLanguages = payload.LspAutoStartLanguages
|
settings.LspAutoStartLanguages = payload.LspAutoStartLanguages
|
||||||
if settings.LspAutoStartLanguages == nil {
|
if settings.LspAutoStartLanguages == nil {
|
||||||
settings.LspAutoStartLanguages = []string{}
|
settings.LspAutoStartLanguages = []string{}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
export default {
|
||||||
|
extends: ["@commitlint/config-conventional"],
|
||||||
|
rules: {
|
||||||
|
"type-enum": [
|
||||||
|
2,
|
||||||
|
"always",
|
||||||
|
[
|
||||||
|
"feat",
|
||||||
|
"fix",
|
||||||
|
"perf",
|
||||||
|
"refactor",
|
||||||
|
"docs",
|
||||||
|
"chore",
|
||||||
|
"ci",
|
||||||
|
"test",
|
||||||
|
"release",
|
||||||
|
],
|
||||||
|
],
|
||||||
|
"subject-case": [2, "never", ["start-case", "pascal-case", "upper-case"]],
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -13,6 +13,8 @@
|
|||||||
"clean": "pnpm -r run clean && rm -rf node_modules"
|
"clean": "pnpm -r run clean && rm -rf node_modules"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@commitlint/cli": "^20.4.2",
|
||||||
|
"@commitlint/config-conventional": "^20.4.2",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.6.2",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
|
|||||||
Generated
+354
-15
@@ -12,6 +12,12 @@ importers:
|
|||||||
|
|
||||||
.:
|
.:
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@commitlint/cli':
|
||||||
|
specifier: ^20.4.2
|
||||||
|
version: 20.4.2(@types/node@20.19.28)(typescript@5.9.3)
|
||||||
|
'@commitlint/config-conventional':
|
||||||
|
specifier: ^20.4.2
|
||||||
|
version: 20.4.2
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^20
|
specifier: ^20
|
||||||
version: 20.19.28
|
version: 20.19.28
|
||||||
@@ -388,7 +394,7 @@ importers:
|
|||||||
version: 0.55.1
|
version: 0.55.1
|
||||||
next:
|
next:
|
||||||
specifier: 16.1.1
|
specifier: 16.1.1
|
||||||
version: 16.1.1(@babel/core@7.28.6)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
version: 16.1.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||||
next-themes:
|
next-themes:
|
||||||
specifier: ^0.4.6
|
specifier: ^0.4.6
|
||||||
version: 0.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
version: 0.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||||
@@ -436,7 +442,7 @@ importers:
|
|||||||
version: 2.0.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
version: 2.0.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||||
styled-jsx:
|
styled-jsx:
|
||||||
specifier: 5.1.6
|
specifier: 5.1.6
|
||||||
version: 5.1.6(@babel/core@7.28.6)(react@19.2.3)
|
version: 5.1.6(react@19.2.3)
|
||||||
tailwind-merge:
|
tailwind-merge:
|
||||||
specifier: ^3.4.0
|
specifier: ^3.4.0
|
||||||
version: 3.4.0
|
version: 3.4.0
|
||||||
@@ -738,6 +744,75 @@ packages:
|
|||||||
'@codemirror/view@6.39.10':
|
'@codemirror/view@6.39.10':
|
||||||
resolution: {integrity: sha512-QfT/PXhiiP76PxMnX0RQVPDQrqfRt9wr9QhInNHnEUu4PWoNS8QwwcIDEneXFChJv22y+Yu/Cz5lFMTPz+h16w==}
|
resolution: {integrity: sha512-QfT/PXhiiP76PxMnX0RQVPDQrqfRt9wr9QhInNHnEUu4PWoNS8QwwcIDEneXFChJv22y+Yu/Cz5lFMTPz+h16w==}
|
||||||
|
|
||||||
|
'@commitlint/cli@20.4.2':
|
||||||
|
resolution: {integrity: sha512-YjYSX2yj/WsVoxh9mNiymfFS2ADbg2EK4+1WAsMuckwKMCqJ5PDG0CJU/8GvmHWcv4VRB2V02KqSiecRksWqZQ==}
|
||||||
|
engines: {node: '>=v18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
'@commitlint/config-conventional@20.4.2':
|
||||||
|
resolution: {integrity: sha512-rwkTF55q7Q+6dpSKUmJoScV0f3EpDlWKw2UPzklkLS4o5krMN1tPWAVOgHRtyUTMneIapLeQwaCjn44Td6OzBQ==}
|
||||||
|
engines: {node: '>=v18'}
|
||||||
|
|
||||||
|
'@commitlint/config-validator@20.4.0':
|
||||||
|
resolution: {integrity: sha512-zShmKTF+sqyNOfAE0vKcqnpvVpG0YX8F9G/ZIQHI2CoKyK+PSdladXMSns400aZ5/QZs+0fN75B//3Q5CHw++w==}
|
||||||
|
engines: {node: '>=v18'}
|
||||||
|
|
||||||
|
'@commitlint/ensure@20.4.1':
|
||||||
|
resolution: {integrity: sha512-WLQqaFx1pBooiVvBrA1YfJNFqZF8wS/YGOtr5RzApDbV9tQ52qT5VkTsY65hFTnXhW8PcDfZLaknfJTmPejmlw==}
|
||||||
|
engines: {node: '>=v18'}
|
||||||
|
|
||||||
|
'@commitlint/execute-rule@20.0.0':
|
||||||
|
resolution: {integrity: sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==}
|
||||||
|
engines: {node: '>=v18'}
|
||||||
|
|
||||||
|
'@commitlint/format@20.4.0':
|
||||||
|
resolution: {integrity: sha512-i3ki3WR0rgolFVX6r64poBHXM1t8qlFel1G1eCBvVgntE3fCJitmzSvH5JD/KVJN/snz6TfaX2CLdON7+s4WVQ==}
|
||||||
|
engines: {node: '>=v18'}
|
||||||
|
|
||||||
|
'@commitlint/is-ignored@20.4.1':
|
||||||
|
resolution: {integrity: sha512-In5EO4JR1lNsAv1oOBBO24V9ND1IqdAJDKZiEpdfjDl2HMasAcT7oA+5BKONv1pRoLG380DGPE2W2RIcUwdgLA==}
|
||||||
|
engines: {node: '>=v18'}
|
||||||
|
|
||||||
|
'@commitlint/lint@20.4.2':
|
||||||
|
resolution: {integrity: sha512-buquzNRtFng6xjXvBU1abY/WPEEjCgUipNQrNmIWe8QuJ6LWLtei/LDBAzEe5ASm45+Q9L2Xi3/GVvlj50GAug==}
|
||||||
|
engines: {node: '>=v18'}
|
||||||
|
|
||||||
|
'@commitlint/load@20.4.0':
|
||||||
|
resolution: {integrity: sha512-Dauup/GfjwffBXRJUdlX/YRKfSVXsXZLnINXKz0VZkXdKDcaEILAi9oflHGbfydonJnJAbXEbF3nXPm9rm3G6A==}
|
||||||
|
engines: {node: '>=v18'}
|
||||||
|
|
||||||
|
'@commitlint/message@20.4.0':
|
||||||
|
resolution: {integrity: sha512-B5lGtvHgiLAIsK5nLINzVW0bN5hXv+EW35sKhYHE8F7V9Uz1fR4tx3wt7mobA5UNhZKUNgB/+ldVMQE6IHZRyA==}
|
||||||
|
engines: {node: '>=v18'}
|
||||||
|
|
||||||
|
'@commitlint/parse@20.4.1':
|
||||||
|
resolution: {integrity: sha512-XNtZjeRcFuAfUnhYrCY02+mpxwY4OmnvD3ETbVPs25xJFFz1nRo/25nHj+5eM+zTeRFvWFwD4GXWU2JEtoK1/w==}
|
||||||
|
engines: {node: '>=v18'}
|
||||||
|
|
||||||
|
'@commitlint/read@20.4.0':
|
||||||
|
resolution: {integrity: sha512-QfpFn6/I240ySEGv7YWqho4vxqtPpx40FS7kZZDjUJ+eHxu3azfhy7fFb5XzfTqVNp1hNoI3tEmiEPbDB44+cg==}
|
||||||
|
engines: {node: '>=v18'}
|
||||||
|
|
||||||
|
'@commitlint/resolve-extends@20.4.0':
|
||||||
|
resolution: {integrity: sha512-ay1KM8q0t+/OnlpqXJ+7gEFQNlUtSU5Gxr8GEwnVf2TPN3+ywc5DzL3JCxmpucqxfHBTFwfRMXxPRRnR5Ki20g==}
|
||||||
|
engines: {node: '>=v18'}
|
||||||
|
|
||||||
|
'@commitlint/rules@20.4.2':
|
||||||
|
resolution: {integrity: sha512-oz83pnp5Yq6uwwTAabuVQPNlPfeD2Y5ZjMb7Wx8FSUlu4sLYJjbBWt8031Z0osCFPfHzAwSYrjnfDFKtuSMdKg==}
|
||||||
|
engines: {node: '>=v18'}
|
||||||
|
|
||||||
|
'@commitlint/to-lines@20.0.0':
|
||||||
|
resolution: {integrity: sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw==}
|
||||||
|
engines: {node: '>=v18'}
|
||||||
|
|
||||||
|
'@commitlint/top-level@20.4.0':
|
||||||
|
resolution: {integrity: sha512-NDzq8Q6jmFaIIBC/GG6n1OQEaHdmaAAYdrZRlMgW6glYWGZ+IeuXmiymDvQNXPc82mVxq2KiE3RVpcs+1OeDeA==}
|
||||||
|
engines: {node: '>=v18'}
|
||||||
|
|
||||||
|
'@commitlint/types@20.4.0':
|
||||||
|
resolution: {integrity: sha512-aO5l99BQJ0X34ft8b0h7QFkQlqxC6e7ZPVmBKz13xM9O8obDaM1Cld4sQlJDXXU/VFuUzQ30mVtHjVz74TuStw==}
|
||||||
|
engines: {node: '>=v18'}
|
||||||
|
|
||||||
'@date-fns/tz@1.4.1':
|
'@date-fns/tz@1.4.1':
|
||||||
resolution: {integrity: sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==}
|
resolution: {integrity: sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==}
|
||||||
|
|
||||||
@@ -3335,6 +3410,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==}
|
resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
array-ify@1.0.0:
|
||||||
|
resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==}
|
||||||
|
|
||||||
array-includes@3.1.9:
|
array-includes@3.1.9:
|
||||||
resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==}
|
resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -3559,6 +3637,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
|
resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
|
||||||
engines: {node: '>= 12'}
|
engines: {node: '>= 12'}
|
||||||
|
|
||||||
|
compare-func@2.0.0:
|
||||||
|
resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==}
|
||||||
|
|
||||||
concat-map@0.0.1:
|
concat-map@0.0.1:
|
||||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||||
|
|
||||||
@@ -3573,6 +3654,19 @@ packages:
|
|||||||
resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
|
resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
conventional-changelog-angular@8.1.0:
|
||||||
|
resolution: {integrity: sha512-GGf2Nipn1RUCAktxuVauVr1e3r8QrLP/B0lEUsFktmGqc3ddbQkhoJZHJctVU829U1c6mTSWftrVOCHaL85Q3w==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
conventional-changelog-conventionalcommits@9.1.0:
|
||||||
|
resolution: {integrity: sha512-MnbEysR8wWa8dAEvbj5xcBgJKQlX/m0lhS8DsyAAWDHdfs2faDJxTgzRYlRYpXSe7UiKrIIlB4TrBKU9q9DgkA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
conventional-commits-parser@6.2.1:
|
||||||
|
resolution: {integrity: sha512-20pyHgnO40rvfI0NGF/xiEoFMkXDtkF8FwHvk5BokoFoCuTQRI8vrNCNFWUOfuolKJMm1tPCHc8GgYEtr1XRNA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
convert-source-map@2.0.0:
|
convert-source-map@2.0.0:
|
||||||
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
||||||
|
|
||||||
@@ -3598,6 +3692,14 @@ packages:
|
|||||||
cose-base@2.2.0:
|
cose-base@2.2.0:
|
||||||
resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==}
|
resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==}
|
||||||
|
|
||||||
|
cosmiconfig-typescript-loader@6.2.0:
|
||||||
|
resolution: {integrity: sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==}
|
||||||
|
engines: {node: '>=v18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '*'
|
||||||
|
cosmiconfig: '>=9'
|
||||||
|
typescript: '>=5'
|
||||||
|
|
||||||
cosmiconfig@9.0.0:
|
cosmiconfig@9.0.0:
|
||||||
resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==}
|
resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
@@ -3786,6 +3888,10 @@ packages:
|
|||||||
damerau-levenshtein@1.0.8:
|
damerau-levenshtein@1.0.8:
|
||||||
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
|
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
|
||||||
|
|
||||||
|
dargs@8.1.0:
|
||||||
|
resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
data-uri-to-buffer@4.0.1:
|
data-uri-to-buffer@4.0.1:
|
||||||
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
|
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
|
||||||
engines: {node: '>= 12'}
|
engines: {node: '>= 12'}
|
||||||
@@ -3926,6 +4032,10 @@ packages:
|
|||||||
dompurify@3.3.1:
|
dompurify@3.3.1:
|
||||||
resolution: {integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==}
|
resolution: {integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==}
|
||||||
|
|
||||||
|
dot-prop@5.3.0:
|
||||||
|
resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
dotenv@17.2.3:
|
dotenv@17.2.3:
|
||||||
resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==}
|
resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@@ -4391,6 +4501,11 @@ packages:
|
|||||||
get-tsconfig@4.13.0:
|
get-tsconfig@4.13.0:
|
||||||
resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==}
|
resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==}
|
||||||
|
|
||||||
|
git-raw-commits@4.0.0:
|
||||||
|
resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==}
|
||||||
|
engines: {node: '>=16'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
glob-parent@5.1.2:
|
glob-parent@5.1.2:
|
||||||
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
@@ -4399,6 +4514,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
|
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
|
||||||
engines: {node: '>=10.13.0'}
|
engines: {node: '>=10.13.0'}
|
||||||
|
|
||||||
|
global-directory@4.0.1:
|
||||||
|
resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
globals@14.0.0:
|
globals@14.0.0:
|
||||||
resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
|
resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -4531,6 +4650,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
|
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
import-meta-resolve@4.2.0:
|
||||||
|
resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==}
|
||||||
|
|
||||||
imurmurhash@0.1.4:
|
imurmurhash@0.1.4:
|
||||||
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
|
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
|
||||||
engines: {node: '>=0.8.19'}
|
engines: {node: '>=0.8.19'}
|
||||||
@@ -4538,6 +4660,10 @@ packages:
|
|||||||
inherits@2.0.4:
|
inherits@2.0.4:
|
||||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||||
|
|
||||||
|
ini@4.1.1:
|
||||||
|
resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==}
|
||||||
|
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||||
|
|
||||||
inline-style-parser@0.2.7:
|
inline-style-parser@0.2.7:
|
||||||
resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==}
|
resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==}
|
||||||
|
|
||||||
@@ -4669,6 +4795,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||||
engines: {node: '>=0.12.0'}
|
engines: {node: '>=0.12.0'}
|
||||||
|
|
||||||
|
is-obj@2.0.0:
|
||||||
|
resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
is-obj@3.0.0:
|
is-obj@3.0.0:
|
||||||
resolution: {integrity: sha512-IlsXEHOjtKhpN8r/tRFj2nDyTmHvcfNeu/nrRIcXE17ROeatXchkojffa1SpdqW4cr/Fj6QkEf/Gn4zf6KKvEQ==}
|
resolution: {integrity: sha512-IlsXEHOjtKhpN8r/tRFj2nDyTmHvcfNeu/nrRIcXE17ROeatXchkojffa1SpdqW4cr/Fj6QkEf/Gn4zf6KKvEQ==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@@ -4962,9 +5092,27 @@ packages:
|
|||||||
lodash-es@4.17.23:
|
lodash-es@4.17.23:
|
||||||
resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==}
|
resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==}
|
||||||
|
|
||||||
|
lodash.camelcase@4.3.0:
|
||||||
|
resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
|
||||||
|
|
||||||
|
lodash.kebabcase@4.1.1:
|
||||||
|
resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==}
|
||||||
|
|
||||||
lodash.merge@4.6.2:
|
lodash.merge@4.6.2:
|
||||||
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||||
|
|
||||||
|
lodash.mergewith@4.6.2:
|
||||||
|
resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==}
|
||||||
|
|
||||||
|
lodash.snakecase@4.1.1:
|
||||||
|
resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==}
|
||||||
|
|
||||||
|
lodash.startcase@4.4.0:
|
||||||
|
resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==}
|
||||||
|
|
||||||
|
lodash.upperfirst@4.3.1:
|
||||||
|
resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==}
|
||||||
|
|
||||||
lodash@4.17.21:
|
lodash@4.17.21:
|
||||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||||
|
|
||||||
@@ -5073,6 +5221,14 @@ packages:
|
|||||||
resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==}
|
resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
meow@12.1.1:
|
||||||
|
resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==}
|
||||||
|
engines: {node: '>=16.10'}
|
||||||
|
|
||||||
|
meow@13.2.0:
|
||||||
|
resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
merge-descriptors@2.0.0:
|
merge-descriptors@2.0.0:
|
||||||
resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==}
|
resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -5787,6 +5943,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
resolve-from@5.0.0:
|
||||||
|
resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
resolve-pkg-maps@1.0.0:
|
resolve-pkg-maps@1.0.0:
|
||||||
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
|
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
|
||||||
|
|
||||||
@@ -5962,6 +6122,10 @@ packages:
|
|||||||
space-separated-tokens@2.0.2:
|
space-separated-tokens@2.0.2:
|
||||||
resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
|
resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
|
||||||
|
|
||||||
|
split2@4.2.0:
|
||||||
|
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
|
||||||
|
engines: {node: '>= 10.x'}
|
||||||
|
|
||||||
stable-hash@0.0.5:
|
stable-hash@0.0.5:
|
||||||
resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==}
|
resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==}
|
||||||
|
|
||||||
@@ -6983,6 +7147,115 @@ snapshots:
|
|||||||
style-mod: 4.1.3
|
style-mod: 4.1.3
|
||||||
w3c-keyname: 2.2.8
|
w3c-keyname: 2.2.8
|
||||||
|
|
||||||
|
'@commitlint/cli@20.4.2(@types/node@20.19.28)(typescript@5.9.3)':
|
||||||
|
dependencies:
|
||||||
|
'@commitlint/format': 20.4.0
|
||||||
|
'@commitlint/lint': 20.4.2
|
||||||
|
'@commitlint/load': 20.4.0(@types/node@20.19.28)(typescript@5.9.3)
|
||||||
|
'@commitlint/read': 20.4.0
|
||||||
|
'@commitlint/types': 20.4.0
|
||||||
|
tinyexec: 1.0.2
|
||||||
|
yargs: 17.7.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@types/node'
|
||||||
|
- typescript
|
||||||
|
|
||||||
|
'@commitlint/config-conventional@20.4.2':
|
||||||
|
dependencies:
|
||||||
|
'@commitlint/types': 20.4.0
|
||||||
|
conventional-changelog-conventionalcommits: 9.1.0
|
||||||
|
|
||||||
|
'@commitlint/config-validator@20.4.0':
|
||||||
|
dependencies:
|
||||||
|
'@commitlint/types': 20.4.0
|
||||||
|
ajv: 8.17.1
|
||||||
|
|
||||||
|
'@commitlint/ensure@20.4.1':
|
||||||
|
dependencies:
|
||||||
|
'@commitlint/types': 20.4.0
|
||||||
|
lodash.camelcase: 4.3.0
|
||||||
|
lodash.kebabcase: 4.1.1
|
||||||
|
lodash.snakecase: 4.1.1
|
||||||
|
lodash.startcase: 4.4.0
|
||||||
|
lodash.upperfirst: 4.3.1
|
||||||
|
|
||||||
|
'@commitlint/execute-rule@20.0.0': {}
|
||||||
|
|
||||||
|
'@commitlint/format@20.4.0':
|
||||||
|
dependencies:
|
||||||
|
'@commitlint/types': 20.4.0
|
||||||
|
picocolors: 1.1.1
|
||||||
|
|
||||||
|
'@commitlint/is-ignored@20.4.1':
|
||||||
|
dependencies:
|
||||||
|
'@commitlint/types': 20.4.0
|
||||||
|
semver: 7.7.4
|
||||||
|
|
||||||
|
'@commitlint/lint@20.4.2':
|
||||||
|
dependencies:
|
||||||
|
'@commitlint/is-ignored': 20.4.1
|
||||||
|
'@commitlint/parse': 20.4.1
|
||||||
|
'@commitlint/rules': 20.4.2
|
||||||
|
'@commitlint/types': 20.4.0
|
||||||
|
|
||||||
|
'@commitlint/load@20.4.0(@types/node@20.19.28)(typescript@5.9.3)':
|
||||||
|
dependencies:
|
||||||
|
'@commitlint/config-validator': 20.4.0
|
||||||
|
'@commitlint/execute-rule': 20.0.0
|
||||||
|
'@commitlint/resolve-extends': 20.4.0
|
||||||
|
'@commitlint/types': 20.4.0
|
||||||
|
cosmiconfig: 9.0.0(typescript@5.9.3)
|
||||||
|
cosmiconfig-typescript-loader: 6.2.0(@types/node@20.19.28)(cosmiconfig@9.0.0(typescript@5.9.3))(typescript@5.9.3)
|
||||||
|
is-plain-obj: 4.1.0
|
||||||
|
lodash.mergewith: 4.6.2
|
||||||
|
picocolors: 1.1.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@types/node'
|
||||||
|
- typescript
|
||||||
|
|
||||||
|
'@commitlint/message@20.4.0': {}
|
||||||
|
|
||||||
|
'@commitlint/parse@20.4.1':
|
||||||
|
dependencies:
|
||||||
|
'@commitlint/types': 20.4.0
|
||||||
|
conventional-changelog-angular: 8.1.0
|
||||||
|
conventional-commits-parser: 6.2.1
|
||||||
|
|
||||||
|
'@commitlint/read@20.4.0':
|
||||||
|
dependencies:
|
||||||
|
'@commitlint/top-level': 20.4.0
|
||||||
|
'@commitlint/types': 20.4.0
|
||||||
|
git-raw-commits: 4.0.0
|
||||||
|
minimist: 1.2.8
|
||||||
|
tinyexec: 1.0.2
|
||||||
|
|
||||||
|
'@commitlint/resolve-extends@20.4.0':
|
||||||
|
dependencies:
|
||||||
|
'@commitlint/config-validator': 20.4.0
|
||||||
|
'@commitlint/types': 20.4.0
|
||||||
|
global-directory: 4.0.1
|
||||||
|
import-meta-resolve: 4.2.0
|
||||||
|
lodash.mergewith: 4.6.2
|
||||||
|
resolve-from: 5.0.0
|
||||||
|
|
||||||
|
'@commitlint/rules@20.4.2':
|
||||||
|
dependencies:
|
||||||
|
'@commitlint/ensure': 20.4.1
|
||||||
|
'@commitlint/message': 20.4.0
|
||||||
|
'@commitlint/to-lines': 20.0.0
|
||||||
|
'@commitlint/types': 20.4.0
|
||||||
|
|
||||||
|
'@commitlint/to-lines@20.0.0': {}
|
||||||
|
|
||||||
|
'@commitlint/top-level@20.4.0':
|
||||||
|
dependencies:
|
||||||
|
escalade: 3.2.0
|
||||||
|
|
||||||
|
'@commitlint/types@20.4.0':
|
||||||
|
dependencies:
|
||||||
|
conventional-commits-parser: 6.2.1
|
||||||
|
picocolors: 1.1.1
|
||||||
|
|
||||||
'@date-fns/tz@1.4.1': {}
|
'@date-fns/tz@1.4.1': {}
|
||||||
|
|
||||||
'@dnd-kit/accessibility@3.1.1(react@19.2.3)':
|
'@dnd-kit/accessibility@3.1.1(react@19.2.3)':
|
||||||
@@ -9413,6 +9686,8 @@ snapshots:
|
|||||||
call-bound: 1.0.4
|
call-bound: 1.0.4
|
||||||
is-array-buffer: 3.0.5
|
is-array-buffer: 3.0.5
|
||||||
|
|
||||||
|
array-ify@1.0.0: {}
|
||||||
|
|
||||||
array-includes@3.1.9:
|
array-includes@3.1.9:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.8
|
call-bind: 1.0.8
|
||||||
@@ -9674,6 +9949,11 @@ snapshots:
|
|||||||
|
|
||||||
commander@8.3.0: {}
|
commander@8.3.0: {}
|
||||||
|
|
||||||
|
compare-func@2.0.0:
|
||||||
|
dependencies:
|
||||||
|
array-ify: 1.0.0
|
||||||
|
dot-prop: 5.3.0
|
||||||
|
|
||||||
concat-map@0.0.1: {}
|
concat-map@0.0.1: {}
|
||||||
|
|
||||||
confbox@0.1.8: {}
|
confbox@0.1.8: {}
|
||||||
@@ -9682,6 +9962,18 @@ snapshots:
|
|||||||
|
|
||||||
content-type@1.0.5: {}
|
content-type@1.0.5: {}
|
||||||
|
|
||||||
|
conventional-changelog-angular@8.1.0:
|
||||||
|
dependencies:
|
||||||
|
compare-func: 2.0.0
|
||||||
|
|
||||||
|
conventional-changelog-conventionalcommits@9.1.0:
|
||||||
|
dependencies:
|
||||||
|
compare-func: 2.0.0
|
||||||
|
|
||||||
|
conventional-commits-parser@6.2.1:
|
||||||
|
dependencies:
|
||||||
|
meow: 13.2.0
|
||||||
|
|
||||||
convert-source-map@2.0.0: {}
|
convert-source-map@2.0.0: {}
|
||||||
|
|
||||||
cookie-signature@1.2.2: {}
|
cookie-signature@1.2.2: {}
|
||||||
@@ -9703,6 +9995,13 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
layout-base: 2.0.1
|
layout-base: 2.0.1
|
||||||
|
|
||||||
|
cosmiconfig-typescript-loader@6.2.0(@types/node@20.19.28)(cosmiconfig@9.0.0(typescript@5.9.3))(typescript@5.9.3):
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 20.19.28
|
||||||
|
cosmiconfig: 9.0.0(typescript@5.9.3)
|
||||||
|
jiti: 2.6.1
|
||||||
|
typescript: 5.9.3
|
||||||
|
|
||||||
cosmiconfig@9.0.0(typescript@5.9.3):
|
cosmiconfig@9.0.0(typescript@5.9.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
env-paths: 2.2.1
|
env-paths: 2.2.1
|
||||||
@@ -9914,6 +10213,8 @@ snapshots:
|
|||||||
|
|
||||||
damerau-levenshtein@1.0.8: {}
|
damerau-levenshtein@1.0.8: {}
|
||||||
|
|
||||||
|
dargs@8.1.0: {}
|
||||||
|
|
||||||
data-uri-to-buffer@4.0.1: {}
|
data-uri-to-buffer@4.0.1: {}
|
||||||
|
|
||||||
data-view-buffer@1.0.2:
|
data-view-buffer@1.0.2:
|
||||||
@@ -10035,6 +10336,10 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/trusted-types': 2.0.7
|
'@types/trusted-types': 2.0.7
|
||||||
|
|
||||||
|
dot-prop@5.3.0:
|
||||||
|
dependencies:
|
||||||
|
is-obj: 2.0.0
|
||||||
|
|
||||||
dotenv@17.2.3: {}
|
dotenv@17.2.3: {}
|
||||||
|
|
||||||
dunder-proto@1.0.1:
|
dunder-proto@1.0.1:
|
||||||
@@ -10256,8 +10561,8 @@ snapshots:
|
|||||||
'@next/eslint-plugin-next': 16.1.1
|
'@next/eslint-plugin-next': 16.1.1
|
||||||
eslint: 9.39.2(jiti@2.6.1)
|
eslint: 9.39.2(jiti@2.6.1)
|
||||||
eslint-import-resolver-node: 0.3.9
|
eslint-import-resolver-node: 0.3.9
|
||||||
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1))
|
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))
|
||||||
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
|
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))
|
||||||
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@2.6.1))
|
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@2.6.1))
|
||||||
eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@2.6.1))
|
eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@2.6.1))
|
||||||
eslint-plugin-react-hooks: 7.0.1(eslint@9.39.2(jiti@2.6.1))
|
eslint-plugin-react-hooks: 7.0.1(eslint@9.39.2(jiti@2.6.1))
|
||||||
@@ -10279,7 +10584,7 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)):
|
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@nolyfill/is-core-module': 1.0.39
|
'@nolyfill/is-core-module': 1.0.39
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
@@ -10290,22 +10595,22 @@ snapshots:
|
|||||||
tinyglobby: 0.2.15
|
tinyglobby: 0.2.15
|
||||||
unrs-resolver: 1.11.1
|
unrs-resolver: 1.11.1
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
|
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)):
|
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)):
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@typescript-eslint/parser': 8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
|
'@typescript-eslint/parser': 8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
|
||||||
eslint: 9.39.2(jiti@2.6.1)
|
eslint: 9.39.2(jiti@2.6.1)
|
||||||
eslint-import-resolver-node: 0.3.9
|
eslint-import-resolver-node: 0.3.9
|
||||||
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1))
|
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)):
|
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@rtsao/scc': 1.1.0
|
'@rtsao/scc': 1.1.0
|
||||||
array-includes: 3.1.9
|
array-includes: 3.1.9
|
||||||
@@ -10316,7 +10621,7 @@ snapshots:
|
|||||||
doctrine: 2.1.0
|
doctrine: 2.1.0
|
||||||
eslint: 9.39.2(jiti@2.6.1)
|
eslint: 9.39.2(jiti@2.6.1)
|
||||||
eslint-import-resolver-node: 0.3.9
|
eslint-import-resolver-node: 0.3.9
|
||||||
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
|
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))
|
||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
is-core-module: 2.16.1
|
is-core-module: 2.16.1
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
@@ -10737,6 +11042,12 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
resolve-pkg-maps: 1.0.0
|
resolve-pkg-maps: 1.0.0
|
||||||
|
|
||||||
|
git-raw-commits@4.0.0:
|
||||||
|
dependencies:
|
||||||
|
dargs: 8.1.0
|
||||||
|
meow: 12.1.1
|
||||||
|
split2: 4.2.0
|
||||||
|
|
||||||
glob-parent@5.1.2:
|
glob-parent@5.1.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
@@ -10745,6 +11056,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
|
|
||||||
|
global-directory@4.0.1:
|
||||||
|
dependencies:
|
||||||
|
ini: 4.1.1
|
||||||
|
|
||||||
globals@14.0.0: {}
|
globals@14.0.0: {}
|
||||||
|
|
||||||
globals@16.4.0: {}
|
globals@16.4.0: {}
|
||||||
@@ -10889,10 +11204,14 @@ snapshots:
|
|||||||
parent-module: 1.0.1
|
parent-module: 1.0.1
|
||||||
resolve-from: 4.0.0
|
resolve-from: 4.0.0
|
||||||
|
|
||||||
|
import-meta-resolve@4.2.0: {}
|
||||||
|
|
||||||
imurmurhash@0.1.4: {}
|
imurmurhash@0.1.4: {}
|
||||||
|
|
||||||
inherits@2.0.4: {}
|
inherits@2.0.4: {}
|
||||||
|
|
||||||
|
ini@4.1.1: {}
|
||||||
|
|
||||||
inline-style-parser@0.2.7: {}
|
inline-style-parser@0.2.7: {}
|
||||||
|
|
||||||
input-otp@1.4.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
|
input-otp@1.4.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
|
||||||
@@ -11012,6 +11331,8 @@ snapshots:
|
|||||||
|
|
||||||
is-number@7.0.0: {}
|
is-number@7.0.0: {}
|
||||||
|
|
||||||
|
is-obj@2.0.0: {}
|
||||||
|
|
||||||
is-obj@3.0.0: {}
|
is-obj@3.0.0: {}
|
||||||
|
|
||||||
is-plain-obj@4.1.0: {}
|
is-plain-obj@4.1.0: {}
|
||||||
@@ -11248,8 +11569,20 @@ snapshots:
|
|||||||
|
|
||||||
lodash-es@4.17.23: {}
|
lodash-es@4.17.23: {}
|
||||||
|
|
||||||
|
lodash.camelcase@4.3.0: {}
|
||||||
|
|
||||||
|
lodash.kebabcase@4.1.1: {}
|
||||||
|
|
||||||
lodash.merge@4.6.2: {}
|
lodash.merge@4.6.2: {}
|
||||||
|
|
||||||
|
lodash.mergewith@4.6.2: {}
|
||||||
|
|
||||||
|
lodash.snakecase@4.1.1: {}
|
||||||
|
|
||||||
|
lodash.startcase@4.4.0: {}
|
||||||
|
|
||||||
|
lodash.upperfirst@4.3.1: {}
|
||||||
|
|
||||||
lodash@4.17.21: {}
|
lodash@4.17.21: {}
|
||||||
|
|
||||||
log-symbols@6.0.0:
|
log-symbols@6.0.0:
|
||||||
@@ -11464,6 +11797,10 @@ snapshots:
|
|||||||
|
|
||||||
media-typer@1.1.0: {}
|
media-typer@1.1.0: {}
|
||||||
|
|
||||||
|
meow@12.1.1: {}
|
||||||
|
|
||||||
|
meow@13.2.0: {}
|
||||||
|
|
||||||
merge-descriptors@2.0.0: {}
|
merge-descriptors@2.0.0: {}
|
||||||
|
|
||||||
merge-stream@2.0.0: {}
|
merge-stream@2.0.0: {}
|
||||||
@@ -11779,7 +12116,7 @@ snapshots:
|
|||||||
react: 19.2.3
|
react: 19.2.3
|
||||||
react-dom: 19.2.3(react@19.2.3)
|
react-dom: 19.2.3(react@19.2.3)
|
||||||
|
|
||||||
next@16.1.1(@babel/core@7.28.6)(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
|
next@16.1.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@next/env': 16.1.1
|
'@next/env': 16.1.1
|
||||||
'@swc/helpers': 0.5.15
|
'@swc/helpers': 0.5.15
|
||||||
@@ -11788,7 +12125,7 @@ snapshots:
|
|||||||
postcss: 8.4.31
|
postcss: 8.4.31
|
||||||
react: 19.2.3
|
react: 19.2.3
|
||||||
react-dom: 19.2.3(react@19.2.3)
|
react-dom: 19.2.3(react@19.2.3)
|
||||||
styled-jsx: 5.1.6(@babel/core@7.28.6)(react@19.2.3)
|
styled-jsx: 5.1.6(react@19.2.3)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@next/swc-darwin-arm64': 16.1.1
|
'@next/swc-darwin-arm64': 16.1.1
|
||||||
'@next/swc-darwin-x64': 16.1.1
|
'@next/swc-darwin-x64': 16.1.1
|
||||||
@@ -12466,6 +12803,8 @@ snapshots:
|
|||||||
|
|
||||||
resolve-from@4.0.0: {}
|
resolve-from@4.0.0: {}
|
||||||
|
|
||||||
|
resolve-from@5.0.0: {}
|
||||||
|
|
||||||
resolve-pkg-maps@1.0.0: {}
|
resolve-pkg-maps@1.0.0: {}
|
||||||
|
|
||||||
resolve@1.22.11:
|
resolve@1.22.11:
|
||||||
@@ -12773,6 +13112,8 @@ snapshots:
|
|||||||
|
|
||||||
space-separated-tokens@2.0.2: {}
|
space-separated-tokens@2.0.2: {}
|
||||||
|
|
||||||
|
split2@4.2.0: {}
|
||||||
|
|
||||||
stable-hash@0.0.5: {}
|
stable-hash@0.0.5: {}
|
||||||
|
|
||||||
stackback@0.0.2: {}
|
stackback@0.0.2: {}
|
||||||
@@ -12897,12 +13238,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
inline-style-parser: 0.2.7
|
inline-style-parser: 0.2.7
|
||||||
|
|
||||||
styled-jsx@5.1.6(@babel/core@7.28.6)(react@19.2.3):
|
styled-jsx@5.1.6(react@19.2.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
client-only: 0.0.1
|
client-only: 0.0.1
|
||||||
react: 19.2.3
|
react: 19.2.3
|
||||||
optionalDependencies:
|
|
||||||
'@babel/core': 7.28.6
|
|
||||||
|
|
||||||
stylis@4.3.6: {}
|
stylis@4.3.6: {}
|
||||||
|
|
||||||
|
|||||||
+3
-45
@@ -9,8 +9,9 @@ import {
|
|||||||
listTaskSessionMessages,
|
listTaskSessionMessages,
|
||||||
} from "@/lib/api";
|
} from "@/lib/api";
|
||||||
import { snapshotToState } from "@/lib/ssr/mapper";
|
import { snapshotToState } from "@/lib/ssr/mapper";
|
||||||
|
import { mapUserSettingsResponse } from "@/lib/ssr/user-settings";
|
||||||
import type { AppState } from "@/lib/state/store";
|
import type { AppState } from "@/lib/state/store";
|
||||||
import type { ListWorkspacesResponse, UserSettings, UserSettingsResponse } from "@/lib/types/http";
|
import type { ListWorkspacesResponse, UserSettingsResponse } from "@/lib/types/http";
|
||||||
|
|
||||||
// Server Component: runs on the server for SSR and data hydration.
|
// Server Component: runs on the server for SSR and data hydration.
|
||||||
type PageProps = {
|
type PageProps = {
|
||||||
@@ -36,54 +37,11 @@ function mapWorkspaceItem(ws: WorkspaceItem) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildLspConfig(s: UserSettings) {
|
|
||||||
return {
|
|
||||||
lspAutoStartLanguages: s.lsp_auto_start_languages ?? [],
|
|
||||||
lspAutoInstallLanguages: s.lsp_auto_install_languages ?? [],
|
|
||||||
lspServerConfigs: s.lsp_server_configs ?? {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildUserSettingsState(
|
function buildUserSettingsState(
|
||||||
resp: UserSettingsResponse | null,
|
resp: UserSettingsResponse | null,
|
||||||
workspaceId: string | null,
|
workspaceId: string | null,
|
||||||
): AppState["userSettings"] {
|
): AppState["userSettings"] {
|
||||||
const s = resp?.settings;
|
return { ...mapUserSettingsResponse(resp), workspaceId };
|
||||||
const shellOptions = resp ? (resp.shell_options ?? []) : [];
|
|
||||||
if (!s) {
|
|
||||||
return {
|
|
||||||
workspaceId,
|
|
||||||
workflowId: null,
|
|
||||||
kanbanViewMode: null,
|
|
||||||
repositoryIds: [],
|
|
||||||
preferredShell: null,
|
|
||||||
shellOptions,
|
|
||||||
defaultEditorId: null,
|
|
||||||
enablePreviewOnClick: false,
|
|
||||||
chatSubmitKey: "cmd_enter",
|
|
||||||
reviewAutoMarkOnScroll: true,
|
|
||||||
savedLayouts: [],
|
|
||||||
lspAutoStartLanguages: [],
|
|
||||||
lspAutoInstallLanguages: [],
|
|
||||||
lspServerConfigs: {},
|
|
||||||
loaded: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
workspaceId,
|
|
||||||
workflowId: s.workflow_filter_id || null,
|
|
||||||
kanbanViewMode: s.kanban_view_mode || null,
|
|
||||||
repositoryIds: Array.from(new Set(s.repository_ids ?? [])).sort(),
|
|
||||||
preferredShell: s.preferred_shell || null,
|
|
||||||
shellOptions,
|
|
||||||
defaultEditorId: s.default_editor_id || null,
|
|
||||||
enablePreviewOnClick: s.enable_preview_on_click ?? false,
|
|
||||||
chatSubmitKey: s.chat_submit_key ?? "cmd_enter",
|
|
||||||
reviewAutoMarkOnScroll: s.review_auto_mark_on_scroll ?? true,
|
|
||||||
savedLayouts: s.saved_layouts ?? [],
|
|
||||||
...buildLspConfig(s),
|
|
||||||
loaded: true,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveActiveId<T extends { id: string }>(
|
function resolveActiveId<T extends { id: string }>(
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import type {
|
|||||||
} from "@/lib/types/http";
|
} from "@/lib/types/http";
|
||||||
import type { Terminal } from "@/hooks/domains/session/use-terminals";
|
import type { Terminal } from "@/hooks/domains/session/use-terminals";
|
||||||
import { snapshotToState, taskToState } from "@/lib/ssr/mapper";
|
import { snapshotToState, taskToState } from "@/lib/ssr/mapper";
|
||||||
|
import { mapUserSettingsResponse } from "@/lib/ssr/user-settings";
|
||||||
import { TaskPageContent } from "@/components/task/task-page-content";
|
import { TaskPageContent } from "@/components/task/task-page-content";
|
||||||
|
|
||||||
function buildWorktreeState(allSessions: TaskSession[]) {
|
function buildWorktreeState(allSessions: TaskSession[]) {
|
||||||
@@ -50,53 +51,6 @@ function buildWorktreeState(allSessions: TaskSession[]) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserSettingsSettings = NonNullable<UserSettingsResponse["settings"]>;
|
|
||||||
|
|
||||||
function buildLspSettings(s: UserSettingsSettings | undefined) {
|
|
||||||
return {
|
|
||||||
lspAutoStartLanguages: s?.lsp_auto_start_languages ?? [],
|
|
||||||
lspAutoInstallLanguages: s?.lsp_auto_install_languages ?? [],
|
|
||||||
lspServerConfigs: s?.lsp_server_configs ?? {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildUserSettingsCore(s: UserSettingsSettings) {
|
|
||||||
return {
|
|
||||||
workspaceId: s.workspace_id || null,
|
|
||||||
workflowId: s.workflow_filter_id || null,
|
|
||||||
kanbanViewMode: s.kanban_view_mode || null,
|
|
||||||
repositoryIds: Array.from(new Set(s.repository_ids ?? [])).sort(),
|
|
||||||
preferredShell: s.preferred_shell || null,
|
|
||||||
defaultEditorId: s.default_editor_id || null,
|
|
||||||
enablePreviewOnClick: s.enable_preview_on_click ?? false,
|
|
||||||
chatSubmitKey: s.chat_submit_key ?? "cmd_enter",
|
|
||||||
reviewAutoMarkOnScroll: s.review_auto_mark_on_scroll ?? true,
|
|
||||||
savedLayouts: s.saved_layouts ?? [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildUserSettingsFromResponse(userSettingsResponse: UserSettingsResponse | null) {
|
|
||||||
const s = userSettingsResponse?.settings;
|
|
||||||
const shellOptions = userSettingsResponse?.shell_options ?? [];
|
|
||||||
if (!s)
|
|
||||||
return {
|
|
||||||
workspaceId: null,
|
|
||||||
workflowId: null,
|
|
||||||
kanbanViewMode: null,
|
|
||||||
repositoryIds: [],
|
|
||||||
preferredShell: null,
|
|
||||||
shellOptions,
|
|
||||||
defaultEditorId: null,
|
|
||||||
enablePreviewOnClick: false,
|
|
||||||
chatSubmitKey: "cmd_enter" as const,
|
|
||||||
reviewAutoMarkOnScroll: true,
|
|
||||||
savedLayouts: [],
|
|
||||||
loaded: false,
|
|
||||||
...buildLspSettings(undefined),
|
|
||||||
};
|
|
||||||
return { ...buildUserSettingsCore(s), shellOptions, loaded: true, ...buildLspSettings(s) };
|
|
||||||
}
|
|
||||||
|
|
||||||
type BuildSessionPageStateParams = {
|
type BuildSessionPageStateParams = {
|
||||||
task: Task;
|
task: Task;
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
@@ -206,7 +160,7 @@ function buildSessionPageState(p: BuildSessionPageStateParams) {
|
|||||||
agentsLoaded: true,
|
agentsLoaded: true,
|
||||||
executorsLoaded: false,
|
executorsLoaded: false,
|
||||||
},
|
},
|
||||||
userSettings: buildUserSettingsFromResponse(userSettingsResponse),
|
userSettings: mapUserSettingsResponse(userSettingsResponse),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { ChangelogSettings } from "@/components/settings/changelog-settings";
|
||||||
|
import { StateProvider } from "@/components/state-provider";
|
||||||
|
import { fetchUserSettings } from "@/lib/api";
|
||||||
|
import { mapUserSettingsResponse } from "@/lib/ssr/user-settings";
|
||||||
|
|
||||||
|
export default async function ChangelogPage() {
|
||||||
|
let initialState = {};
|
||||||
|
try {
|
||||||
|
const response = await fetchUserSettings({ cache: "no-store" });
|
||||||
|
const mapped = mapUserSettingsResponse(response);
|
||||||
|
initialState = { userSettings: mapped.loaded ? mapped : undefined };
|
||||||
|
} catch {
|
||||||
|
initialState = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StateProvider initialState={initialState}>
|
||||||
|
<ChangelogSettings />
|
||||||
|
</StateProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { EditorsSettings } from "@/components/settings/editors-settings";
|
import { EditorsSettings } from "@/components/settings/editors-settings";
|
||||||
import { StateProvider } from "@/components/state-provider";
|
import { StateProvider } from "@/components/state-provider";
|
||||||
import { fetchUserSettings, listEditors } from "@/lib/api";
|
import { fetchUserSettings, listEditors } from "@/lib/api";
|
||||||
|
import { mapUserSettingsResponse } from "@/lib/ssr/user-settings";
|
||||||
|
|
||||||
export default async function GeneralEditorsPage() {
|
export default async function GeneralEditorsPage() {
|
||||||
let initialState = {};
|
let initialState = {};
|
||||||
@@ -9,25 +10,14 @@ export default async function GeneralEditorsPage() {
|
|||||||
listEditors({ cache: "no-store" }),
|
listEditors({ cache: "no-store" }),
|
||||||
fetchUserSettings({ cache: "no-store" }),
|
fetchUserSettings({ cache: "no-store" }),
|
||||||
]);
|
]);
|
||||||
const settings = settingsResponse.settings;
|
const mapped = mapUserSettingsResponse(settingsResponse);
|
||||||
initialState = {
|
initialState = {
|
||||||
editors: {
|
editors: {
|
||||||
items: editorsResponse.editors ?? [],
|
items: editorsResponse.editors ?? [],
|
||||||
loaded: true,
|
loaded: true,
|
||||||
loading: false,
|
loading: false,
|
||||||
},
|
},
|
||||||
userSettings: settings
|
userSettings: mapped.loaded ? mapped : undefined,
|
||||||
? {
|
|
||||||
workspaceId: settings.workspace_id ?? null,
|
|
||||||
workflowId: settings.workflow_filter_id ?? null,
|
|
||||||
kanbanViewMode: settings.kanban_view_mode ?? null,
|
|
||||||
repositoryIds: settings.repository_ids ?? [],
|
|
||||||
preferredShell: settings.preferred_shell ?? null,
|
|
||||||
shellOptions: settingsResponse.shell_options ?? [],
|
|
||||||
defaultEditorId: settings.default_editor_id ?? null,
|
|
||||||
loaded: true,
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
initialState = {};
|
initialState = {};
|
||||||
|
|||||||
@@ -1,28 +1,14 @@
|
|||||||
import { GeneralSettings } from "@/components/settings/general-settings";
|
import { GeneralSettings } from "@/components/settings/general-settings";
|
||||||
import { StateProvider } from "@/components/state-provider";
|
import { StateProvider } from "@/components/state-provider";
|
||||||
import { fetchUserSettings } from "@/lib/api";
|
import { fetchUserSettings } from "@/lib/api";
|
||||||
|
import { mapUserSettingsResponse } from "@/lib/ssr/user-settings";
|
||||||
|
|
||||||
export default async function GeneralSettingsPage() {
|
export default async function GeneralSettingsPage() {
|
||||||
let initialState = {};
|
let initialState = {};
|
||||||
try {
|
try {
|
||||||
const response = await fetchUserSettings({ cache: "no-store" });
|
const response = await fetchUserSettings({ cache: "no-store" });
|
||||||
const settings = response.settings;
|
const mapped = mapUserSettingsResponse(response);
|
||||||
initialState = {
|
initialState = { userSettings: mapped.loaded ? mapped : undefined };
|
||||||
userSettings: settings
|
|
||||||
? {
|
|
||||||
workspaceId: settings.workspace_id ?? null,
|
|
||||||
workflowId: settings.workflow_filter_id ?? null,
|
|
||||||
kanbanViewMode: settings.kanban_view_mode ?? null,
|
|
||||||
repositoryIds: settings.repository_ids ?? [],
|
|
||||||
preferredShell: settings.preferred_shell ?? null,
|
|
||||||
shellOptions: response.shell_options ?? [],
|
|
||||||
defaultEditorId: settings.default_editor_id ?? null,
|
|
||||||
enablePreviewOnClick: settings.enable_preview_on_click ?? false,
|
|
||||||
chatSubmitKey: settings.chat_submit_key ?? "cmd_enter",
|
|
||||||
loaded: true,
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
};
|
|
||||||
} catch {
|
} catch {
|
||||||
initialState = {};
|
initialState = {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Button } from "@kandev/ui/button";
|
import { Button } from "@kandev/ui/button";
|
||||||
|
import { TooltipProvider } from "@kandev/ui/tooltip";
|
||||||
import { IconMenu2 } from "@tabler/icons-react";
|
import { IconMenu2 } from "@tabler/icons-react";
|
||||||
|
import { ReleaseNotesButton } from "../release-notes/release-notes-button";
|
||||||
import { MobileMenuSheet } from "./mobile-menu-sheet";
|
import { MobileMenuSheet } from "./mobile-menu-sheet";
|
||||||
import { useAppStore } from "@/components/state-provider";
|
import { useAppStore } from "@/components/state-provider";
|
||||||
|
|
||||||
@@ -12,6 +14,8 @@ type KanbanHeaderMobileProps = {
|
|||||||
searchQuery?: string;
|
searchQuery?: string;
|
||||||
onSearchChange?: (query: string) => void;
|
onSearchChange?: (query: string) => void;
|
||||||
isSearchLoading?: boolean;
|
isSearchLoading?: boolean;
|
||||||
|
showReleaseNotesButton: boolean;
|
||||||
|
onOpenReleaseNotes: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function KanbanHeaderMobile({
|
export function KanbanHeaderMobile({
|
||||||
@@ -20,6 +24,8 @@ export function KanbanHeaderMobile({
|
|||||||
searchQuery = "",
|
searchQuery = "",
|
||||||
onSearchChange,
|
onSearchChange,
|
||||||
isSearchLoading = false,
|
isSearchLoading = false,
|
||||||
|
showReleaseNotesButton,
|
||||||
|
onOpenReleaseNotes,
|
||||||
}: KanbanHeaderMobileProps) {
|
}: KanbanHeaderMobileProps) {
|
||||||
const isMenuOpen = useAppStore((state) => state.mobileKanban.isMenuOpen);
|
const isMenuOpen = useAppStore((state) => state.mobileKanban.isMenuOpen);
|
||||||
const setMenuOpen = useAppStore((state) => state.setMobileKanbanMenuOpen);
|
const setMenuOpen = useAppStore((state) => state.setMobileKanbanMenuOpen);
|
||||||
@@ -30,15 +36,22 @@ export function KanbanHeaderMobile({
|
|||||||
<Link href="/" className="text-xl font-bold hover:opacity-80">
|
<Link href="/" className="text-xl font-bold hover:opacity-80">
|
||||||
KanDev
|
KanDev
|
||||||
</Link>
|
</Link>
|
||||||
<Button
|
<div className="flex items-center gap-2">
|
||||||
variant="outline"
|
{showReleaseNotesButton && (
|
||||||
size="icon"
|
<TooltipProvider>
|
||||||
onClick={() => setMenuOpen(true)}
|
<ReleaseNotesButton hasUnseen onClick={onOpenReleaseNotes} />
|
||||||
className="cursor-pointer h-10 w-10"
|
</TooltipProvider>
|
||||||
>
|
)}
|
||||||
<IconMenu2 className="h-5 w-5" />
|
<Button
|
||||||
<span className="sr-only">Open menu</span>
|
variant="outline"
|
||||||
</Button>
|
size="icon"
|
||||||
|
onClick={() => setMenuOpen(true)}
|
||||||
|
className="cursor-pointer h-10 w-10"
|
||||||
|
>
|
||||||
|
<IconMenu2 className="h-5 w-5" />
|
||||||
|
<span className="sr-only">Open menu</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<MobileMenuSheet
|
<MobileMenuSheet
|
||||||
open={isMenuOpen}
|
open={isMenuOpen}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import {
|
|||||||
} from "@tabler/icons-react";
|
} from "@tabler/icons-react";
|
||||||
import { KanbanDisplayDropdown } from "../kanban-display-dropdown";
|
import { KanbanDisplayDropdown } from "../kanban-display-dropdown";
|
||||||
import { RefreshReviewsButton } from "../github/refresh-reviews-button";
|
import { RefreshReviewsButton } from "../github/refresh-reviews-button";
|
||||||
|
import { ReleaseNotesButton } from "../release-notes/release-notes-button";
|
||||||
|
import { ReleaseNotesDialog } from "../release-notes/release-notes-dialog";
|
||||||
import { TaskSearchInput } from "./task-search-input";
|
import { TaskSearchInput } from "./task-search-input";
|
||||||
import { KanbanHeaderMobile } from "./kanban-header-mobile";
|
import { KanbanHeaderMobile } from "./kanban-header-mobile";
|
||||||
import { MobileMenuSheet } from "./mobile-menu-sheet";
|
import { MobileMenuSheet } from "./mobile-menu-sheet";
|
||||||
@@ -23,6 +25,7 @@ import { linkToTasks } from "@/lib/links";
|
|||||||
import { useResponsiveBreakpoint } from "@/hooks/use-responsive-breakpoint";
|
import { useResponsiveBreakpoint } from "@/hooks/use-responsive-breakpoint";
|
||||||
import { useAppStore } from "@/components/state-provider";
|
import { useAppStore } from "@/components/state-provider";
|
||||||
import { useKanbanDisplaySettings } from "@/hooks/use-kanban-display-settings";
|
import { useKanbanDisplaySettings } from "@/hooks/use-kanban-display-settings";
|
||||||
|
import { useReleaseNotes } from "@/hooks/use-release-notes";
|
||||||
|
|
||||||
type KanbanHeaderProps = {
|
type KanbanHeaderProps = {
|
||||||
onCreateTask: () => void;
|
onCreateTask: () => void;
|
||||||
@@ -98,6 +101,8 @@ function TabletHeader({
|
|||||||
toggleValue,
|
toggleValue,
|
||||||
handleViewChange,
|
handleViewChange,
|
||||||
setMenuOpen,
|
setMenuOpen,
|
||||||
|
showReleaseNotesButton,
|
||||||
|
onOpenReleaseNotes,
|
||||||
}: {
|
}: {
|
||||||
onCreateTask: () => void;
|
onCreateTask: () => void;
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
@@ -106,6 +111,8 @@ function TabletHeader({
|
|||||||
toggleValue: string;
|
toggleValue: string;
|
||||||
handleViewChange: (value: string) => void;
|
handleViewChange: (value: string) => void;
|
||||||
setMenuOpen: (open: boolean) => void;
|
setMenuOpen: (open: boolean) => void;
|
||||||
|
showReleaseNotesButton: boolean;
|
||||||
|
onOpenReleaseNotes: () => void;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<header className="flex items-center justify-between p-4 pb-3 gap-3">
|
<header className="flex items-center justify-between p-4 pb-3 gap-3">
|
||||||
@@ -133,6 +140,7 @@ function TabletHeader({
|
|||||||
className="h-8"
|
className="h-8"
|
||||||
itemClassName="h-8 w-8"
|
itemClassName="h-8 w-8"
|
||||||
/>
|
/>
|
||||||
|
{showReleaseNotesButton && <ReleaseNotesButton hasUnseen onClick={onOpenReleaseNotes} />}
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@@ -155,6 +163,8 @@ function DesktopHeader({
|
|||||||
isSearchLoading,
|
isSearchLoading,
|
||||||
toggleValue,
|
toggleValue,
|
||||||
handleViewChange,
|
handleViewChange,
|
||||||
|
showReleaseNotesButton,
|
||||||
|
onOpenReleaseNotes,
|
||||||
}: {
|
}: {
|
||||||
onCreateTask: () => void;
|
onCreateTask: () => void;
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
@@ -162,6 +172,8 @@ function DesktopHeader({
|
|||||||
isSearchLoading: boolean;
|
isSearchLoading: boolean;
|
||||||
toggleValue: string;
|
toggleValue: string;
|
||||||
handleViewChange: (value: string) => void;
|
handleViewChange: (value: string) => void;
|
||||||
|
showReleaseNotesButton: boolean;
|
||||||
|
onOpenReleaseNotes: () => void;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<header className="relative flex items-center justify-between p-4 pb-3">
|
<header className="relative flex items-center justify-between p-4 pb-3">
|
||||||
@@ -199,6 +211,7 @@ function DesktopHeader({
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
<RefreshReviewsButton />
|
<RefreshReviewsButton />
|
||||||
|
{showReleaseNotesButton && <ReleaseNotesButton hasUnseen onClick={onOpenReleaseNotes} />}
|
||||||
<KanbanDisplayDropdown />
|
<KanbanDisplayDropdown />
|
||||||
<Link href="/settings" className="cursor-pointer">
|
<Link href="/settings" className="cursor-pointer">
|
||||||
<Button variant="outline" className="cursor-pointer">
|
<Button variant="outline" className="cursor-pointer">
|
||||||
@@ -225,6 +238,7 @@ export function KanbanHeader({
|
|||||||
const setMenuOpen = useAppStore((state) => state.setMobileKanbanMenuOpen);
|
const setMenuOpen = useAppStore((state) => state.setMobileKanbanMenuOpen);
|
||||||
|
|
||||||
const { kanbanViewMode, onViewModeChange } = useKanbanDisplaySettings();
|
const { kanbanViewMode, onViewModeChange } = useKanbanDisplaySettings();
|
||||||
|
const releaseNotes = useReleaseNotes();
|
||||||
|
|
||||||
const toggleValue = getToggleValue(currentPage, kanbanViewMode);
|
const toggleValue = getToggleValue(currentPage, kanbanViewMode);
|
||||||
|
|
||||||
@@ -240,20 +254,25 @@ export function KanbanHeader({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const releaseNotesProps = {
|
||||||
|
showReleaseNotesButton: releaseNotes.showTopbarButton,
|
||||||
|
onOpenReleaseNotes: releaseNotes.openDialog,
|
||||||
|
};
|
||||||
|
|
||||||
|
let header: React.ReactNode;
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
return (
|
header = (
|
||||||
<KanbanHeaderMobile
|
<KanbanHeaderMobile
|
||||||
workspaceId={workspaceId}
|
workspaceId={workspaceId}
|
||||||
currentPage={currentPage}
|
currentPage={currentPage}
|
||||||
searchQuery={searchQuery}
|
searchQuery={searchQuery}
|
||||||
onSearchChange={onSearchChange}
|
onSearchChange={onSearchChange}
|
||||||
isSearchLoading={isSearchLoading}
|
isSearchLoading={isSearchLoading}
|
||||||
|
{...releaseNotesProps}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
} else if (isTablet) {
|
||||||
|
header = (
|
||||||
if (isTablet) {
|
|
||||||
return (
|
|
||||||
<>
|
<>
|
||||||
<TabletHeader
|
<TabletHeader
|
||||||
onCreateTask={onCreateTask}
|
onCreateTask={onCreateTask}
|
||||||
@@ -263,6 +282,7 @@ export function KanbanHeader({
|
|||||||
toggleValue={toggleValue}
|
toggleValue={toggleValue}
|
||||||
handleViewChange={handleViewChange}
|
handleViewChange={handleViewChange}
|
||||||
setMenuOpen={setMenuOpen}
|
setMenuOpen={setMenuOpen}
|
||||||
|
{...releaseNotesProps}
|
||||||
/>
|
/>
|
||||||
<MobileMenuSheet
|
<MobileMenuSheet
|
||||||
open={isMenuOpen}
|
open={isMenuOpen}
|
||||||
@@ -275,16 +295,31 @@ export function KanbanHeader({
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
header = (
|
||||||
|
<DesktopHeader
|
||||||
|
onCreateTask={onCreateTask}
|
||||||
|
searchQuery={searchQuery}
|
||||||
|
onSearchChange={onSearchChange}
|
||||||
|
isSearchLoading={isSearchLoading}
|
||||||
|
toggleValue={toggleValue}
|
||||||
|
handleViewChange={handleViewChange}
|
||||||
|
{...releaseNotesProps}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DesktopHeader
|
<>
|
||||||
onCreateTask={onCreateTask}
|
{header}
|
||||||
searchQuery={searchQuery}
|
{releaseNotes.hasNotes && (
|
||||||
onSearchChange={onSearchChange}
|
<ReleaseNotesDialog
|
||||||
isSearchLoading={isSearchLoading}
|
open={releaseNotes.dialogOpen}
|
||||||
toggleValue={toggleValue}
|
onOpenChange={releaseNotes.closeDialog}
|
||||||
handleViewChange={handleViewChange}
|
entries={releaseNotes.unseenEntries}
|
||||||
/>
|
latestVersion={releaseNotes.latestVersion}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Button } from "@kandev/ui/button";
|
||||||
|
import { Tooltip, TooltipContent, TooltipTrigger } from "@kandev/ui/tooltip";
|
||||||
|
import { IconSparkles } from "@tabler/icons-react";
|
||||||
|
|
||||||
|
type ReleaseNotesButtonProps = {
|
||||||
|
hasUnseen: boolean;
|
||||||
|
onClick: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ReleaseNotesButton({ hasUnseen, onClick }: ReleaseNotesButtonProps) {
|
||||||
|
return (
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<Button variant="outline" size="icon" onClick={onClick} className="cursor-pointer relative">
|
||||||
|
<IconSparkles className="h-4 w-4" />
|
||||||
|
{hasUnseen && (
|
||||||
|
<span className="absolute -top-1 -right-1 h-2.5 w-2.5 rounded-full bg-primary border-2 border-background" />
|
||||||
|
)}
|
||||||
|
<span className="sr-only">What's New</span>
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>What's New</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import ReactMarkdown from "react-markdown";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogDescription,
|
||||||
|
} from "@kandev/ui/dialog";
|
||||||
|
import { ScrollArea } from "@kandev/ui/scroll-area";
|
||||||
|
import { Badge } from "@kandev/ui/badge";
|
||||||
|
import { Separator } from "@kandev/ui/separator";
|
||||||
|
import { IconExternalLink } from "@tabler/icons-react";
|
||||||
|
import { remarkPlugins, markdownComponents } from "@/components/shared/markdown-components";
|
||||||
|
import { getReleaseUrl } from "@/lib/release-notes";
|
||||||
|
import type { ChangelogEntry } from "@/lib/changelog";
|
||||||
|
|
||||||
|
type ReleaseNotesDialogProps = {
|
||||||
|
open: boolean;
|
||||||
|
onOpenChange: (open: boolean) => void;
|
||||||
|
entries: ChangelogEntry[];
|
||||||
|
latestVersion: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function buildDescription(entries: ChangelogEntry[]): string {
|
||||||
|
if (entries.length === 1) {
|
||||||
|
return entries[0].date ? `Released on ${entries[0].date}` : "Latest release notes";
|
||||||
|
}
|
||||||
|
return `${entries.length} new releases`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ReleaseNotesDialog({
|
||||||
|
open,
|
||||||
|
onOpenChange,
|
||||||
|
entries,
|
||||||
|
latestVersion,
|
||||||
|
}: ReleaseNotesDialogProps) {
|
||||||
|
const releaseUrl = getReleaseUrl(latestVersion);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
|
<DialogContent className="sm:max-w-[520px]">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="flex items-center gap-2">
|
||||||
|
What's New
|
||||||
|
<Badge variant="secondary">v{latestVersion}</Badge>
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogDescription>{buildDescription(entries)}</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<ScrollArea className="max-h-[60vh] pr-4">
|
||||||
|
<div className="space-y-4">
|
||||||
|
{entries.map((entry, index) => (
|
||||||
|
<div key={entry.version}>
|
||||||
|
{entries.length > 1 && (
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<Badge variant="outline">v{entry.version}</Badge>
|
||||||
|
{entry.date && (
|
||||||
|
<span className="text-xs text-muted-foreground">{entry.date}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="text-sm">
|
||||||
|
<ReactMarkdown remarkPlugins={remarkPlugins} components={markdownComponents}>
|
||||||
|
{entry.notes}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
{index < entries.length - 1 && <Separator className="mt-4" />}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
|
<div className="pt-2 border-t border-border">
|
||||||
|
<a
|
||||||
|
href={releaseUrl}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="inline-flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer"
|
||||||
|
>
|
||||||
|
View full release on GitHub
|
||||||
|
<IconExternalLink className="h-3 w-3" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useMemo, useState } from "react";
|
||||||
|
import ReactMarkdown from "react-markdown";
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from "@kandev/ui/card";
|
||||||
|
import { Badge } from "@kandev/ui/badge";
|
||||||
|
import {
|
||||||
|
Pagination,
|
||||||
|
PaginationContent,
|
||||||
|
PaginationItem,
|
||||||
|
PaginationLink,
|
||||||
|
PaginationNext,
|
||||||
|
PaginationPrevious,
|
||||||
|
PaginationEllipsis,
|
||||||
|
} from "@kandev/ui/pagination";
|
||||||
|
import { IconExternalLink } from "@tabler/icons-react";
|
||||||
|
import { remarkPlugins, markdownComponents } from "@/components/shared/markdown-components";
|
||||||
|
import { getChangelog, type ChangelogEntry } from "@/lib/changelog";
|
||||||
|
import { getReleaseUrl } from "@/lib/release-notes";
|
||||||
|
|
||||||
|
const PAGE_SIZE = 10;
|
||||||
|
|
||||||
|
function ChangelogEntryCard({ entry }: { entry: ChangelogEntry }) {
|
||||||
|
const releaseUrl = getReleaseUrl(entry.version);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<CardTitle className="text-base flex items-center gap-2">
|
||||||
|
<Badge variant="secondary">v{entry.version}</Badge>
|
||||||
|
{entry.date && (
|
||||||
|
<span className="text-sm font-normal text-muted-foreground">{entry.date}</span>
|
||||||
|
)}
|
||||||
|
</CardTitle>
|
||||||
|
<a
|
||||||
|
href={releaseUrl}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer"
|
||||||
|
>
|
||||||
|
View on GitHub
|
||||||
|
<IconExternalLink className="h-3 w-3" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="text-sm">
|
||||||
|
<ReactMarkdown remarkPlugins={remarkPlugins} components={markdownComponents}>
|
||||||
|
{entry.notes}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPageNumbers(currentPage: number, totalPages: number): (number | "ellipsis")[] {
|
||||||
|
if (totalPages <= 5) return Array.from({ length: totalPages }, (_, i) => i + 1);
|
||||||
|
|
||||||
|
const pages: (number | "ellipsis")[] = [1];
|
||||||
|
if (currentPage > 3) pages.push("ellipsis");
|
||||||
|
|
||||||
|
const start = Math.max(2, currentPage - 1);
|
||||||
|
const end = Math.min(totalPages - 1, currentPage + 1);
|
||||||
|
for (let i = start; i <= end; i++) pages.push(i);
|
||||||
|
|
||||||
|
if (currentPage < totalPages - 2) pages.push("ellipsis");
|
||||||
|
pages.push(totalPages);
|
||||||
|
|
||||||
|
return pages;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ChangelogList() {
|
||||||
|
const changelog = getChangelog();
|
||||||
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
|
||||||
|
const totalPages = Math.ceil(changelog.length / PAGE_SIZE);
|
||||||
|
const pageEntries = useMemo(() => {
|
||||||
|
const start = (currentPage - 1) * PAGE_SIZE;
|
||||||
|
return changelog.slice(start, start + PAGE_SIZE);
|
||||||
|
}, [changelog, currentPage]);
|
||||||
|
|
||||||
|
if (changelog.length === 0) {
|
||||||
|
return <p className="text-sm text-muted-foreground">No changelog entries available.</p>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pageNumbers = buildPageNumbers(currentPage, totalPages);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-4">
|
||||||
|
{pageEntries.map((entry) => (
|
||||||
|
<ChangelogEntryCard key={entry.version} entry={entry} />
|
||||||
|
))}
|
||||||
|
{totalPages > 1 && (
|
||||||
|
<Pagination>
|
||||||
|
<PaginationContent>
|
||||||
|
<PaginationItem>
|
||||||
|
<PaginationPrevious
|
||||||
|
onClick={() => setCurrentPage((p) => Math.max(1, p - 1))}
|
||||||
|
className={currentPage === 1 ? "pointer-events-none opacity-50" : "cursor-pointer"}
|
||||||
|
/>
|
||||||
|
</PaginationItem>
|
||||||
|
{pageNumbers.map((page, i) =>
|
||||||
|
page === "ellipsis" ? (
|
||||||
|
<PaginationItem key={`ellipsis-${i}`}>
|
||||||
|
<PaginationEllipsis />
|
||||||
|
</PaginationItem>
|
||||||
|
) : (
|
||||||
|
<PaginationItem key={page}>
|
||||||
|
<PaginationLink
|
||||||
|
isActive={currentPage === page}
|
||||||
|
onClick={() => setCurrentPage(page)}
|
||||||
|
className="cursor-pointer"
|
||||||
|
>
|
||||||
|
{page}
|
||||||
|
</PaginationLink>
|
||||||
|
</PaginationItem>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
<PaginationItem>
|
||||||
|
<PaginationNext
|
||||||
|
onClick={() => setCurrentPage((p) => Math.min(totalPages, p + 1))}
|
||||||
|
className={
|
||||||
|
currentPage === totalPages ? "pointer-events-none opacity-50" : "cursor-pointer"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</PaginationItem>
|
||||||
|
</PaginationContent>
|
||||||
|
</Pagination>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { Button } from "@kandev/ui/button";
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from "@kandev/ui/card";
|
||||||
|
import { Label } from "@kandev/ui/label";
|
||||||
|
import { Separator } from "@kandev/ui/separator";
|
||||||
|
import { Switch } from "@kandev/ui/switch";
|
||||||
|
import { useAppStore } from "@/components/state-provider";
|
||||||
|
import { updateUserSettings } from "@/lib/api";
|
||||||
|
|
||||||
|
export function ChangelogNotificationCard() {
|
||||||
|
const userSettings = useAppStore((state) => state.userSettings);
|
||||||
|
const setUserSettings = useAppStore((state) => state.setUserSettings);
|
||||||
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
|
|
||||||
|
const handleToggle = async (checked: boolean) => {
|
||||||
|
if (isSaving) return;
|
||||||
|
setIsSaving(true);
|
||||||
|
const previousValue = userSettings.showReleaseNotification;
|
||||||
|
try {
|
||||||
|
setUserSettings({ ...userSettings, showReleaseNotification: checked });
|
||||||
|
await updateUserSettings({ show_release_notification: checked });
|
||||||
|
} catch {
|
||||||
|
setUserSettings({ ...userSettings, showReleaseNotification: previousValue });
|
||||||
|
} finally {
|
||||||
|
setIsSaving(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleResetLastSeen = async () => {
|
||||||
|
if (isSaving) return;
|
||||||
|
setIsSaving(true);
|
||||||
|
try {
|
||||||
|
setUserSettings({ ...userSettings, releaseNotesLastSeenVersion: null });
|
||||||
|
await updateUserSettings({ release_notes_last_seen_version: "" });
|
||||||
|
} catch {
|
||||||
|
// Ignore — worst case the button stays hidden until next release
|
||||||
|
} finally {
|
||||||
|
setIsSaving(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-base">Topbar Release Notification</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<Label htmlFor="release-notification-toggle">Show notification for new releases</Label>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
When enabled, a sparkle icon appears in the topbar when a new version is released
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Switch
|
||||||
|
id="release-notification-toggle"
|
||||||
|
checked={userSettings.showReleaseNotification}
|
||||||
|
onCheckedChange={handleToggle}
|
||||||
|
disabled={isSaving}
|
||||||
|
className="cursor-pointer"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Separator className="my-4" />
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<Label>Reset seen releases</Label>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
Clear your last seen version so the topbar notification appears again
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={handleResetLastSeen}
|
||||||
|
disabled={isSaving || !userSettings.releaseNotesLastSeenVersion}
|
||||||
|
className="cursor-pointer"
|
||||||
|
>
|
||||||
|
Reset
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { IconBell } from "@tabler/icons-react";
|
||||||
|
import { Separator } from "@kandev/ui/separator";
|
||||||
|
import { SettingsSection } from "@/components/settings/settings-section";
|
||||||
|
import { ChangelogNotificationCard } from "@/components/settings/changelog-notification-card";
|
||||||
|
import { ChangelogList } from "@/components/settings/changelog-list";
|
||||||
|
|
||||||
|
export function ChangelogSettings() {
|
||||||
|
return (
|
||||||
|
<div className="space-y-8">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-2xl font-bold">Changelog</h2>
|
||||||
|
<p className="text-sm text-muted-foreground mt-1">
|
||||||
|
View all releases and manage notification preferences
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Separator />
|
||||||
|
|
||||||
|
<SettingsSection
|
||||||
|
icon={<IconBell className="h-5 w-5" />}
|
||||||
|
title="Notifications"
|
||||||
|
description="Control the release notes notification in the topbar"
|
||||||
|
>
|
||||||
|
<ChangelogNotificationCard />
|
||||||
|
</SettingsSection>
|
||||||
|
|
||||||
|
<Separator />
|
||||||
|
|
||||||
|
<SettingsSection title="Release History" description="All versions and their release notes">
|
||||||
|
<ChangelogList />
|
||||||
|
</SettingsSection>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -212,6 +212,8 @@ function mapEditorSettingsFields(
|
|||||||
return {
|
return {
|
||||||
chatSubmitKey: s.chat_submit_key ?? "cmd_enter",
|
chatSubmitKey: s.chat_submit_key ?? "cmd_enter",
|
||||||
reviewAutoMarkOnScroll: s.review_auto_mark_on_scroll ?? true,
|
reviewAutoMarkOnScroll: s.review_auto_mark_on_scroll ?? true,
|
||||||
|
showReleaseNotification: s.show_release_notification ?? true,
|
||||||
|
releaseNotesLastSeenVersion: (s.release_notes_last_seen_version as string) || null,
|
||||||
lspAutoStartLanguages: s.lsp_auto_start_languages ?? [],
|
lspAutoStartLanguages: s.lsp_auto_start_languages ?? [],
|
||||||
lspAutoInstallLanguages: s.lsp_auto_install_languages ?? [],
|
lspAutoInstallLanguages: s.lsp_auto_install_languages ?? [],
|
||||||
lspServerConfigs: s.lsp_server_configs ?? {},
|
lspServerConfigs: s.lsp_server_configs ?? {},
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
IconKey,
|
IconKey,
|
||||||
IconMessageCircle,
|
IconMessageCircle,
|
||||||
IconBrandGithub,
|
IconBrandGithub,
|
||||||
|
IconSparkles,
|
||||||
} from "@tabler/icons-react";
|
} from "@tabler/icons-react";
|
||||||
import {
|
import {
|
||||||
Sidebar,
|
Sidebar,
|
||||||
@@ -299,6 +300,16 @@ export function SettingsAppSidebar() {
|
|||||||
|
|
||||||
<ExecutorsSidebarSection pathname={pathname} executors={executors} />
|
<ExecutorsSidebarSection pathname={pathname} executors={executors} />
|
||||||
<SecretsSidebarSection pathname={pathname} />
|
<SecretsSidebarSection pathname={pathname} />
|
||||||
|
|
||||||
|
{/* Changelog */}
|
||||||
|
<SidebarMenuItem>
|
||||||
|
<SidebarMenuButton asChild isActive={pathname === "/settings/changelog"}>
|
||||||
|
<Link href="/settings/changelog">
|
||||||
|
<IconSparkles className="h-4 w-4" />
|
||||||
|
<span>Changelog</span>
|
||||||
|
</Link>
|
||||||
|
</SidebarMenuButton>
|
||||||
|
</SidebarMenuItem>
|
||||||
</SidebarMenu>
|
</SidebarMenu>
|
||||||
</SidebarGroupContent>
|
</SidebarGroupContent>
|
||||||
</SidebarGroup>
|
</SidebarGroup>
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
@@ -4,29 +4,7 @@ import { useEffect } from "react";
|
|||||||
import { fetchUserSettings, listEditors } from "@/lib/api";
|
import { fetchUserSettings, listEditors } from "@/lib/api";
|
||||||
import { getWebSocketClient } from "@/lib/ws/connection";
|
import { getWebSocketClient } from "@/lib/ws/connection";
|
||||||
import { useAppStore } from "@/components/state-provider";
|
import { useAppStore } from "@/components/state-provider";
|
||||||
import type { UserSettingsState } from "@/lib/state/slices/settings/types";
|
import { mapUserSettingsResponse } from "@/lib/ssr/user-settings";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
function mapUserSettingsResponse(data: any): UserSettingsState {
|
|
||||||
const s = data.settings;
|
|
||||||
return {
|
|
||||||
workspaceId: s.workspace_id || null,
|
|
||||||
workflowId: s.workflow_filter_id || null,
|
|
||||||
kanbanViewMode: s.kanban_view_mode || null,
|
|
||||||
repositoryIds: s.repository_ids ?? [],
|
|
||||||
preferredShell: s.preferred_shell || null,
|
|
||||||
shellOptions: data.shell_options ?? [],
|
|
||||||
defaultEditorId: s.default_editor_id || null,
|
|
||||||
enablePreviewOnClick: s.enable_preview_on_click ?? false,
|
|
||||||
chatSubmitKey: s.chat_submit_key ?? "cmd_enter",
|
|
||||||
reviewAutoMarkOnScroll: s.review_auto_mark_on_scroll ?? true,
|
|
||||||
lspAutoStartLanguages: s.lsp_auto_start_languages ?? [],
|
|
||||||
lspAutoInstallLanguages: s.lsp_auto_install_languages ?? [],
|
|
||||||
lspServerConfigs: s.lsp_server_configs ?? {},
|
|
||||||
savedLayouts: s.saved_layouts ?? [],
|
|
||||||
loaded: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useEditors() {
|
export function useEditors() {
|
||||||
const editors = useAppStore((state) => state.editors.items);
|
const editors = useAppStore((state) => state.editors.items);
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState, useCallback, useMemo, useEffect } from "react";
|
||||||
|
import { getReleaseNotes, hasReleaseNotes } from "@/lib/release-notes";
|
||||||
|
import { getChangelog, type ChangelogEntry } from "@/lib/changelog";
|
||||||
|
import { updateUserSettings } from "@/lib/api";
|
||||||
|
import { getWebSocketClient } from "@/lib/ws/connection";
|
||||||
|
import { useAppStore, useAppStoreApi } from "@/components/state-provider";
|
||||||
|
|
||||||
|
const LEGACY_STORAGE_KEY = "kandev.releaseNotes.lastSeenVersion";
|
||||||
|
|
||||||
|
function isVersionNewer(version: string, baseline: string): boolean {
|
||||||
|
const a = version.split(".").map(Number);
|
||||||
|
const b = baseline.split(".").map(Number);
|
||||||
|
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
||||||
|
if ((a[i] ?? 0) > (b[i] ?? 0)) return true;
|
||||||
|
if ((a[i] ?? 0) < (b[i] ?? 0)) return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUnseenEntries(changelog: ChangelogEntry[], lastSeen: string | null): ChangelogEntry[] {
|
||||||
|
if (!hasReleaseNotes()) return [];
|
||||||
|
if (!lastSeen) return changelog.slice(0, 1);
|
||||||
|
return changelog.filter((entry) => isVersionNewer(entry.version, lastSeen));
|
||||||
|
}
|
||||||
|
|
||||||
|
function persistLastSeenVersion(version: string) {
|
||||||
|
const payload = { release_notes_last_seen_version: version };
|
||||||
|
const client = getWebSocketClient();
|
||||||
|
if (client) {
|
||||||
|
client.request("user.settings.update", payload).catch(() => {
|
||||||
|
updateUserSettings(payload, { cache: "no-store" }).catch(() => {});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
updateUserSettings(payload, { cache: "no-store" }).catch(() => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useReleaseNotes() {
|
||||||
|
const latestRelease = getReleaseNotes();
|
||||||
|
const showReleaseNotification = useAppStore((s) => s.userSettings.showReleaseNotification);
|
||||||
|
const lastSeenVersion = useAppStore((s) => s.userSettings.releaseNotesLastSeenVersion);
|
||||||
|
const settingsLoaded = useAppStore((s) => s.userSettings.loaded);
|
||||||
|
const storeApi = useAppStoreApi();
|
||||||
|
|
||||||
|
const changelog = useMemo(() => getChangelog(), []);
|
||||||
|
const unseenEntries = useMemo(
|
||||||
|
() => getUnseenEntries(changelog, lastSeenVersion),
|
||||||
|
[changelog, lastSeenVersion],
|
||||||
|
);
|
||||||
|
|
||||||
|
const hasUnseen = unseenEntries.length > 0;
|
||||||
|
const [dialogOpen, setDialogOpen] = useState(false);
|
||||||
|
// Snapshot entries when dialog opens so markAsSeen doesn't clear them mid-view
|
||||||
|
const [dialogEntries, setDialogEntries] = useState<ChangelogEntry[]>([]);
|
||||||
|
|
||||||
|
// One-time migration from localStorage to backend
|
||||||
|
useEffect(() => {
|
||||||
|
if (!settingsLoaded || lastSeenVersion) return;
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(LEGACY_STORAGE_KEY);
|
||||||
|
if (!raw) return;
|
||||||
|
const parsed = JSON.parse(raw) as string;
|
||||||
|
if (!parsed) return;
|
||||||
|
const { userSettings, setUserSettings } = storeApi.getState();
|
||||||
|
setUserSettings({ ...userSettings, releaseNotesLastSeenVersion: parsed });
|
||||||
|
persistLastSeenVersion(parsed);
|
||||||
|
localStorage.removeItem(LEGACY_STORAGE_KEY);
|
||||||
|
} catch {
|
||||||
|
// Ignore migration errors
|
||||||
|
}
|
||||||
|
}, [settingsLoaded, lastSeenVersion, storeApi]);
|
||||||
|
|
||||||
|
const markAsSeen = useCallback(() => {
|
||||||
|
const version = latestRelease.version;
|
||||||
|
const { userSettings, setUserSettings } = storeApi.getState();
|
||||||
|
setUserSettings({ ...userSettings, releaseNotesLastSeenVersion: version });
|
||||||
|
persistLastSeenVersion(version);
|
||||||
|
}, [latestRelease.version, storeApi]);
|
||||||
|
|
||||||
|
const openDialog = useCallback(() => {
|
||||||
|
setDialogEntries(unseenEntries);
|
||||||
|
setDialogOpen(true);
|
||||||
|
markAsSeen();
|
||||||
|
}, [markAsSeen, unseenEntries]);
|
||||||
|
|
||||||
|
const closeDialog = useCallback(() => {
|
||||||
|
setDialogOpen(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
unseenEntries: dialogOpen ? dialogEntries : unseenEntries,
|
||||||
|
latestVersion: latestRelease.version,
|
||||||
|
hasUnseen,
|
||||||
|
dialogOpen,
|
||||||
|
openDialog,
|
||||||
|
closeDialog,
|
||||||
|
hasNotes: hasReleaseNotes(),
|
||||||
|
showTopbarButton: hasReleaseNotes() && hasUnseen && showReleaseNotification,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import { fetchUserSettings, updateUserSettings } from "@/lib/api";
|
|||||||
import { mapSelectedRepositoryIds } from "@/lib/kanban/filters";
|
import { mapSelectedRepositoryIds } from "@/lib/kanban/filters";
|
||||||
import { useAppStore } from "@/components/state-provider";
|
import { useAppStore } from "@/components/state-provider";
|
||||||
import { useRepositories } from "@/hooks/domains/workspace/use-repositories";
|
import { useRepositories } from "@/hooks/domains/workspace/use-repositories";
|
||||||
|
import { mapUserSettingsResponse } from "@/lib/ssr/user-settings";
|
||||||
import type { Repository } from "@/lib/types/http";
|
import type { Repository } from "@/lib/types/http";
|
||||||
import type { UserSettingsState } from "@/lib/state/slices/settings/types";
|
import type { UserSettingsState } from "@/lib/state/slices/settings/types";
|
||||||
|
|
||||||
@@ -25,24 +26,31 @@ type CommitPayload = {
|
|||||||
kanbanViewMode?: string | null;
|
kanbanViewMode?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function carryForwardSettings(current: DisplaySettings) {
|
||||||
|
return {
|
||||||
|
shellOptions: current.shellOptions ?? [],
|
||||||
|
defaultEditorId: current.defaultEditorId ?? null,
|
||||||
|
chatSubmitKey: current.chatSubmitKey ?? "cmd_enter",
|
||||||
|
reviewAutoMarkOnScroll: current.reviewAutoMarkOnScroll ?? true,
|
||||||
|
showReleaseNotification: current.showReleaseNotification ?? true,
|
||||||
|
releaseNotesLastSeenVersion: current.releaseNotesLastSeenVersion ?? null,
|
||||||
|
lspAutoStartLanguages: current.lspAutoStartLanguages ?? [],
|
||||||
|
lspAutoInstallLanguages: current.lspAutoInstallLanguages ?? [],
|
||||||
|
lspServerConfigs: current.lspServerConfigs ?? {},
|
||||||
|
savedLayouts: current.savedLayouts ?? [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function buildNormalizedSettings(next: CommitPayload, current: DisplaySettings): DisplaySettings {
|
function buildNormalizedSettings(next: CommitPayload, current: DisplaySettings): DisplaySettings {
|
||||||
const repositoryIds = Array.from(new Set(next.repositoryIds)).sort();
|
|
||||||
return {
|
return {
|
||||||
workspaceId: next.workspaceId,
|
workspaceId: next.workspaceId,
|
||||||
workflowId: next.workflowId,
|
workflowId: next.workflowId,
|
||||||
kanbanViewMode:
|
kanbanViewMode:
|
||||||
next.kanbanViewMode !== undefined ? next.kanbanViewMode : (current.kanbanViewMode ?? null),
|
next.kanbanViewMode !== undefined ? next.kanbanViewMode : (current.kanbanViewMode ?? null),
|
||||||
repositoryIds,
|
repositoryIds: Array.from(new Set(next.repositoryIds)).sort(),
|
||||||
preferredShell: next.preferredShell ?? current.preferredShell ?? null,
|
preferredShell: next.preferredShell ?? current.preferredShell ?? null,
|
||||||
shellOptions: current.shellOptions ?? [],
|
|
||||||
defaultEditorId: current.defaultEditorId ?? null,
|
|
||||||
enablePreviewOnClick: next.enablePreviewOnClick ?? current.enablePreviewOnClick,
|
enablePreviewOnClick: next.enablePreviewOnClick ?? current.enablePreviewOnClick,
|
||||||
chatSubmitKey: current.chatSubmitKey ?? "cmd_enter",
|
...carryForwardSettings(current),
|
||||||
reviewAutoMarkOnScroll: current.reviewAutoMarkOnScroll ?? true,
|
|
||||||
lspAutoStartLanguages: current.lspAutoStartLanguages ?? [],
|
|
||||||
lspAutoInstallLanguages: current.lspAutoInstallLanguages ?? [],
|
|
||||||
lspServerConfigs: current.lspServerConfigs ?? {},
|
|
||||||
savedLayouts: current.savedLayouts ?? [],
|
|
||||||
loaded: true,
|
loaded: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -82,29 +90,6 @@ function useUserSettingsRef(userSettings: DisplaySettings) {
|
|||||||
return userSettingsRef;
|
return userSettingsRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
function mapFetchedUserSettings(data: any): DisplaySettings {
|
|
||||||
const s = data.settings;
|
|
||||||
const repositoryIds = Array.from(new Set<string>(s.repository_ids ?? [])).sort();
|
|
||||||
return {
|
|
||||||
workspaceId: s.workspace_id || null,
|
|
||||||
workflowId: s.workflow_filter_id || null,
|
|
||||||
kanbanViewMode: s.kanban_view_mode || null,
|
|
||||||
repositoryIds,
|
|
||||||
preferredShell: s.preferred_shell || null,
|
|
||||||
shellOptions: data.shell_options ?? [],
|
|
||||||
defaultEditorId: s.default_editor_id || null,
|
|
||||||
enablePreviewOnClick: s.enable_preview_on_click ?? false,
|
|
||||||
chatSubmitKey: s.chat_submit_key ?? "cmd_enter",
|
|
||||||
reviewAutoMarkOnScroll: s.review_auto_mark_on_scroll ?? true,
|
|
||||||
lspAutoStartLanguages: s.lsp_auto_start_languages ?? [],
|
|
||||||
lspAutoInstallLanguages: s.lsp_auto_install_languages ?? [],
|
|
||||||
lspServerConfigs: s.lsp_server_configs ?? {},
|
|
||||||
savedLayouts: s.saved_layouts ?? [],
|
|
||||||
loaded: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function useLoadUserSettings(
|
function useLoadUserSettings(
|
||||||
loaded: boolean,
|
loaded: boolean,
|
||||||
setUserSettings: (settings: DisplaySettings) => void,
|
setUserSettings: (settings: DisplaySettings) => void,
|
||||||
@@ -114,7 +99,9 @@ function useLoadUserSettings(
|
|||||||
fetchUserSettings({ cache: "no-store" })
|
fetchUserSettings({ cache: "no-store" })
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (!data?.settings) return;
|
if (!data?.settings) return;
|
||||||
setUserSettings(mapFetchedUserSettings(data));
|
const mapped = mapUserSettingsResponse(data);
|
||||||
|
if (!mapped.loaded) return;
|
||||||
|
setUserSettings(mapped);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
/* Ignore settings fetch errors for now. */
|
/* Ignore settings fetch errors for now. */
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ export async function updateUserSettings(
|
|||||||
enable_preview_on_click?: boolean;
|
enable_preview_on_click?: boolean;
|
||||||
chat_submit_key?: "enter" | "cmd_enter";
|
chat_submit_key?: "enter" | "cmd_enter";
|
||||||
review_auto_mark_on_scroll?: boolean;
|
review_auto_mark_on_scroll?: boolean;
|
||||||
|
show_release_notification?: boolean;
|
||||||
|
release_notes_last_seen_version?: string;
|
||||||
lsp_auto_start_languages?: string[];
|
lsp_auto_start_languages?: string[];
|
||||||
lsp_auto_install_languages?: string[];
|
lsp_auto_install_languages?: string[];
|
||||||
lsp_server_configs?: Record<string, Record<string, unknown>>;
|
lsp_server_configs?: Record<string, Record<string, unknown>>;
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import changelogData from "@/generated/changelog.json";
|
||||||
|
|
||||||
|
export type ChangelogEntry = {
|
||||||
|
version: string;
|
||||||
|
date: string;
|
||||||
|
notes: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getChangelog(): ChangelogEntry[] {
|
||||||
|
return changelogData;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import releaseNotesData from "@/generated/release-notes.json";
|
||||||
|
|
||||||
|
export type ReleaseNotes = {
|
||||||
|
version: string;
|
||||||
|
date: string;
|
||||||
|
notes: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getReleaseNotes(): ReleaseNotes {
|
||||||
|
return releaseNotesData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasReleaseNotes(): boolean {
|
||||||
|
return !!releaseNotesData.notes && releaseNotesData.version !== "dev";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getReleaseUrl(version: string): string {
|
||||||
|
return `https://github.com/kdlbs/kandev/releases/tag/v${version}`;
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import type { SavedLayout, UserSettingsResponse } from "@/lib/types/http";
|
||||||
|
|
||||||
|
export type UserSettingsData = NonNullable<UserSettingsResponse["settings"]>;
|
||||||
|
|
||||||
|
export function buildCoreFields(s: UserSettingsData) {
|
||||||
|
return {
|
||||||
|
workspaceId: s.workspace_id || null,
|
||||||
|
workflowId: s.workflow_filter_id || null,
|
||||||
|
kanbanViewMode: s.kanban_view_mode || null,
|
||||||
|
repositoryIds: s.repository_ids ?? [],
|
||||||
|
preferredShell: s.preferred_shell || null,
|
||||||
|
defaultEditorId: s.default_editor_id || null,
|
||||||
|
enablePreviewOnClick: s.enable_preview_on_click ?? false,
|
||||||
|
chatSubmitKey: s.chat_submit_key ?? "cmd_enter",
|
||||||
|
reviewAutoMarkOnScroll: s.review_auto_mark_on_scroll ?? true,
|
||||||
|
showReleaseNotification: s.show_release_notification ?? true,
|
||||||
|
releaseNotesLastSeenVersion: s.release_notes_last_seen_version || null,
|
||||||
|
savedLayouts: s.saved_layouts ?? [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildLspFields(s: UserSettingsData | undefined) {
|
||||||
|
return {
|
||||||
|
lspAutoStartLanguages: s?.lsp_auto_start_languages ?? [],
|
||||||
|
lspAutoInstallLanguages: s?.lsp_auto_install_languages ?? [],
|
||||||
|
lspServerConfigs: s?.lsp_server_configs ?? {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps a `fetchUserSettings()` API response into the shape expected by `AppState["userSettings"]`.
|
||||||
|
* Use in SSR pages to build `initialState.userSettings`.
|
||||||
|
*/
|
||||||
|
export function mapUserSettingsResponse(response: UserSettingsResponse | null) {
|
||||||
|
const s = response?.settings;
|
||||||
|
const shellOptions = response?.shell_options ?? [];
|
||||||
|
if (!s) {
|
||||||
|
return {
|
||||||
|
workspaceId: null,
|
||||||
|
workflowId: null,
|
||||||
|
kanbanViewMode: null,
|
||||||
|
repositoryIds: [] as string[],
|
||||||
|
preferredShell: null,
|
||||||
|
shellOptions,
|
||||||
|
defaultEditorId: null,
|
||||||
|
enablePreviewOnClick: false,
|
||||||
|
chatSubmitKey: "cmd_enter" as const,
|
||||||
|
reviewAutoMarkOnScroll: true,
|
||||||
|
showReleaseNotification: true,
|
||||||
|
releaseNotesLastSeenVersion: null,
|
||||||
|
savedLayouts: [] as SavedLayout[],
|
||||||
|
...buildLspFields(undefined),
|
||||||
|
loaded: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...buildCoreFields(s),
|
||||||
|
shellOptions,
|
||||||
|
...buildLspFields(s),
|
||||||
|
loaded: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -30,6 +30,8 @@ export const defaultSettingsState: SettingsSliceState = {
|
|||||||
enablePreviewOnClick: false,
|
enablePreviewOnClick: false,
|
||||||
chatSubmitKey: "cmd_enter",
|
chatSubmitKey: "cmd_enter",
|
||||||
reviewAutoMarkOnScroll: true,
|
reviewAutoMarkOnScroll: true,
|
||||||
|
showReleaseNotification: true,
|
||||||
|
releaseNotesLastSeenVersion: null,
|
||||||
lspAutoStartLanguages: [],
|
lspAutoStartLanguages: [],
|
||||||
lspAutoInstallLanguages: [],
|
lspAutoInstallLanguages: [],
|
||||||
lspServerConfigs: {},
|
lspServerConfigs: {},
|
||||||
|
|||||||
@@ -108,6 +108,8 @@ export type UserSettingsState = {
|
|||||||
enablePreviewOnClick: boolean;
|
enablePreviewOnClick: boolean;
|
||||||
chatSubmitKey: "enter" | "cmd_enter";
|
chatSubmitKey: "enter" | "cmd_enter";
|
||||||
reviewAutoMarkOnScroll: boolean;
|
reviewAutoMarkOnScroll: boolean;
|
||||||
|
showReleaseNotification: boolean;
|
||||||
|
releaseNotesLastSeenVersion: string | null;
|
||||||
lspAutoStartLanguages: string[];
|
lspAutoStartLanguages: string[];
|
||||||
lspAutoInstallLanguages: string[];
|
lspAutoInstallLanguages: string[];
|
||||||
lspServerConfigs: Record<string, Record<string, unknown>>;
|
lspServerConfigs: Record<string, Record<string, unknown>>;
|
||||||
|
|||||||
@@ -347,6 +347,8 @@ export type UserSettingsUpdatedPayload = {
|
|||||||
enable_preview_on_click?: boolean;
|
enable_preview_on_click?: boolean;
|
||||||
chat_submit_key?: string;
|
chat_submit_key?: string;
|
||||||
review_auto_mark_on_scroll?: boolean;
|
review_auto_mark_on_scroll?: boolean;
|
||||||
|
show_release_notification?: boolean;
|
||||||
|
release_notes_last_seen_version?: string;
|
||||||
lsp_auto_start_languages?: string[];
|
lsp_auto_start_languages?: string[];
|
||||||
lsp_auto_install_languages?: string[];
|
lsp_auto_install_languages?: string[];
|
||||||
saved_layouts?: SavedLayout[];
|
saved_layouts?: SavedLayout[];
|
||||||
|
|||||||
@@ -358,6 +358,8 @@ export type UserSettings = {
|
|||||||
enable_preview_on_click?: boolean;
|
enable_preview_on_click?: boolean;
|
||||||
chat_submit_key?: "enter" | "cmd_enter";
|
chat_submit_key?: "enter" | "cmd_enter";
|
||||||
review_auto_mark_on_scroll?: boolean;
|
review_auto_mark_on_scroll?: boolean;
|
||||||
|
show_release_notification?: boolean;
|
||||||
|
release_notes_last_seen_version?: string;
|
||||||
lsp_auto_start_languages?: string[];
|
lsp_auto_start_languages?: string[];
|
||||||
lsp_auto_install_languages?: string[];
|
lsp_auto_install_languages?: string[];
|
||||||
lsp_server_configs?: Record<string, Record<string, unknown>>;
|
lsp_server_configs?: Record<string, Record<string, unknown>>;
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ export function registerUsersHandlers(store: StoreApi<AppState>): WsHandlers {
|
|||||||
enablePreviewOnClick: message.payload.enable_preview_on_click ?? false,
|
enablePreviewOnClick: message.payload.enable_preview_on_click ?? false,
|
||||||
chatSubmitKey: (message.payload.chat_submit_key as "enter" | "cmd_enter") ?? "cmd_enter",
|
chatSubmitKey: (message.payload.chat_submit_key as "enter" | "cmd_enter") ?? "cmd_enter",
|
||||||
reviewAutoMarkOnScroll: message.payload.review_auto_mark_on_scroll ?? true,
|
reviewAutoMarkOnScroll: message.payload.review_auto_mark_on_scroll ?? true,
|
||||||
|
showReleaseNotification: message.payload.show_release_notification ?? true,
|
||||||
|
releaseNotesLastSeenVersion:
|
||||||
|
(message.payload.release_notes_last_seen_version as string) || null,
|
||||||
lspAutoStartLanguages: message.payload.lsp_auto_start_languages ?? [],
|
lspAutoStartLanguages: message.payload.lsp_auto_start_languages ?? [],
|
||||||
lspAutoInstallLanguages: message.payload.lsp_auto_install_languages ?? [],
|
lspAutoInstallLanguages: message.payload.lsp_auto_install_languages ?? [],
|
||||||
savedLayouts: message.payload.saved_layouts ?? [],
|
savedLayouts: message.payload.saved_layouts ?? [],
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"predev": "node scripts/generate-release-notes.mjs && node scripts/generate-changelog.mjs",
|
||||||
"dev": "next dev --hostname 0.0.0.0",
|
"dev": "next dev --hostname 0.0.0.0",
|
||||||
|
"prebuild": "node scripts/generate-release-notes.mjs && node scripts/generate-changelog.mjs",
|
||||||
"build": "cross-env NODE_ENV=production next build",
|
"build": "cross-env NODE_ENV=production next build",
|
||||||
"start": "cross-env NODE_ENV=production next start",
|
"start": "cross-env NODE_ENV=production next start",
|
||||||
"lint": "eslint --max-warnings 0",
|
"lint": "eslint --max-warnings 0",
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* Pre-build script that parses CHANGELOG.md into a JSON array of version entries
|
||||||
|
* for the changelog settings page to import at build time.
|
||||||
|
*
|
||||||
|
* Output: apps/web/generated/changelog.json
|
||||||
|
* Format: [{ version, date, notes }, ...]
|
||||||
|
*/
|
||||||
|
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
||||||
|
import { join, dirname } from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
const WEB_ROOT = join(__dirname, "..");
|
||||||
|
const REPO_ROOT = join(WEB_ROOT, "..", "..");
|
||||||
|
const OUTPUT_DIR = join(WEB_ROOT, "generated");
|
||||||
|
const OUTPUT_FILE = join(OUTPUT_DIR, "changelog.json");
|
||||||
|
const CHANGELOG_PATH = join(REPO_ROOT, "CHANGELOG.md");
|
||||||
|
|
||||||
|
function parseChangelog(content) {
|
||||||
|
const entries = [];
|
||||||
|
// Match version headings: ## VERSION - DATE or ## VERSION
|
||||||
|
const headerPattern = /^## (\S+?)(?:\s*-\s*(\S+))?\s*$/gm;
|
||||||
|
|
||||||
|
let match;
|
||||||
|
const headers = [];
|
||||||
|
while ((match = headerPattern.exec(content)) !== null) {
|
||||||
|
headers.push({
|
||||||
|
version: match[1],
|
||||||
|
date: match[2] || "",
|
||||||
|
index: match.index,
|
||||||
|
end: match.index + match[0].length,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < headers.length; i++) {
|
||||||
|
const header = headers[i];
|
||||||
|
const bodyStart = header.end;
|
||||||
|
const bodyEnd = i + 1 < headers.length ? headers[i + 1].index : content.length;
|
||||||
|
const notes = content.slice(bodyStart, bodyEnd).trim();
|
||||||
|
|
||||||
|
if (header.version === "Unreleased") continue;
|
||||||
|
|
||||||
|
entries.push({
|
||||||
|
version: header.version,
|
||||||
|
date: header.date,
|
||||||
|
notes,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
mkdirSync(OUTPUT_DIR, { recursive: true });
|
||||||
|
|
||||||
|
let content;
|
||||||
|
try {
|
||||||
|
content = readFileSync(CHANGELOG_PATH, "utf-8");
|
||||||
|
} catch {
|
||||||
|
writeFileSync(OUTPUT_FILE, "[]\n");
|
||||||
|
console.log("[changelog] CHANGELOG.md not found — wrote empty array");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const entries = parseChangelog(content);
|
||||||
|
writeFileSync(OUTPUT_FILE, JSON.stringify(entries, null, 2) + "\n");
|
||||||
|
console.log(`[changelog] parsed ${entries.length} version entries`);
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/**
|
||||||
|
* Pre-build script that extracts the current version's release notes
|
||||||
|
* from CHANGELOG.md and writes them as JSON for the web app to import.
|
||||||
|
*
|
||||||
|
* Version resolution order:
|
||||||
|
* 1. KANDEV_VERSION env var (set by CI)
|
||||||
|
* 2. `git describe --tags --abbrev=0` (local dev with tags)
|
||||||
|
* 3. "dev" fallback
|
||||||
|
*/
|
||||||
|
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
||||||
|
import { execSync } from "node:child_process";
|
||||||
|
import { join, dirname } from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
const WEB_ROOT = join(__dirname, "..");
|
||||||
|
const REPO_ROOT = join(WEB_ROOT, "..", "..");
|
||||||
|
const OUTPUT_DIR = join(WEB_ROOT, "generated");
|
||||||
|
const OUTPUT_FILE = join(OUTPUT_DIR, "release-notes.json");
|
||||||
|
const CHANGELOG_PATH = join(REPO_ROOT, "CHANGELOG.md");
|
||||||
|
|
||||||
|
const FALLBACK = { version: "dev", date: "", notes: "" };
|
||||||
|
|
||||||
|
function getVersion() {
|
||||||
|
if (process.env.KANDEV_VERSION) {
|
||||||
|
return process.env.KANDEV_VERSION.replace(/^v/, "");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const tag = execSync("git describe --tags --abbrev=0", {
|
||||||
|
encoding: "utf-8",
|
||||||
|
stdio: ["pipe", "pipe", "pipe"],
|
||||||
|
}).trim();
|
||||||
|
return tag.replace(/^v/, "");
|
||||||
|
} catch {
|
||||||
|
return "dev";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractVersionNotes(changelog, version) {
|
||||||
|
// Split on ## headings and find the section for our version
|
||||||
|
const escapedVersion = version.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||||
|
const headerPattern = new RegExp(`^## ${escapedVersion}(?:\\s*-\\s*(\\S+))?\\s*$`, "m");
|
||||||
|
const headerMatch = changelog.match(headerPattern);
|
||||||
|
if (!headerMatch) return null;
|
||||||
|
|
||||||
|
const date = headerMatch[1] || "";
|
||||||
|
const sectionStart = headerMatch.index + headerMatch[0].length;
|
||||||
|
const rest = changelog.slice(sectionStart);
|
||||||
|
|
||||||
|
// Find the next ## heading (or end of string)
|
||||||
|
const nextHeading = rest.search(/^## /m);
|
||||||
|
const body = nextHeading === -1 ? rest : rest.slice(0, nextHeading);
|
||||||
|
const notes = body.trim();
|
||||||
|
|
||||||
|
return { date, notes };
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
mkdirSync(OUTPUT_DIR, { recursive: true });
|
||||||
|
const version = getVersion();
|
||||||
|
|
||||||
|
if (version === "dev") {
|
||||||
|
writeFileSync(OUTPUT_FILE, JSON.stringify(FALLBACK, null, 2) + "\n");
|
||||||
|
console.log("[release-notes] dev mode — wrote fallback");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let changelog;
|
||||||
|
try {
|
||||||
|
changelog = readFileSync(CHANGELOG_PATH, "utf-8");
|
||||||
|
} catch {
|
||||||
|
writeFileSync(OUTPUT_FILE, JSON.stringify(FALLBACK, null, 2) + "\n");
|
||||||
|
console.log("[release-notes] CHANGELOG.md not found — wrote fallback");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const extracted = extractVersionNotes(changelog, version);
|
||||||
|
if (!extracted) {
|
||||||
|
writeFileSync(OUTPUT_FILE, JSON.stringify({ version, date: "", notes: "" }, null, 2) + "\n");
|
||||||
|
console.log(`[release-notes] version ${version} not found in CHANGELOG.md — wrote empty notes`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = { version, date: extracted.date, notes: extracted.notes };
|
||||||
|
writeFileSync(OUTPUT_FILE, JSON.stringify(result, null, 2) + "\n");
|
||||||
|
console.log(`[release-notes] extracted notes for v${version}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
# git-cliff configuration
|
||||||
|
# https://git-cliff.org/docs/configuration
|
||||||
|
|
||||||
|
[changelog]
|
||||||
|
header = """# Changelog
|
||||||
|
|
||||||
|
All notable changes to Kandev.\n
|
||||||
|
"""
|
||||||
|
body = """
|
||||||
|
{% if version %}\
|
||||||
|
## {{ version | trim_start_matches(pat="v") }} - {{ timestamp | date(format="%Y-%m-%d") }}
|
||||||
|
{% else %}\
|
||||||
|
## Unreleased
|
||||||
|
{% endif %}\
|
||||||
|
{% for group, commits in commits | group_by(attribute="group") %}
|
||||||
|
### {{ group | upper_first }}
|
||||||
|
{% for commit in commits %}
|
||||||
|
- {{ commit.message }}\
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}\n
|
||||||
|
"""
|
||||||
|
trim = true
|
||||||
|
footer = ""
|
||||||
|
|
||||||
|
[git]
|
||||||
|
conventional_commits = true
|
||||||
|
filter_unconventional = false
|
||||||
|
split_commits = false
|
||||||
|
commit_preprocessors = [
|
||||||
|
# Convert (#NNN) references into clickable GitHub PR links
|
||||||
|
{ pattern = '\(#(\d+)\)', replace = "([#${1}](https://github.com/kdlbs/kandev/pull/${1}))" },
|
||||||
|
]
|
||||||
|
commit_parsers = [
|
||||||
|
{ message = "^feat", group = "Features" },
|
||||||
|
{ message = "^fix", group = "Bug Fixes" },
|
||||||
|
{ message = "^perf", group = "Performance" },
|
||||||
|
{ message = "^refactor", group = "Refactoring" },
|
||||||
|
{ message = "^docs", group = "Documentation" },
|
||||||
|
{ message = "^release", skip = true },
|
||||||
|
{ message = "^chore", skip = true },
|
||||||
|
{ message = "^ci", skip = true },
|
||||||
|
{ message = "^test", skip = true },
|
||||||
|
]
|
||||||
|
tag_pattern = "v[0-9]*"
|
||||||
|
sort_commits = "newest"
|
||||||
@@ -85,7 +85,7 @@ Versioning:
|
|||||||
|
|
||||||
Prerequisites:
|
Prerequisites:
|
||||||
- Run from a clean working tree on branch main.
|
- Run from a clean working tree on branch main.
|
||||||
- Required tools: git, node, npm, pnpm.
|
- Required tools: git, node, npm, pnpm, git-cliff.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
# Fully interactive release wizard
|
# Fully interactive release wizard
|
||||||
@@ -320,7 +320,7 @@ compute_next_versions() {
|
|||||||
ensure_prerequisites() {
|
ensure_prerequisites() {
|
||||||
log_step "1/5" "Checking prerequisites"
|
log_step "1/5" "Checking prerequisites"
|
||||||
|
|
||||||
for tool in git node npm pnpm; do
|
for tool in git node npm pnpm git-cliff; do
|
||||||
if command_exists "$tool"; then
|
if command_exists "$tool"; then
|
||||||
log_ok "$tool"
|
log_ok "$tool"
|
||||||
else
|
else
|
||||||
@@ -419,8 +419,10 @@ print_plan() {
|
|||||||
fi
|
fi
|
||||||
if [[ "$APP_SELECTED" -eq 1 ]]; then
|
if [[ "$APP_SELECTED" -eq 1 ]]; then
|
||||||
echo " $(dim "Steps for App:")"
|
echo " $(dim "Steps for App:")"
|
||||||
echo " $(dim "1.") Create annotated git tag $NEXT_APP_TAG"
|
echo " $(dim "1.") Generate CHANGELOG.md (git-cliff)"
|
||||||
echo " $(dim "2.") Push tag to origin (triggers GitHub Actions)"
|
echo " $(dim "2.") Commit and push changelog"
|
||||||
|
echo " $(dim "3.") Create annotated git tag $NEXT_APP_TAG"
|
||||||
|
echo " $(dim "4.") Push tag to origin (triggers GitHub Actions)"
|
||||||
fi
|
fi
|
||||||
echo
|
echo
|
||||||
}
|
}
|
||||||
@@ -432,6 +434,8 @@ commit_message() {
|
|||||||
echo "release: app $NEXT_APP_TAG, cli $NEXT_CLI_VERSION"
|
echo "release: app $NEXT_APP_TAG, cli $NEXT_CLI_VERSION"
|
||||||
elif [[ "$CLI_SELECTED" -eq 1 ]]; then
|
elif [[ "$CLI_SELECTED" -eq 1 ]]; then
|
||||||
echo "release: cli $NEXT_CLI_VERSION"
|
echo "release: cli $NEXT_CLI_VERSION"
|
||||||
|
elif [[ "$APP_SELECTED" -eq 1 ]]; then
|
||||||
|
echo "release: app $NEXT_APP_TAG"
|
||||||
else
|
else
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
@@ -467,6 +471,18 @@ apply_cli_release() {
|
|||||||
log_ok "CLI built"
|
log_ok "CLI built"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generate_changelog() {
|
||||||
|
[[ "$APP_SELECTED" -eq 1 ]] || return 0
|
||||||
|
|
||||||
|
log "Generating changelog for $(bold "$NEXT_APP_TAG")..."
|
||||||
|
if [[ "$DRY_RUN" -eq 1 ]]; then
|
||||||
|
echo " $(yellow "[dry-run]") git-cliff --tag $NEXT_APP_TAG -o CHANGELOG.md"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
git-cliff --tag "$NEXT_APP_TAG" -o "$ROOT_DIR/CHANGELOG.md"
|
||||||
|
log_ok "CHANGELOG.md updated"
|
||||||
|
}
|
||||||
|
|
||||||
commit_and_push_if_needed() {
|
commit_and_push_if_needed() {
|
||||||
local msg
|
local msg
|
||||||
msg="$(commit_message)"
|
msg="$(commit_message)"
|
||||||
@@ -474,16 +490,17 @@ commit_and_push_if_needed() {
|
|||||||
|
|
||||||
log_step "4/5" "Committing and pushing"
|
log_step "4/5" "Committing and pushing"
|
||||||
|
|
||||||
# App-only releases are tag-only and do not require a version commit.
|
|
||||||
if [[ "$DRY_RUN" -eq 1 ]]; then
|
if [[ "$DRY_RUN" -eq 1 ]]; then
|
||||||
echo " $(yellow "[dry-run]") git add apps/cli/package.json"
|
[[ "$CLI_SELECTED" -eq 1 ]] && echo " $(yellow "[dry-run]") git add apps/cli/package.json"
|
||||||
|
[[ "$APP_SELECTED" -eq 1 ]] && echo " $(yellow "[dry-run]") git add CHANGELOG.md"
|
||||||
echo " $(yellow "[dry-run]") git commit -m \"$msg\""
|
echo " $(yellow "[dry-run]") git commit -m \"$msg\""
|
||||||
echo " $(yellow "[dry-run]") git push origin $CURRENT_BRANCH"
|
echo " $(yellow "[dry-run]") git push origin $CURRENT_BRANCH"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log "Staging apps/cli/package.json..."
|
log "Staging release files..."
|
||||||
git -C "$ROOT_DIR" add apps/cli/package.json
|
[[ "$CLI_SELECTED" -eq 1 ]] && git -C "$ROOT_DIR" add apps/cli/package.json
|
||||||
|
[[ "$APP_SELECTED" -eq 1 && -f "$ROOT_DIR/CHANGELOG.md" ]] && git -C "$ROOT_DIR" add CHANGELOG.md
|
||||||
if git -C "$ROOT_DIR" diff --cached --quiet; then
|
if git -C "$ROOT_DIR" diff --cached --quiet; then
|
||||||
log_skip "No staged changes to commit"
|
log_skip "No staged changes to commit"
|
||||||
return 0
|
return 0
|
||||||
@@ -612,6 +629,7 @@ main() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
apply_cli_release
|
apply_cli_release
|
||||||
|
generate_changelog
|
||||||
commit_and_push_if_needed
|
commit_and_push_if_needed
|
||||||
create_and_push_app_tag
|
create_and_push_app_tag
|
||||||
publish_cli
|
publish_cli
|
||||||
|
|||||||
Reference in New Issue
Block a user