feat: pages is_favorite, commandbar improvements, wiki ui
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d1286cd717
commit
8bffebfd36
|
|
@ -50,12 +50,29 @@ Beim Tippen werden **Contexts** (Jour Fixes, Projekte, Firmen, Personen) und **W
|
||||||
|
|
||||||
## Slash-Commands
|
## Slash-Commands
|
||||||
|
|
||||||
|
Scope-dynamische Commands verwenden den aktuell aktiven Scope (Privat/Firma). Scope-explizite Varianten mit Präfix `p` (Privat) oder `b` (Business/Firma) überschreiben den aktiven Scope.
|
||||||
|
|
||||||
| Command | Funktion |
|
| Command | Funktion |
|
||||||
|---------|----------|
|
|---------|----------|
|
||||||
| `/note [Text]` | Neues Topic im Daily Log erstellen |
|
| `/note [Text]` | Neues Topic im Daily Log (aktiver Scope) |
|
||||||
| `/todo [Text]` | Topic mit Wiedervorlage (heute) im Daily Log |
|
| `/pnote [Text]` | Neues Topic im Daily Log (Privat) |
|
||||||
|
| `/bnote [Text]` | Neues Topic im Daily Log (Firma) |
|
||||||
|
| `/todo [Text]` | Topic mit Wiedervorlage heute (aktiver Scope) |
|
||||||
|
| `/ptodo [Text]` | Topic mit Wiedervorlage heute (Privat) |
|
||||||
|
| `/btodo [Text]` | Topic mit Wiedervorlage heute (Firma) |
|
||||||
|
| `/page [Titel]` | Neue Wiki-Seite erstellen (aktiver Scope) |
|
||||||
|
| `/ppage [Titel]` | Neue Wiki-Seite erstellen (Privat) |
|
||||||
|
| `/bpage [Titel]` | Neue Wiki-Seite erstellen (Firma) |
|
||||||
|
| `/person [Name]` | Neuen Personen-Kontext erstellen (aktiver Scope) |
|
||||||
|
| `/pperson [Name]` | Neuen Personen-Kontext erstellen (Privat) |
|
||||||
|
| `/bperson [Name]` | Neuen Personen-Kontext erstellen (Firma) |
|
||||||
|
| `/firma [Name]` | Neuen Firmen-Kontext erstellen (aktiver Scope) |
|
||||||
|
| `/pfirma [Name]` | Neuen Firmen-Kontext erstellen (Privat) |
|
||||||
|
| `/bfirma [Name]` | Neuen Firmen-Kontext erstellen (Firma) |
|
||||||
| `/jf [Name]` | Zu passendem Jour-Fix-Kontext springen |
|
| `/jf [Name]` | Zu passendem Jour-Fix-Kontext springen |
|
||||||
|
|
||||||
|
Scope-spezifische Commands erscheinen in der Bar erst ab dem zweiten Zeichen (`/p...` / `/b...`), um Duplikate mit den scope-dynamischen Commands zu vermeiden.
|
||||||
|
|
||||||
Beispiel: `/note Anruf bei Steffen wegen Angebot` → sofort als Topic in `daily-log` gespeichert, ohne den aktuellen Screen zu verlassen.
|
Beispiel: `/note Anruf bei Steffen wegen Angebot` → sofort als Topic in `daily-log` gespeichert, ohne den aktuellen Screen zu verlassen.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
1.1.68
|
1.1.71
|
||||||
|
|
@ -100,7 +100,8 @@
|
||||||
|
|
||||||
const actions = [];
|
const actions = [];
|
||||||
|
|
||||||
if ("/note".startsWith(cmd)) {
|
// Show scope-dynamic /note only when not narrowed to /pnote or /bnote
|
||||||
|
if ("/note".startsWith(cmd) && !"/pnote".startsWith(cmd) && !"/bnote".startsWith(cmd)) {
|
||||||
actions.push({
|
actions.push({
|
||||||
id: "cmd-note",
|
id: "cmd-note",
|
||||||
type: "action",
|
type: "action",
|
||||||
|
|
@ -117,7 +118,7 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("/pnote".startsWith(cmd)) {
|
if ("/pnote".startsWith(cmd) && cmd.length > 1) {
|
||||||
actions.push({
|
actions.push({
|
||||||
id: "cmd-pnote",
|
id: "cmd-pnote",
|
||||||
type: "action",
|
type: "action",
|
||||||
|
|
@ -133,7 +134,7 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("/bnote".startsWith(cmd)) {
|
if ("/bnote".startsWith(cmd) && cmd.length > 1) {
|
||||||
actions.push({
|
actions.push({
|
||||||
id: "cmd-bnote",
|
id: "cmd-bnote",
|
||||||
type: "action",
|
type: "action",
|
||||||
|
|
@ -149,7 +150,8 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("/todo".startsWith(cmd)) {
|
// Show scope-dynamic /todo only when not narrowed to /ptodo or /btodo
|
||||||
|
if ("/todo".startsWith(cmd) && !"/ptodo".startsWith(cmd) && !"/btodo".startsWith(cmd)) {
|
||||||
actions.push({
|
actions.push({
|
||||||
id: "cmd-todo",
|
id: "cmd-todo",
|
||||||
type: "action",
|
type: "action",
|
||||||
|
|
@ -166,7 +168,7 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("/ptodo".startsWith(cmd)) {
|
if ("/ptodo".startsWith(cmd) && cmd.length > 1) {
|
||||||
actions.push({
|
actions.push({
|
||||||
id: "cmd-ptodo",
|
id: "cmd-ptodo",
|
||||||
type: "action",
|
type: "action",
|
||||||
|
|
@ -182,7 +184,7 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("/btodo".startsWith(cmd)) {
|
if ("/btodo".startsWith(cmd) && cmd.length > 1) {
|
||||||
actions.push({
|
actions.push({
|
||||||
id: "cmd-btodo",
|
id: "cmd-btodo",
|
||||||
type: "action",
|
type: "action",
|
||||||
|
|
@ -198,7 +200,7 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("/page".startsWith(cmd)) {
|
if ("/page".startsWith(cmd) && !"/ppage".startsWith(cmd) && !"/bpage".startsWith(cmd)) {
|
||||||
actions.push({
|
actions.push({
|
||||||
id: "cmd-page",
|
id: "cmd-page",
|
||||||
type: "action",
|
type: "action",
|
||||||
|
|
@ -217,7 +219,7 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("/ppage".startsWith(cmd)) {
|
if ("/ppage".startsWith(cmd) && cmd.length > 1) {
|
||||||
actions.push({
|
actions.push({
|
||||||
id: "cmd-ppage",
|
id: "cmd-ppage",
|
||||||
type: "action",
|
type: "action",
|
||||||
|
|
@ -235,7 +237,7 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("/bpage".startsWith(cmd)) {
|
if ("/bpage".startsWith(cmd) && cmd.length > 1) {
|
||||||
actions.push({
|
actions.push({
|
||||||
id: "cmd-bpage",
|
id: "cmd-bpage",
|
||||||
type: "action",
|
type: "action",
|
||||||
|
|
|
||||||
|
|
@ -96,9 +96,9 @@
|
||||||
|
|
||||||
<section class="rounded-lg border border-[#333] bg-[#1a1a1a]">
|
<section class="rounded-lg border border-[#333] bg-[#1a1a1a]">
|
||||||
<!-- Tab bar -->
|
<!-- Tab bar -->
|
||||||
<div class="flex border-b border-[#333]">
|
<div class="flex overflow-x-auto border-b border-[#333] scrollbar-none">
|
||||||
<button
|
<button
|
||||||
class="flex items-center gap-1.5 px-4 py-2.5 text-sm font-semibold transition-colors border-b-2 -mb-px {activeTab === 'notebooks' ? 'border-accent text-accent' : 'border-transparent text-muted hover:text-white'}"
|
class="flex shrink-0 items-center gap-1.5 px-4 py-2.5 text-sm font-semibold transition-colors border-b-2 -mb-px {activeTab === 'notebooks' ? 'border-accent text-accent' : 'border-transparent text-muted hover:text-white'}"
|
||||||
onclick={() => setTab('notebooks')}
|
onclick={() => setTab('notebooks')}
|
||||||
>
|
>
|
||||||
<span>📓</span>
|
<span>📓</span>
|
||||||
|
|
@ -108,7 +108,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="flex items-center gap-1.5 px-4 py-2.5 text-sm font-semibold transition-colors border-b-2 -mb-px {activeTab === 'favorites' ? 'border-warning text-warning' : 'border-transparent text-muted hover:text-white'}"
|
class="flex shrink-0 items-center gap-1.5 px-4 py-2.5 text-sm font-semibold transition-colors border-b-2 -mb-px {activeTab === 'favorites' ? 'border-warning text-warning' : 'border-transparent text-muted hover:text-white'}"
|
||||||
onclick={() => setTab('favorites')}
|
onclick={() => setTab('favorites')}
|
||||||
>
|
>
|
||||||
<span>★</span>
|
<span>★</span>
|
||||||
|
|
@ -118,27 +118,28 @@
|
||||||
{/if}
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="flex items-center gap-1.5 px-4 py-2.5 text-sm font-semibold transition-colors border-b-2 -mb-px {activeTab === 'recent' ? 'border-[#aaa] text-[#ccc]' : 'border-transparent text-muted hover:text-white'}"
|
class="flex shrink-0 items-center gap-1.5 px-4 py-2.5 text-sm font-semibold transition-colors border-b-2 -mb-px {activeTab === 'recent' ? 'border-[#aaa] text-[#ccc]' : 'border-transparent text-muted hover:text-white'}"
|
||||||
onclick={() => setTab('recent')}
|
onclick={() => setTab('recent')}
|
||||||
>
|
>
|
||||||
<span>✎</span>
|
<span>✎</span>
|
||||||
<span>Zuletzt bearbeitet</span>
|
<span>Zuletzt bearbeitet</span>
|
||||||
</button>
|
</button>
|
||||||
<!-- Actions (right side) -->
|
|
||||||
{#if activeTab === 'notebooks'}
|
|
||||||
<div class="ml-auto flex items-center gap-2 px-3">
|
|
||||||
<button
|
|
||||||
class="rounded bg-accent px-3 py-1.5 text-sm font-medium text-white hover:bg-accent/80"
|
|
||||||
onclick={addPage}
|
|
||||||
>+ Seite</button>
|
|
||||||
<button
|
|
||||||
class="rounded bg-white/10 px-3 py-1.5 text-sm font-medium text-muted hover:bg-white/20 hover:text-white"
|
|
||||||
onclick={() => creatingNotebook = true}
|
|
||||||
>+ Notizbuch</button>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Actions bar (Notebooks tab only) -->
|
||||||
|
{#if activeTab === 'notebooks'}
|
||||||
|
<div class="flex items-center gap-2 border-b border-[#2a2a2a] px-3 py-2">
|
||||||
|
<button
|
||||||
|
class="rounded bg-accent px-3 py-1.5 text-sm font-medium text-white hover:bg-accent/80"
|
||||||
|
onclick={addPage}
|
||||||
|
>+ Seite</button>
|
||||||
|
<button
|
||||||
|
class="rounded bg-white/10 px-3 py-1.5 text-sm font-medium text-muted hover:bg-white/20 hover:text-white"
|
||||||
|
onclick={() => creatingNotebook = true}
|
||||||
|
>+ Notizbuch</button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<!-- Notebooks tab -->
|
<!-- Notebooks tab -->
|
||||||
{#if activeTab === 'notebooks'}
|
{#if activeTab === 'notebooks'}
|
||||||
<div class="divide-y divide-[#2a2a2a]">
|
<div class="divide-y divide-[#2a2a2a]">
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
-- no-op: is_favorite column added in 0012_chunky_stature.sql
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER TABLE `pages` ADD `is_favorite` integer DEFAULT false NOT NULL;
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -78,6 +78,20 @@
|
||||||
"when": 1771857652883,
|
"when": 1771857652883,
|
||||||
"tag": "0010_sharp_bishop",
|
"tag": "0010_sharp_bishop",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 11,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1771900000000,
|
||||||
|
"tag": "0011_pages_is_favorite",
|
||||||
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 12,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1772004047537,
|
||||||
|
"tag": "0012_chunky_stature",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -85,6 +85,7 @@ export const pages = sqliteTable('pages', {
|
||||||
title: text('title').notNull(),
|
title: text('title').notNull(),
|
||||||
body: text('body').notNull().default(''),
|
body: text('body').notNull().default(''),
|
||||||
isPrivate: integer('is_private', { mode: 'boolean' }).notNull().default(false),
|
isPrivate: integer('is_private', { mode: 'boolean' }).notNull().default(false),
|
||||||
|
isFavorite: integer('is_favorite', { mode: 'boolean' }).notNull().default(false),
|
||||||
sortOrder: integer('sort_order').notNull().default(0),
|
sortOrder: integer('sort_order').notNull().default(0),
|
||||||
updatedAt: text('updated_at').notNull(),
|
updatedAt: text('updated_at').notNull(),
|
||||||
deletedAt: text('deleted_at'),
|
deletedAt: text('deleted_at'),
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,7 @@ function mapPage(row: typeof pages.$inferSelect): Page {
|
||||||
title: row.title,
|
title: row.title,
|
||||||
body: row.body,
|
body: row.body,
|
||||||
isPrivate: row.isPrivate,
|
isPrivate: row.isPrivate,
|
||||||
|
isFavorite: row.isFavorite,
|
||||||
sortOrder: row.sortOrder,
|
sortOrder: row.sortOrder,
|
||||||
updatedAt: row.updatedAt,
|
updatedAt: row.updatedAt,
|
||||||
deletedAt: row.deletedAt,
|
deletedAt: row.deletedAt,
|
||||||
|
|
@ -276,7 +277,7 @@ export async function pushChanges(request: SyncPushRequest, userId: string): Pro
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const pg of pgs) {
|
for (const pg of pgs) {
|
||||||
const row = { id: pg.id, userId, title: pg.title, body: pg.body, isPrivate: pg.isPrivate, sortOrder: pg.sortOrder, updatedAt: pg.updatedAt, deletedAt: pg.deletedAt, purgedAt: pg.purgedAt ?? null, version: pg.version };
|
const row = { id: pg.id, userId, title: pg.title, body: pg.body, isPrivate: pg.isPrivate, isFavorite: pg.isFavorite ?? false, sortOrder: pg.sortOrder, updatedAt: pg.updatedAt, deletedAt: pg.deletedAt, purgedAt: pg.purgedAt ?? null, version: pg.version };
|
||||||
if (await upsertEntity(pages, row, conflicts, 'page', userId)) accepted++;
|
if (await upsertEntity(pages, row, conflicts, 'page', userId)) accepted++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue