From 508980ea5ce177a42af057cbac313d7ae9f06ccf Mon Sep 17 00:00:00 2001 From: scgopi Date: Tue, 1 Sep 2026 21:09:37 -0700 Subject: [PATCH 1/2] Start user-templates-first branch Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_0199VUTtyfuWHbu2hHXpHYw2 From 67edbe8a7588a07a46f44ec454e5712bbf3d7429 Mon Sep 17 00:00:00 2001 From: scgopi Date: Tue, 1 Sep 2026 21:09:57 -0700 Subject: [PATCH 2/2] Template picker: your templates above the starters Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_0199VUTtyfuWHbu2hHXpHYw2 --- .../Features/Project/ProjectFeatureState.swift | 10 ++++++---- graphcode/Tests/TemplatePickerTests.swift | 13 +++++++------ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/graphcode/Sources/Features/Project/ProjectFeatureState.swift b/graphcode/Sources/Features/Project/ProjectFeatureState.swift index 5ea1a4f1..210d58e4 100644 --- a/graphcode/Sources/Features/Project/ProjectFeatureState.swift +++ b/graphcode/Sources/Features/Project/ProjectFeatureState.swift @@ -150,9 +150,11 @@ extension ProjectFeature.State { !unfilledTokens.isEmpty } - /// The picker's rows, already grouped: **This project** first, then **All - /// projects**, each sorted by name. The query filters on name and body — a - /// template is findable by what it says, not only by what it is called. + /// The picker's rows, already grouped: **This project** first, then **Your + /// templates**, then **Starters**. What a person wrote outranks what shipped — + /// the starters are scaffolding for a first week, and a library that has grown past + /// them should not scroll under them every time. The query filters on name and + /// body — a template is findable by what it says, not only by what it is called. var templatePickerRows: [ProjectFeature.TemplatePickerRow] { let query = templates.query.trimmingCharacters(in: .whitespaces).lowercased() let matches: (PromptTemplate) -> Bool = { template in @@ -182,8 +184,8 @@ extension ProjectFeature.State { StarterTemplates.priority(of: $0.id) < StarterTemplates.priority(of: $1.id) } return byName(project).map { ProjectFeature.TemplatePickerRow(template: $0, scope: .project) } - + byPriority.map { ProjectFeature.TemplatePickerRow(template: $0, scope: .starter) } + byName(mine).map { ProjectFeature.TemplatePickerRow(template: $0, scope: .home) } + + byPriority.map { ProjectFeature.TemplatePickerRow(template: $0, scope: .starter) } } /// The fields still holding a `{token}`, in the order `⇥` walks them — the order diff --git a/graphcode/Tests/TemplatePickerTests.swift b/graphcode/Tests/TemplatePickerTests.swift index a55463cd..94a93f27 100644 --- a/graphcode/Tests/TemplatePickerTests.swift +++ b/graphcode/Tests/TemplatePickerTests.swift @@ -177,11 +177,12 @@ struct TemplatePickerTests { } /// Starters and a person's own templates are two sections for good — scaffolding - /// and a library are different things — and the starters keep their shipped order, + /// and a library are different things. Yours sit above the starters, since what you + /// wrote outranks what shipped, and the starters keep their shipped order below, /// because the group is a ladder: lead a team, then Goal, then Timed, then the rest. @Test @MainActor - func startersKeepTheirLadderOrderAndYourOwnSitBelow() async { + func yourOwnSitAboveTheStartersWhichKeepTheirLadderOrder() async { // Deliberately handed over scrambled and alphabetically hostile. let shipped = StarterTemplates.all let scrambled = Array(shipped.reversed()) @@ -193,10 +194,10 @@ struct TemplatePickerTests { await store.send(.templatesButtonTapped) await store.send(.templateLibraryChanged(library)) let rows = store.state.templatePickerRows - #expect(rows.map(\.scope) == Array(repeating: .starter, count: shipped.count) + [.home]) - #expect(rows.prefix(shipped.count).map(\.template.name) == shipped.map(\.name)) - #expect(rows.first?.template.name == "Lead a team toward a goal") - #expect(rows.last?.template.name == "Aardvark brief") + #expect(rows.map(\.scope) == [.home] + Array(repeating: .starter, count: shipped.count)) + #expect(rows.first?.template.name == "Aardvark brief") + #expect(rows.dropFirst().map(\.template.name) == shipped.map(\.name)) + #expect(rows[1].template.name == "Lead a team toward a goal") #expect(ProjectFeature.TemplatePickerScope.home.displayName == "Your templates") }