BlueSky & more on desktop lazurite.stormlightlabs.org/
tauri rust typescript bluesky appview atproto solid
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor: consolidate and reorganize explorer types and state management

+94 -104
+1
CHANGELOG.md
··· 6 6 7 7 - Forward/Back history navigation in the app rail/navigation, and thread drawer 8 8 - Added theme control (light/dark/system) with proper light theme support 9 + - Help section with keyboard shortcuts and usage help 9 10 10 11 ### 2026-04-07 11 12
docs/public/README.md

This is a binary file and will not be displayed.

+1 -10
docs/todo.md
··· 1 1 --- 2 2 title: "To-Do List/Parking Lot" 3 - updated: 2026-03-31 3 + updated: 2026-04-08 4 4 --- 5 5 6 6 ## Bugs 7 - 8 - ## Refactor 9 - 10 - Typeahead code is largely repeated in the following places: 11 - 12 - 1. `src/components/LoginPanel.tsx` 13 - 2. `src/components/deck/ColumnPicker/ProfileColumnPicker.tsx` 14 - 3. `src/components/explorer/ExplorerUrlBar.tsx` 15 - 4. `src/components/search/hooks/useSearchController.ts` 16 7 17 8 ## High Priority Updates 18 9
+7 -7
src/components/explorer/ExplorerPanel.tsx
··· 1 1 import { ExplorerController } from "$/lib/api/explorer"; 2 2 import { ProfileController } from "$/lib/api/profile"; 3 - import type { ExplorerNavigation, ExplorerTargetKind } from "$/lib/api/types/explorer"; 3 + import type { 4 + ExplorerNavigation, 5 + ExplorerTargetKind, 6 + ExplorerViewLevel, 7 + ExplorerViewState, 8 + } from "$/lib/api/types/explorer"; 4 9 import { NAVIGATION_EVENT } from "$/lib/constants/events"; 5 10 import { consumeQueuedExplorerTarget } from "$/lib/explorer-navigation"; 6 11 import { listen } from "@tauri-apps/api/event"; ··· 11 16 import { createExplorerState } from "./explorer-state"; 12 17 import { ExplorerBreadcrumb } from "./ExplorerBreadcrumb"; 13 18 import { ExplorerUrlBar } from "./ExplorerUrlBar"; 14 - import type { ExplorerViewLevel, ExplorerViewState } from "./types"; 15 19 import { CollectionView } from "./views/CollectionView"; 16 20 import { PdsView } from "./views/PdsView"; 17 21 import { RecordView } from "./views/RecordView"; 18 22 import { RepoView } from "./views/RepoView"; 19 - 20 - function resolveTargetLevel(kind: ExplorerTargetKind): ExplorerViewLevel { 21 - return kind as ExplorerViewLevel; 22 - } 23 23 24 24 function resolveParentInput(view: ExplorerViewState): string | null { 25 25 switch (view.level) { ··· 145 145 const resolved = await ExplorerController.resolveInput(submittedInput); 146 146 if (requestId !== resolveRequestId) return; 147 147 148 - const level = resolveTargetLevel(resolved.targetKind); 148 + const level = resolved.targetKind as ExplorerViewLevel; 149 149 150 150 const viewState = { level, input: submittedInput, resolved, loading: true, error: null, data: null }; 151 151
+1 -2
src/components/explorer/explorer-state.ts
··· 1 - import type { ExplorerTargetKind } from "$/lib/api/types/explorer"; 1 + import type { ExplorerState, ExplorerTargetKind, ExplorerViewState } from "$/lib/api/types/explorer"; 2 2 import { createStore, produce } from "solid-js/store"; 3 - import type { ExplorerState, ExplorerViewState } from "./types"; 4 3 5 4 export function createExplorerState() { 6 5 const [state, setState] = createStore<ExplorerState>({
-84
src/components/explorer/types.ts
··· 1 - import type { ResolvedExplorerInput } from "$/lib/api/types/explorer"; 2 - 3 - export type ExplorerViewLevel = "pds" | "repo" | "collection" | "record"; 4 - 5 - type PDSData = { repos: Array<PDSRepoData>; server: Record<string, unknown>; cursor: string | null }; 6 - 7 - type PDSRepoData = { did: string; head: string; rev: string; active: boolean; status: string | null }; 8 - 9 - type RepoViewCollection = { nsid: string }; 10 - 11 - type RepoViewData = { 12 - collections: Array<RepoViewCollection>; 13 - did: string; 14 - handle: string; 15 - pdsUrl: string | null; 16 - socialSummary?: { followerCount: number | null; followingCount: number | null } | null; 17 - }; 18 - 19 - type CollectionViewData = { 20 - records: Array<Record<string, unknown>>; 21 - cursor: string | null; 22 - did: string; 23 - collection: string; 24 - loadingMore: boolean; 25 - }; 26 - 27 - type RecordViewData = { 28 - record: Record<string, unknown>; 29 - cid: string | null; 30 - uri: string; 31 - labels: Array<Record<string, unknown>>; 32 - }; 33 - 34 - export type ExplorerViewState = { 35 - level: ExplorerViewLevel; 36 - input: string; 37 - resolved: ResolvedExplorerInput | null; 38 - loading: boolean; 39 - error: string | null; 40 - data: unknown; 41 - pdsData?: PDSData; 42 - repoData?: RepoViewData; 43 - collectionData?: CollectionViewData; 44 - recordData?: RecordViewData; 45 - }; 46 - 47 - export type ExplorerState = { 48 - inputValue: string; 49 - current: ExplorerViewState | null; 50 - history: ExplorerViewState[]; 51 - historyIndex: number; 52 - lexiconIcons: Record<string, string | null>; 53 - }; 54 - 55 - export type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue }; 56 - 57 - function isJsonValue(value: unknown): value is JsonValue { 58 - return (typeof value === "string" 59 - || typeof value === "number" 60 - || typeof value === "boolean" 61 - || value === null 62 - || (Array.isArray(value) && value.every(isJsonValue)) 63 - || (typeof value === "object" && value !== null && Object.values(value).every(isJsonValue))); 64 - } 65 - 66 - export const JsonValueAs = { 67 - string(value: unknown): string | null { 68 - return isJsonValue(value) && typeof value === "string" ? value : null; 69 - }, 70 - number(value: unknown): number | null { 71 - return isJsonValue(value) && typeof value === "number" ? value : null; 72 - }, 73 - boolean(value: unknown): boolean | null { 74 - return isJsonValue(value) && typeof value === "boolean" ? value : null; 75 - }, 76 - array(value: unknown): JsonValue[] | null { 77 - return isJsonValue(value) && Array.isArray(value) ? value : null; 78 - }, 79 - object(value: unknown): Record<string, JsonValue> | null { 80 - return isJsonValue(value) && typeof value === "object" && value !== null && !Array.isArray(value) 81 - ? (value as Record<string, JsonValue>) 82 - : null; 83 - }, 84 - };
+1 -1
src/components/explorer/views/RecordView.tsx
··· 1 1 import { RecordBacklinksPanel } from "$/components/diagnostics/RecordBacklinksPanel"; 2 - import { type JsonValue, JsonValueAs } from "$/components/explorer/types"; 3 2 import { ArrowIcon, Icon } from "$/components/shared/Icon"; 4 3 import { PostRichText } from "$/components/shared/PostRichText"; 4 + import { type JsonValue, JsonValueAs } from "$/lib/api/types/explorer"; 5 5 import { getStringProperty, isRecordLike, isString } from "$/lib/type-guards"; 6 6 import type { PostRecord } from "$/lib/types"; 7 7 import { createMemo, createSignal, For, type ParentProps, Show } from "solid-js";
+83
src/lib/api/types/explorer.ts
··· 29 29 export type RepoCarExport = { did: string; path: string; bytesWritten: number }; 30 30 31 31 export type TempBlobFile = { path: string; bytesWritten: number }; 32 + 33 + export type ExplorerViewLevel = ExplorerTargetKind; 34 + 35 + type PDSData = { repos: Array<PDSRepoData>; server: Record<string, unknown>; cursor: string | null }; 36 + 37 + type PDSRepoData = { did: string; head: string; rev: string; active: boolean; status: string | null }; 38 + 39 + type RepoViewCollection = { nsid: string }; 40 + 41 + type RepoViewData = { 42 + collections: Array<RepoViewCollection>; 43 + did: string; 44 + handle: string; 45 + pdsUrl: string | null; 46 + socialSummary?: { followerCount: number | null; followingCount: number | null } | null; 47 + }; 48 + 49 + type CollectionViewData = { 50 + records: Array<Record<string, unknown>>; 51 + cursor: string | null; 52 + did: string; 53 + collection: string; 54 + loadingMore: boolean; 55 + }; 56 + 57 + type RecordViewData = { 58 + record: Record<string, unknown>; 59 + cid: string | null; 60 + uri: string; 61 + labels: Array<Record<string, unknown>>; 62 + }; 63 + 64 + export type ExplorerViewState = { 65 + level: ExplorerViewLevel; 66 + input: string; 67 + resolved: ResolvedExplorerInput | null; 68 + loading: boolean; 69 + error: string | null; 70 + data: unknown; 71 + pdsData?: PDSData; 72 + repoData?: RepoViewData; 73 + collectionData?: CollectionViewData; 74 + recordData?: RecordViewData; 75 + }; 76 + 77 + export type ExplorerState = { 78 + inputValue: string; 79 + current: ExplorerViewState | null; 80 + history: ExplorerViewState[]; 81 + historyIndex: number; 82 + lexiconIcons: Record<string, string | null>; 83 + }; 84 + 85 + export type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue }; 86 + 87 + function isJsonValue(value: unknown): value is JsonValue { 88 + return (typeof value === "string" 89 + || typeof value === "number" 90 + || typeof value === "boolean" 91 + || value === null 92 + || (Array.isArray(value) && value.every(isJsonValue)) 93 + || (typeof value === "object" && value !== null && Object.values(value).every(isJsonValue))); 94 + } 95 + 96 + export const JsonValueAs = { 97 + string(value: unknown): string | null { 98 + return isJsonValue(value) && typeof value === "string" ? value : null; 99 + }, 100 + number(value: unknown): number | null { 101 + return isJsonValue(value) && typeof value === "number" ? value : null; 102 + }, 103 + boolean(value: unknown): boolean | null { 104 + return isJsonValue(value) && typeof value === "boolean" ? value : null; 105 + }, 106 + array(value: unknown): JsonValue[] | null { 107 + return isJsonValue(value) && Array.isArray(value) ? value : null; 108 + }, 109 + object(value: unknown): Record<string, JsonValue> | null { 110 + return isJsonValue(value) && typeof value === "object" && value !== null && !Array.isArray(value) 111 + ? (value as Record<string, JsonValue>) 112 + : null; 113 + }, 114 + };