Skip to content

Move iOS search and page creation to bottom toolbar#17

Merged
Chefski merged 3 commits into
devfrom
bottom-search-create-toolbar
Jul 6, 2026
Merged

Move iOS search and page creation to bottom toolbar#17
Chefski merged 3 commits into
devfrom
bottom-search-create-toolbar

Conversation

@Chefski

@Chefski Chefski commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Summary

  • remove the compact iOS sidebar search destination in favor of native bottom-toolbar search
  • move root page creation from the top page-tree toolbar to a separate bottom toolbar button
  • reuse the existing SearchViewModel/results UI inside the page browser when search is focused or populated

Verification

  • swiftlint lint --strict --no-cache -- docmostly/Features/PageTree/PageTreeView.swift docmostly/Features/Search/SearchView.swift docmostly/Features/Spaces/SidebarRootView.swift
  • git diff --check
  • XcodeBuildMCP build_run_sim on iPhone 17 / iOS 26.5
  • XcodeBuildMCP runtime UI check: bottom search field focuses, typed query shows results, separate New Page button opens creation sheet

Apple docs referenced:

Use SwiftUI's native bottom toolbar search item for the iOS page browser and move the root page creation action into a separate bottom toolbar button. Reuse the existing search results model so focused search swaps the page list into live results without duplicating search logic.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 3 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread docmostly/Features/PageTree/PageTreeView.swift Outdated
@greptile-apps

greptile-apps Bot commented Jul 6, 2026

Copy link
Copy Markdown

Greptile Summary

This PR migrates iOS search and root page creation from the top navigation toolbar/sidebar into a native bottom toolbar, and refactors the search UI into a reusable SearchResultsContent component that is embedded directly in PageTreeView when the search field is active.

  • Bottom toolbar (iOS): Adds DefaultToolbarItem(kind: .search, placement: .bottomBar) and a dedicated "New Page" button using square.and.pencil; the non-iOS toolbar retains the original plus button unchanged.
  • In-tree search: PageTreeView gains a private SearchViewModel and a 300 ms debounced .task(id: searchTaskKey) that drives SearchResultsContent inline, scoped to the current space via the new PageTreeSearchProvider.
  • Sidebar cleanup: The compact iOS sidebar "Search" NavigationLink is removed in SidebarRootView.

Confidence Score: 5/5

Safe to merge — the change is additive on iOS, non-breaking on macOS, and the search/debounce wiring follows the same pattern already in SearchView.

All three files change in a straightforward, well-scoped way. The new PageTreeSearchProvider correctly threads the current space ID through the existing SearchProviding protocol, the debounce task mirrors the established pattern in SearchView, and the #if os(iOS) guards keep the macOS toolbar unchanged. The only finding is a file-organisation nit.

No files require special attention beyond the cosmetic note about SearchResultsContent belonging in its own file.

Important Files Changed

Filename Overview
docmostly/Features/PageTree/PageTreeView.swift Adds iOS-only bottom-bar search via DefaultToolbarItem and a standalone New Page button; embeds SearchResultsContent/SearchViewModel for in-tree search with correct debounce and task keying; private helper types are correctly scoped.
docmostly/Features/Search/SearchView.swift Extracts search UI into reusable SearchResultsContent struct; logic is correct but the new type belongs in its own file per the project structure rule.
docmostly/Features/Spaces/SidebarRootView.swift Removes the compact iOS sidebar Search navigation link; straightforward deletion with no regressions.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User opens Space on iOS] --> B[PageTreeView shown]
    B --> C{Bottom toolbar}
    C -->|Tap search icon| D[isSearchPresented = true
searchable focused]
    C -->|Tap pencil icon| E[PageCreationSheet]
    D --> F{isShowingSearch = true}
    F -->|query empty| G[SearchResultsContent
shows filter bar only]
    F -->|query >= 2 chars| H[task debounce 300ms]
    H --> I[PageTreeSearchProvider.search
scoped to current space]
    I --> J[SearchResultsContent
shows results]
    J -->|Tap result| K[SearchResultDestinationView]
    J -->|Show More| L[SearchViewModel.loadMore]
    D -->|Cancel| M[query cleared
isSearchPresented = false]
    M --> N[Back to normal
PageTreeView list]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[User opens Space on iOS] --> B[PageTreeView shown]
    B --> C{Bottom toolbar}
    C -->|Tap search icon| D[isSearchPresented = true
searchable focused]
    C -->|Tap pencil icon| E[PageCreationSheet]
    D --> F{isShowingSearch = true}
    F -->|query empty| G[SearchResultsContent
shows filter bar only]
    F -->|query >= 2 chars| H[task debounce 300ms]
    H --> I[PageTreeSearchProvider.search
scoped to current space]
    I --> J[SearchResultsContent
shows results]
    J -->|Tap result| K[SearchResultDestinationView]
    J -->|Show More| L[SearchViewModel.loadMore]
    D -->|Cancel| M[query cleared
isSearchPresented = false]
    M --> N[Back to normal
PageTreeView list]
Loading

Reviews (3): Last reviewed commit: "Pin page tree search scope to displayed ..." | Re-trigger Greptile

Comment thread docmostly/Features/PageTree/PageTreeView.swift Outdated
Include the viewed space in the page-tree search task identity so active queries refresh when switching spaces, and simplify the debounce cancellation path.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 71c1453227

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

do {
try await Task.sleep(for: .milliseconds(300))
try Task.checkCancellation()
await searchViewModel.search(provider: appState)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Pin current-space search to this page tree

When this embedded search uses SearchViewModel.search(provider: appState), the .currentSpace filter is resolved from AppState.selectedSpaceID rather than the space being displayed by PageTreeView. In this new flow, a user can search all spaces from space A, open a result from space B, and SearchResultDestinationView updates selectedSpaceID to B without changing the sidebar destination; after navigating back, the picker still says “Current Space” in space A but subsequent searches/load-more requests are scoped to B. Pass a provider or pinned current-space ID based on space.id for the page-tree search path.

Useful? React with 👍 / 👎.

.task(id: pageBrowserTaskKey) {
await refreshBrowser()
}
.task(id: searchViewModel.searchTaskKey) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Re-run embedded search when the space changes

When a user searches in one space and then selects another space, PageTreeView state is reused while this new task is keyed only by the query/filter values. With a non-empty query the search UI therefore remains visible in the new space without issuing a new request, leaving stale results from the previous space on screen; pressing “Show More” can also reuse the old offset against the new scope. Include space.id in the search task key or clear/reload the search model when the displayed space changes.

Useful? React with 👍 / 👎.

greptile-apps[bot]
greptile-apps Bot previously approved these changes Jul 6, 2026
Use a page-tree search provider that resolves the current-space scope from the displayed space instead of mutable global navigation selection.
@greptile-apps greptile-apps Bot dismissed their stale review July 6, 2026 01:40

Dismissed because a newer commit was pushed; Greptile will re-review the current head.

@Chefski Chefski merged commit 784d35c into dev Jul 6, 2026
8 checks passed
@Chefski Chefski deleted the bottom-search-create-toolbar branch July 6, 2026 01:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant