Ka-Note/plans/rename-cascade.md

100 lines
3.4 KiB
Markdown

# Plan: Rename Cascade + Duplicate Check
## Features
1. After renaming a person/project/company, update all @mentions in existing text
2. On rename: prevent duplicate names of the same type
---
## Affected Files
| File | Change |
|------|--------|
| `client/src/lib/utils/mentionReplace.ts` | NEW — `renameMentions(text, oldName, newName, type)` |
| `client/src/lib/db/repositories.ts` | ADD `renameMentionCascade(oldName, newName, type)` |
| `client/src/lib/components/ContextHeader.svelte` | UPDATE `saveEdit()` — duplicate check + cascade; add warning UI |
---
## New File: `mentionReplace.ts`
Pure function, no Dexie dependency.
```typescript
export function renameMentions(
text: string,
oldName: string,
newName: string,
type: 'person' | 'project' | 'company'
): string
```
**Regex patterns to replace:**
| Type | Unquoted | Quoted |
|------|----------|--------|
| person | `@OldName` | `@"Old Name"` |
| project | `@P:OldName` / `@p:OldName` | `@P:"Old Name"` |
| company | `@F:OldName` / `@f:OldName` | `@F:"Old Name"` |
| assignment (person only) | `-> OldName` | n/a (not supported by extractors) |
**Replacement:** use `quoteMention`-logic — add quotes if newName contains spaces.
**Boundary condition:** match must end at `[\w"]` boundary to avoid partial matches (e.g. `@Alice` must not match `@AliceBob`).
---
## `renameMentionCascade` in repositories.ts
Run in a single Dexie transaction (`rw` on historyEntries, topics, contexts, ratings).
**Fields to scan:**
| Table | Field | Notes |
|-------|-------|-------|
| `historyEntries` | `text` | Primary mention location |
| `topics` | `title` | Inline mentions possible |
| `contexts` | `meta.notes` | PersonMeta / ProjectMeta / CompanyMeta only |
| `ratings` | `comment` | Optional, may have @mentions |
| `ratings` | `personName` | Structured field, person rename only (exact match) |
For each changed record: increment `version`, update `updatedAt`.
Pattern to follow: `renameTitleInLinks()` already exists in repositories.ts (line ~541).
---
## `ContextHeader.svelte` — `saveEdit()` changes
```
1. trimmed = nameInput.trim()
2. if trimmed === editableName() → return (no-op)
3. if type is person/project/company:
- check contextNameExists(fullName(trimmed), type)
- if exists AND id !== context.id → show duplicate warning, revert, return
4. oldName = editableName()
5. await upsertContext({ id, name: fullName(trimmed) })
6. if type is person/project/company:
- await renameMentionCascade(oldName, trimmed, type)
```
Add `duplicateWarning` state + small error message below input.
---
## Sequencing
1. `mentionReplace.ts` — implement + manual regex tests
2. `repositories.ts` — add `renameMentionCascade`
3. `ContextHeader.svelte` — wire up
---
## Open Questions
1. **`-> Name` with spaces:** `extractors.ts` arrow pattern only matches `[\w]+`. If renamed from single-word to multi-word, arrow assignments break silently. Extend extractors to support `-> "First Last"`, or accept as edge case?
2. **`EventMeta.participants`:** Plain `string[]`, not mention syntax. Update on person rename?
3. **Case-only rename** (Alice → ALICE): `contextNameExists` is case-insensitive — will incorrectly block. Need to exclude self from duplicate check.
4. **Performance:** Full table scan (no text index). Acceptable for local-first small datasets — add a comment.
5. **Sync conflicts:** Cascade causes many `version++`. Last-write-wins acceptable, or need special marker?