chat-logbook 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -60,6 +60,9 @@ The full problem statement, user stories, and direction live in the
60
60
  Code's terminal look.
61
61
  - **Soft delete with Trash.** Hide chats you don't want to see; restore
62
62
  them anytime.
63
+ - **Sortable lists.** Sort your chats by title, created time, or updated
64
+ time, and the choice sticks between visits. Trash sorts independently —
65
+ by deleted time by default.
63
66
  - **Custom chat titles.** Rename any chat — click its title in the
64
67
  list or conversation header, or select it and press `F2` / `↵`. Clear
65
68
  the title to fall back to the first message.
@@ -0,0 +1,2 @@
1
+ ALTER TABLE `chats_meta` ADD `deleted_at` integer;--> statement-breakpoint
2
+ UPDATE `chats_meta` SET `deleted_at` = `updated_at` WHERE `is_deleted` = 1;
@@ -22,6 +22,13 @@
22
22
  "when": 1779000000000,
23
23
  "tag": "0002_rename_sessions_meta_to_chats_meta",
24
24
  "breakpoints": true
25
+ },
26
+ {
27
+ "idx": 3,
28
+ "version": "6",
29
+ "when": 1779500000000,
30
+ "tag": "0003_add_deleted_at",
31
+ "breakpoints": true
25
32
  }
26
33
  ]
27
34
  }
package/api/dist/index.js CHANGED
@@ -8252,11 +8252,16 @@ var ingestionEvents = sqliteTable("ingestion_events", {
8252
8252
 
8253
8253
  // src/visibility.ts
8254
8254
  function loadChatVisibility(metadata2, opts) {
8255
- const trashed = new Set(metadata2.listDeletedIds());
8255
+ const deleted = metadata2.listDeleted();
8256
+ const trashed = new Set(deleted.map((r) => r.id));
8257
+ const deletedAtById = new Map(
8258
+ deleted.map((r) => [r.id, r.deletedAt?.getTime() ?? null])
8259
+ );
8256
8260
  const showTrashed = opts.includeTrashed === true;
8257
8261
  return {
8258
8262
  isTrashed: (internalId) => trashed.has(internalId),
8259
- isVisible: (internalId) => showTrashed || !trashed.has(internalId)
8263
+ isVisible: (internalId) => showTrashed || !trashed.has(internalId),
8264
+ deletedAt: (internalId) => deletedAtById.get(internalId) ?? null
8260
8265
  };
8261
8266
  }
8262
8267
 
@@ -8311,7 +8316,8 @@ function createApp({ archive: archive2, metadata: metadata2, webDistDir: webDist
8311
8316
  projectPath: row.projectPath ?? null,
8312
8317
  sourceFilePath: latestRaw?.sourcePath ?? null,
8313
8318
  createdAt: tsRange?.minTs ?? firstSeenAtMs,
8314
- updatedAt: tsRange?.maxTs ?? firstSeenAtMs
8319
+ updatedAt: tsRange?.maxTs ?? firstSeenAtMs,
8320
+ deletedAt: visibility.deletedAt(row.id)
8315
8321
  };
8316
8322
  if (isDeleted) chat.isDeleted = true;
8317
8323
  chats2.push(chat);
@@ -8700,7 +8706,10 @@ var chatsMeta = sqliteTable("chats_meta", {
8700
8706
  isDeleted: integer("is_deleted", { mode: "boolean" }).notNull().default(false),
8701
8707
  customTitle: text("custom_title"),
8702
8708
  createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(),
8703
- updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull()
8709
+ updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull(),
8710
+ // Set when a chat is moved to Trash; null while active. Drives the Trash
8711
+ // view's independent "Deleted time" sort axis.
8712
+ deletedAt: integer("deleted_at", { mode: "timestamp_ms" })
8704
8713
  });
8705
8714
 
8706
8715
  // src/metadata/repository.ts
@@ -8736,23 +8745,28 @@ function createMetadataRepository({
8736
8745
  id: internalId,
8737
8746
  isDeleted: true,
8738
8747
  createdAt: now,
8739
- updatedAt: now
8748
+ updatedAt: now,
8749
+ deletedAt: now
8740
8750
  }).onConflictDoUpdate({
8741
8751
  target: chatsMeta.id,
8742
- set: { isDeleted: true, updatedAt: now }
8752
+ set: { isDeleted: true, updatedAt: now, deletedAt: now }
8743
8753
  }).run();
8744
8754
  },
8745
8755
  restore(internalId) {
8746
8756
  const now = /* @__PURE__ */ new Date();
8747
- db.update(chatsMeta).set({ isDeleted: false, updatedAt: now }).where(eq(chatsMeta.id, internalId)).run();
8757
+ db.update(chatsMeta).set({ isDeleted: false, updatedAt: now, deletedAt: null }).where(eq(chatsMeta.id, internalId)).run();
8748
8758
  },
8749
8759
  isDeleted(internalId) {
8750
8760
  const row = db.select({ isDeleted: chatsMeta.isDeleted }).from(chatsMeta).where(eq(chatsMeta.id, internalId)).get();
8751
8761
  return row?.isDeleted ?? false;
8752
8762
  },
8753
- listDeletedIds() {
8754
- const rows = db.select({ id: chatsMeta.id }).from(chatsMeta).where(eq(chatsMeta.isDeleted, true)).all();
8755
- return rows.map((r) => r.id);
8763
+ getDeletedAt(internalId) {
8764
+ const row = db.select({ deletedAt: chatsMeta.deletedAt }).from(chatsMeta).where(eq(chatsMeta.id, internalId)).get();
8765
+ return row?.deletedAt ?? null;
8766
+ },
8767
+ listDeleted() {
8768
+ const rows = db.select({ id: chatsMeta.id, deletedAt: chatsMeta.deletedAt }).from(chatsMeta).where(eq(chatsMeta.isDeleted, true)).all();
8769
+ return rows.map((r) => ({ id: r.id, deletedAt: r.deletedAt ?? null }));
8756
8770
  },
8757
8771
  getCustomTitle(internalId) {
8758
8772
  const row = db.select({ customTitle: chatsMeta.customTitle }).from(chatsMeta).where(eq(chatsMeta.id, internalId)).get();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chat-logbook",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "A local-first conversation manager for Claude Code",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",