Remove the title field — a note is named by its first line
CI & Build / Build now, or wait for Android? (push) Successful in 3s
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Failing after 7s
CI & Build / Build & push image (push) Skipped
Desktop (Tauri) / Tauri desktop (Linux) (push) Failing after 7s
CI & Build / Python tests (push) Successful in 11s
Desktop (Tauri) / Windows installer (cross-compiled) (push) Failing after 31s
Desktop (Tauri) / Update manifest (push) Skipped
Android / Kotlin + Rust (APK) (push) Successful in 6m45s
CI & Build / Build now, or wait for Android? (push) Successful in 3s
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Failing after 7s
CI & Build / Build & push image (push) Skipped
Desktop (Tauri) / Tauri desktop (Linux) (push) Failing after 7s
CI & Build / Python tests (push) Successful in 11s
Desktop (Tauri) / Windows installer (cross-compiled) (push) Failing after 31s
Desktop (Tauri) / Update manifest (push) Skipped
Android / Kotlin + Rust (APK) (push) Successful in 6m45s
Operator (note 2897): "notes shouldn't have a title field." The concept of a NAME stays — search results, export filenames and the command palette all need one — but nothing is typed into it any more. `display_title` is now the first non-empty line of the body, falling back to the first checklist item. That fallback is what step 2 bought, and the reason this could not go first: a checklist had no body to be named from, so the title was its only name. Now every note has a body, and a note that is only a checklist is named by its first item. Gone everywhere: the column and note_revisions.title (0026), the field on the core's Note/NoteCreateInput/NoteRevision and its SQLite columns (user_version 7), `normalize_title`, the wire field, the FFI record and `NoteEdit::Title` / `ClearTitle`, the web editor's "Title (optional)" input and the card's <h3>, and the Android title field in both the compose sheet and the editor. **The search vector had to be rebuilt, not just left alone.** `notes.search_vector` is a STORED GENERATED column whose expression names `title` — Postgres refuses to drop a column another generated column depends on. It is dropped and recreated over `display_title` at weight A, which keeps the original intent: a note's NAME ranks above the rest of its body. **An imported title becomes the note's first body line.** Keep notes carry one, and so does any ThoughtSync export taken before this. Dropping it would silently lose text someone wrote; folding it in puts it exactly where a name now lives, so the note arrives named as it was. Skipped when the body already opens with that line, so re-importing an export this code produced doesn't stack duplicates. Two smaller things fell out. The Android editor loses its bold first field — one weight throughout, because the first line is the note's name but not a different KIND of text, which is most of step 4 arriving early. And `ClearTitle`'s justification comment moved to `ClearRemindAt`, which is now the surviving example of why NoteEdit is a list rather than a struct of options. Protocol note corrected to say what actually shipped: v2 is "no kind, no title", one bump for the pair. Verified with the local Rust gate this time, not by CI: fmt, clippy and 116 tests all green before pushing. It caught four things — orphaned serde attributes where fields were removed, a `wire::Preview.title` I deleted by mistake (a link preview still has one), nine retention fixtures inserting a dropped column, and four rustfmt diffs.
This commit is contained in:
@@ -93,8 +93,8 @@ mod tests {
|
||||
let when = Utc::now() - age;
|
||||
let stamped = when.to_rfc3339_opts(chrono::SecondsFormat::Millis, true);
|
||||
conn.execute(
|
||||
"INSERT INTO notes (id, title, body, created_at, updated_at, trashed, trashed_at)
|
||||
VALUES (?1, 'T', 'B', ?2, ?2, 1, ?2)",
|
||||
"INSERT INTO notes (id, body, created_at, updated_at, trashed, trashed_at)
|
||||
VALUES (?1, 'B', ?2, ?2, 1, ?2)",
|
||||
rusqlite::params![id, stamped],
|
||||
)
|
||||
.expect("insert");
|
||||
@@ -149,8 +149,8 @@ mod tests {
|
||||
fn an_untrashed_note_is_never_swept() {
|
||||
let conn = db();
|
||||
conn.execute(
|
||||
"INSERT INTO notes (id, title, body, created_at, updated_at, trashed)
|
||||
VALUES ('live', 'T', 'B', '2020-01-01T00:00:00.000Z', '2020-01-01T00:00:00.000Z', 0)",
|
||||
"INSERT INTO notes (id, body, created_at, updated_at, trashed)
|
||||
VALUES ('live', 'B', '2020-01-01T00:00:00.000Z', '2020-01-01T00:00:00.000Z', 0)",
|
||||
[],
|
||||
)
|
||||
.expect("insert");
|
||||
@@ -163,8 +163,8 @@ mod tests {
|
||||
// "Age unknown" must never resolve to "delete it".
|
||||
let conn = db();
|
||||
conn.execute(
|
||||
"INSERT INTO notes (id, title, body, created_at, updated_at, trashed, trashed_at)
|
||||
VALUES ('weird', 'T', 'B', '2020-01-01T00:00:00.000Z', '2020-01-01T00:00:00.000Z', 1, 'not a date')",
|
||||
"INSERT INTO notes (id, body, created_at, updated_at, trashed, trashed_at)
|
||||
VALUES ('weird', 'B', '2020-01-01T00:00:00.000Z', '2020-01-01T00:00:00.000Z', 1, 'not a date')",
|
||||
[],
|
||||
)
|
||||
.expect("insert");
|
||||
@@ -179,8 +179,8 @@ mod tests {
|
||||
let conn = db();
|
||||
let stamped = (Utc::now() - Duration::days(40)).to_rfc3339();
|
||||
conn.execute(
|
||||
"INSERT INTO notes (id, title, body, created_at, updated_at, trashed, trashed_at)
|
||||
VALUES ('server', 'T', 'B', ?1, ?1, 1, ?1)",
|
||||
"INSERT INTO notes (id, body, created_at, updated_at, trashed, trashed_at)
|
||||
VALUES ('server', 'B', ?1, ?1, 1, ?1)",
|
||||
rusqlite::params![stamped],
|
||||
)
|
||||
.expect("insert");
|
||||
|
||||
Reference in New Issue
Block a user