Note editor sidebar, full-doc assist, persistent drafts, version history

NoteEditorView: two-column sidebar layout (project/milestone/tags/assist
always visible), removed assist toggle button, InlineAssistPanel removed.

Writing assist: whole_doc mode rewrites entire document; DiffView.vue
replaces editor during review showing full-document diff. Scope dropdown
in sidebar switches between whole-document and section modes.

Persistent drafts: migration 0022 adds note_drafts (UNIQUE per note+user)
and note_versions (max 20, auto-pruned) tables. Draft saved after generation
completes, restored on editor mount, cleared on accept/reject. Version
snapshot created automatically whenever note body changes on save.

HistoryPanel.vue: version list + DiffView modal, restore button writes
body back to editor.

Config: OLLAMA_NUM_CTX default raised to 65536; assist num_predict now
tracks Config.OLLAMA_NUM_CTX instead of a hardcoded 4096.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-05 17:10:55 -05:00
parent b11a92f32d
commit 9036dfd931
17 changed files with 1275 additions and 278 deletions
+118
View File
@@ -0,0 +1,118 @@
<script setup lang="ts">
import { computed } from "vue";
import type { DiffLine } from "@/composables/useAssist";
const props = defineProps<{
diff: DiffLine[];
}>();
const insertions = computed(() => props.diff.filter(l => l.type === 'insert').length);
const deletions = computed(() => props.diff.filter(l => l.type === 'delete').length);
function markerFor(type: DiffLine['type']): string {
if (type === 'insert') return '+';
if (type === 'delete') return '';
return ' ';
}
</script>
<template>
<div class="diff-view-full">
<div class="diff-summary-bar">
<span class="diff-summary-ins">+{{ insertions }} insertion{{ insertions !== 1 ? 's' : '' }}</span>
<span class="diff-summary-del">{{ deletions }} deletion{{ deletions !== 1 ? 's' : '' }}</span>
</div>
<div class="diff-scroll">
<div v-if="!diff.length" class="diff-empty">No changes.</div>
<div
v-for="(line, i) in diff"
:key="i"
:class="['diff-line', `diff-${line.type}`]"
>
<span class="diff-marker">{{ markerFor(line.type) }}</span>
<span class="diff-text">{{ line.text }}</span>
</div>
</div>
</div>
</template>
<style scoped>
.diff-view-full {
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
border: 1px solid var(--color-input-border);
border-radius: var(--radius-sm);
background: var(--color-bg);
overflow: hidden;
}
.diff-summary-bar {
position: sticky;
top: 0;
flex-shrink: 0;
display: flex;
gap: 1rem;
padding: 0.4rem 0.75rem;
background: var(--color-bg-secondary);
border-bottom: 1px solid var(--color-border);
font-size: 0.78rem;
font-family: monospace;
font-weight: 600;
}
.diff-summary-ins { color: var(--color-success, #2ecc71); }
.diff-summary-del { color: var(--color-danger, #e74c3c); }
.diff-scroll {
flex: 1;
min-height: 0;
overflow-y: auto;
padding: 0.3rem 0;
}
.diff-empty {
padding: 0.75rem;
font-size: 0.85rem;
color: var(--color-text-muted);
font-style: italic;
}
.diff-line {
display: flex;
align-items: baseline;
padding: 0.04rem 0.5rem;
line-height: 1.55;
font-family: monospace;
font-size: 0.82rem;
}
.diff-delete {
background: color-mix(in srgb, var(--color-danger, #e74c3c) 12%, transparent);
color: var(--color-danger, #e74c3c);
}
.diff-insert {
background: color-mix(in srgb, var(--color-success, #2ecc71) 12%, transparent);
color: var(--color-success, #2ecc71);
}
.diff-equal {
color: var(--color-text-muted);
}
.diff-marker {
flex-shrink: 0;
width: 1rem;
text-align: center;
user-select: none;
font-weight: 700;
}
.diff-text {
flex: 1;
white-space: pre-wrap;
word-break: break-word;
}
</style>