+
{!isSidebarOpen && (
diff --git a/frontend/src/utils/mockSidebarChats.ts b/frontend/src/utils/mockSidebarChats.ts
new file mode 100644
index 00000000..5869e710
--- /dev/null
+++ b/frontend/src/utils/mockSidebarChats.ts
@@ -0,0 +1,66 @@
+import type { Conversation } from "@opensecret/react";
+
+/** Prefix for fake sidebar rows (not real API conversations). */
+export const MOCK_SIDEBAR_CHAT_ID_PREFIX = "__maple_dev_mock_chat__";
+
+export function isMockSidebarChatId(id: string): boolean {
+ return id.startsWith(MOCK_SIDEBAR_CHAT_ID_PREFIX);
+}
+
+/**
+ * In dev, prepends fake “recent” chats so the sidebar can be reviewed with a full list.
+ * Set `VITE_MOCK_SIDEBAR_CHAT_COUNT=0` in `frontend/.env.local` to turn off.
+ */
+export function getMockSidebarChatCount(): number {
+ if (!import.meta.env.DEV) return 0;
+ const raw = import.meta.env.VITE_MOCK_SIDEBAR_CHAT_COUNT;
+ if (raw === "0" || raw === "false") return 0;
+ if (raw !== undefined && raw !== "") {
+ const n = parseInt(raw, 10);
+ if (Number.isFinite(n)) return Math.min(Math.max(0, n), 100);
+ }
+ return 32;
+}
+
+const SUBJECT_ROTATION = [
+ "Notes on API pagination",
+ "Draft email to the team",
+ "Rust error E0382 walkthrough",
+ "Trip itinerary ideas",
+ "Recipe tweaks (sourdough)",
+ "Meeting summary — Q2 planning",
+ "Regex for log parsing",
+ "Tailwind spacing audit",
+ "SQLite migration checklist",
+ "Design feedback: sidebar density",
+ "Tauri IPC sketch",
+ "Billing edge cases",
+ "iOS keyboard avoidance",
+ "Dark mode token review",
+ "Copy for empty states",
+ "Performance: list virtualization",
+ "Accessibility: focus rings",
+ "Error copy for rate limits",
+ "Onboarding checklist",
+ "Webhook retry policy"
+];
+
+export function buildMockSidebarConversations(count: number): Conversation[] {
+ if (count <= 0) return [];
+ const nowSec = Math.floor(Date.now() / 1000);
+ return Array.from({ length: count }, (_, i) => {
+ const n = i + 1;
+ const subject = SUBJECT_ROTATION[i % SUBJECT_ROTATION.length];
+ const pass = Math.floor(i / SUBJECT_ROTATION.length);
+ const title = pass === 0 ? subject : `${subject} (${pass + 1})`;
+ return {
+ id: `${MOCK_SIDEBAR_CHAT_ID_PREFIX}${n}`,
+ object: "conversation",
+ created_at: nowSec - n * 3600,
+ metadata: { title },
+ project_id: null,
+ pinned: false,
+ last_activity_at: nowSec - n * 90
+ };
+ });
+}
diff --git a/frontend/src/utils/paginatedLists.ts b/frontend/src/utils/paginatedLists.ts
index 8bcd328c..0fbd2b76 100644
--- a/frontend/src/utils/paginatedLists.ts
+++ b/frontend/src/utils/paginatedLists.ts
@@ -1,14 +1,14 @@
-import type {
- Conversation,
- ConversationProjectListItem,
- ConversationsListParams,
- OpenSecretContextType
+import {
+ listConversationProjects,
+ type Conversation,
+ type ConversationProjectListItem,
+ type ConversationsListParams,
+ type OpenSecretContextType
} from "@opensecret/react";
const SIDEBAR_PAGE_SIZE = 20;
type ConversationsClient = Pick;
-type ConversationProjectsClient = Pick;
export async function listAllConversations(
client: ConversationsClient,
@@ -40,15 +40,13 @@ export async function listAllConversations(
}
}
-export async function listAllConversationProjects(
- client: ConversationProjectsClient
-): Promise {
+export async function listAllConversationProjects(): Promise {
const projects: ConversationProjectListItem[] = [];
const seenProjectIds = new Set();
let after: string | undefined;
while (true) {
- const response = await client.listConversationProjects({
+ const response = await listConversationProjects({
limit: SIDEBAR_PAGE_SIZE,
...(after ? { after } : {})
});
diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js
index 62cafc40..87e29222 100644
--- a/frontend/tailwind.config.js
+++ b/frontend/tailwind.config.js
@@ -18,6 +18,7 @@ module.exports = {
},
extend: {
fontFamily: {
+ sans: ["var(--app-font-family)"],
display: ["Array", "sans-serif"],
displayWide: ["Array Wide", "Array", "sans-serif"],
mondwest: ["Mondwest", "sans-serif"],