Files
tolaria/src/mock-tauri.ts
lucaronin bca6b3c8e8 docs: AI chat architecture + fix pre-existing build errors
Update docs/ARCHITECTURE.md with:
- AI Chat system architecture (context picker, streaming, MCP)
- WebSocket bridge protocol
- New files listing and data flow diagrams
- Updated tech stack table

Update mock-tauri.ts with richer AI chat mock responses
(context-aware summarize, expand, grammar responses).

Fix pre-existing TypeScript build errors:
- Add missing snippet/relationships to test VaultEntry fixtures
- Fix string|null → string type mismatches in Sidebar, NoteList, Inspector
- Remove unused imports (cn in Editor, AIChatPanel in App)
- Fix tryParseMarkdownToBlocks Promise cast

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 23:07:10 +01:00

912 lines
27 KiB
TypeScript

/**
* Mock Tauri invoke for browser testing.
* When running outside Tauri (e.g. in Chrome via localhost:5173),
* this provides realistic test data so the UI can be verified visually.
*/
import type { VaultEntry, GitCommit, ModifiedFile } from './types'
// --- Vault API detection (for reading real files in browser dev mode) ---
let vaultApiAvailable: boolean | null = null
async function checkVaultApi(): Promise<boolean> {
if (vaultApiAvailable !== null) return vaultApiAvailable
try {
const res = await fetch('/api/vault/ping', { signal: AbortSignal.timeout(500) })
vaultApiAvailable = res.ok
} catch {
vaultApiAvailable = false
}
console.info(`[mock-tauri] Vault API available: ${vaultApiAvailable}`)
return vaultApiAvailable
}
const MOCK_CONTENT: Record<string, string> = {
'/Users/luca/Laputa/project/26q1-laputa-app.md': `---
title: Build Laputa App
is_a: Project
status: Active
owner: Luca Rossi
belongs_to:
- "[[quarter/q1-2026]]"
related_to:
- "[[topic/software-development]]"
---
# Build Laputa App
## Text Formatting
This paragraph has **bold text**, *italic text*, ***bold italic***, ~~strikethrough~~, and \`inline code\`. Here's a [regular link](https://example.com) and a wiki-link to [[Matteo Cellini]].
## Headings
### Third Level Heading
Content under H3.
#### Fourth Level Heading
Content under H4.
## Lists
### Bullet Lists (Nested)
- First level item — this is a top-level bullet point
- Second level item — indented one level
- Third level item — indented two levels
- Another third level item with longer text that wraps to multiple lines to test alignment
- Back to second level
- Another first level item
- With a nested child
- Final first level item
### Numbered Lists
1. Step one — do this first
2. Step two — then do this
3. Step three — finally this
1. Sub-step 3a
2. Sub-step 3b
### Checkboxes
- [x] Completed task with strikethrough
- [x] Another done item
- [ ] Pending task — needs attention
- [ ] Future task with **bold** text inside
### Mixed Nesting
- Top level bullet
- Nested bullet
- Deep nested bullet
- Back to second
- Another top level
- With child
## Block Quotes
> This is a blockquote. It should have a left border and distinct styling.
> It can span multiple lines and contain **formatting**.
## Code Blocks
\`\`\`typescript
interface VaultEntry {
path: string;
title: string;
isA: string;
status: string | null;
}
function loadVault(path: string): VaultEntry[] {
// Load all markdown files from the vault
return entries.filter(e => e.isA !== 'Note');
}
\`\`\`
\`\`\`yaml
title: Some Title
is_a: Project
status: Active
\`\`\`
## Tables
| Feature | Status | Priority |
|---------|--------|----------|
| Editor | Done | High |
| Inspector | Done | High |
| Git Integration | Done | Medium |
| Mobile App | Planned | Low |
## Horizontal Rule
---
## Wiki-Links
See [[Stock Screener — EMA200 Wick Bounce]] for the experiment approach.
Contact [[Matteo Cellini]] for sponsorship data.
Link to [[Grow Newsletter]] responsibility.
## Paragraphs & Spacing
This is a normal paragraph with enough text to test line wrapping and spacing between elements. The paragraph should have comfortable line height and spacing from the heading above.
And this is a second paragraph to verify inter-paragraph spacing is correct. Good typography requires consistent vertical rhythm throughout the document.
`,
'/Users/luca/Laputa/responsibility/grow-newsletter.md': `---
title: Grow Newsletter
is_a: Responsibility
status: Active
owner: Luca Rossi
---
# Grow Newsletter
## Purpose
Build a sustainable audience through high-quality weekly essays on **engineering leadership**, **AI**, and **personal systems**.
## Key Metrics
- Subscriber count (target: 100k by Q2 2026)
- Open rate (target: > 50%)
- Click-through rate
## Current Strategy
1. Publish one essay per week — Tuesday morning
2. Promote via Twitter/X threads
3. Cross-post to LinkedIn with native formatting
4. Guest posts on other newsletters monthly
## Procedures
- [[Write Weekly Essays]] — the core writing workflow
- Monthly audience analysis and topic planning
## Notes
The newsletter is the *engine* that drives everything else — sponsorships, consulting leads, and brand building.
`,
'/Users/luca/Laputa/responsibility/manage-sponsorships.md': `---
title: Manage Sponsorships
is_a: Responsibility
status: Active
owner: Matteo Cellini
---
# Manage Sponsorships
## Overview
Revenue stream from newsletter sponsorships. [[Matteo Cellini]] handles day-to-day operations.
## Process
1. Inbound leads via sponsorship page
2. Qualification call
3. Proposal and negotiation
4. Schedule and deliver
5. Report results to sponsor
## Metrics
- Monthly revenue
- Close rate
- Repeat sponsor rate
`,
'/Users/luca/Laputa/procedure/write-weekly-essays.md': `---
title: Write Weekly Essays
is_a: Procedure
status: Active
owner: Luca Rossi
cadence: Weekly
belongs_to:
- "[[responsibility/grow-newsletter]]"
---
# Write Weekly Essays
## Schedule
- **Monday**: Pick topic, outline
- **Tuesday**: First draft
- **Wednesday**: Edit and polish
- **Thursday**: Schedule for Tuesday send
## Writing Guidelines
- 1500-2500 words
- One clear takeaway
- Use *real examples* from personal experience
- Include actionable advice, not just theory
### Checklist
- [ ] Pick a topic from the backlog
- [ ] Write outline with 3-5 sections
- [x] Set up newsletter template
- [x] Configure email scheduling
- [ ] Review analytics from last issue
### Nested Topics
- Content strategy for growing the newsletter audience through organic channels, referrals, and high-quality evergreen content that people want to share with their engineering teams
- Newsletter growth and subscriber acquisition including all the different channels we use to attract new readers to the publication
- Organic subscribers from search, Twitter, and word of mouth — these are the highest quality subscribers with the best retention rates over time
- Paid acquisition through Facebook ads and newsletter cross-promotions with other engineering publications in the space
- Social media cross-posting
- Technical writing
- Code examples
- Architecture diagrams
1. First ordered item with a really long description that should definitely wrap to the next line when displayed in the editor, testing the hanging indent behavior for numbered lists
2. Second ordered item — shorter
1. Nested ordered item that also has quite a long description to verify that the indentation works correctly for nested numbered lists too
`,
'/Users/luca/Laputa/procedure/run-sponsorships.md': `---
title: Run Sponsorships
is_a: Procedure
status: Active
owner: Matteo Cellini
cadence: Weekly
belongs_to:
- "[[responsibility/manage-sponsorships]]"
---
# Run Sponsorships
## Weekly Tasks
- Review pipeline in CRM
- Follow up with pending proposals
- Schedule confirmed sponsors
- Send performance reports to completed sponsors
## Templates
- Proposal template: \`/templates/sponsorship-proposal.md\`
- Report template: \`/templates/sponsorship-report.md\`
`,
'/Users/luca/Laputa/experiment/stock-screener.md': `---
title: Stock Screener — EMA200 Wick Bounce
is_a: Experiment
status: Active
owner: Luca Rossi
related_to:
- "[[topic/trading]]"
- "[[topic/algorithmic-trading]]"
---
# Stock Screener — EMA200 Wick Bounce
## Hypothesis
Stocks that wick below the 200-day EMA and close above it show a **statistically significant bounce** in the following 5-10 days.
## Setup
- Scan for daily candles where:
- Low < EMA200
- Close > EMA200
- Volume > 1.5x average
- Filter for mid-cap stocks ($2B-$20B)
## Results So Far
| Date | Ticker | Entry | Exit | Return |
|------|--------|-------|------|--------|
| 2026-01-15 | AAPL | 182.30 | 189.50 | +3.9% |
| 2026-01-22 | MSFT | 410.20 | 418.80 | +2.1% |
## Next Steps
- [ ] Backtest on 10 years of data
- [ ] Add RSI filter for oversold confirmation
- [ ] Build automated alerts via Python script
`,
'/Users/luca/Laputa/note/facebook-ads-strategy.md': `---
title: Facebook Ads Strategy
is_a: Note
belongs_to:
- "[[project/26q1-laputa-app]]"
related_to:
- "[[topic/growth]]"
- "[[topic/ads]]"
---
# Facebook Ads Strategy
## Key Learnings
- **Lookalike audiences** from newsletter subscribers convert 3x better than interest-based targeting
- Video ads outperform static images by 40% on engagement
- Best performing CTA: "Join 50,000 engineers" (social proof)
## Budget
- Monthly budget: $2,000
- Cost per subscriber: ~$1.50 (down from $3.20 in Q3 2025)
## A/B Tests Running
1. Long-form vs short-form ad copy
2. Testimonial vs data-driven creative
`,
'/Users/luca/Laputa/note/budget-allocation.md': `---
title: Budget Allocation
is_a: Note
belongs_to:
- "[[project/26q1-laputa-app]]"
---
# Budget Allocation
## Q1 2026
| Category | Budget | Actual | Delta |
|----------|--------|--------|-------|
| Ads | $6,000 | $5,400 | -$600 |
| Tools | $500 | $480 | -$20 |
| Freelancers | $2,000 | $1,800 | -$200 |
## Notes
- Under budget on ads due to improved targeting efficiency
- Consider reallocating savings to content production
`,
'/Users/luca/Laputa/person/matteo-cellini.md': `---
title: Matteo Cellini
is_a: Person
aliases:
- Matteo
---
# Matteo Cellini
## Role
Sponsorship manager — handles all sponsor relationships, proposals, and reporting.
## Contact
- Email: matteo@example.com
- Slack: @matteo
## Responsibilities
- [[Manage Sponsorships]]
- [[Run Sponsorships]]
`,
'/Users/luca/Laputa/event/2026-02-14-laputa-app-kickoff.md': `---
title: Laputa App Design Session
is_a: Event
related_to:
- "[[project/26q1-laputa-app]]"
- "[[person/matteo-cellini]]"
---
# Laputa App Design Session
## Date
2026-02-14
## Attendees
- Luca Rossi
- [[Matteo Cellini]]
## Notes
- Agreed on four-panel layout inspired by Bear Notes
- CodeMirror 6 for the editor — live preview is critical
- MVP by end of Q1: sidebar + note list + editor working
- Inspector panel can wait for M4
## Action Items
- [ ] Luca: finalize ontology mapping
- [x] Luca: set up Tauri v2 project scaffold
- [ ] Matteo: test with real vault data
`,
'/Users/luca/Laputa/topic/software-development.md': `---
title: Software Development
is_a: Topic
aliases:
- Dev
- Coding
---
# Software Development
A broad topic covering everything from frontend to systems programming.
## Subtopics of Interest
- **Frontend**: React, TypeScript, CSS
- **Desktop**: Tauri, Electron alternatives
- **AI/ML**: LLMs, agents, code generation
- **Systems**: Rust, performance optimization
`,
'/Users/luca/Laputa/topic/trading.md': `---
title: Trading
is_a: Topic
aliases:
- Algorithmic Trading
---
# Trading
## Focus Areas
- Technical analysis (EMA, RSI, volume patterns)
- Algorithmic screening and alerts
- Risk management and position sizing
## Active Experiments
- [[Stock Screener — EMA200 Wick Bounce]]
`,
'/Users/luca/Laputa/essay/on-writing-well.md': `---
title: On Writing Well
is_a: Essay
Belongs to:
- "[[responsibility/grow-newsletter]]"
---
# On Writing Well
Good writing is lean and confident. Every sentence should serve a purpose.
`,
'/Users/luca/Laputa/essay/engineering-leadership-101.md': `---
title: Engineering Leadership 101
is_a: Essay
Belongs to:
- "[[responsibility/grow-newsletter]]"
Related to:
- "[[topic/software-development]]"
---
# Engineering Leadership 101
The transition from IC to manager is the hardest career shift in engineering.
`,
'/Users/luca/Laputa/essay/ai-agents-primer.md': `---
title: AI Agents Primer
is_a: Essay
Belongs to:
- "[[responsibility/grow-newsletter]]"
---
# AI Agents Primer
AI agents are autonomous systems that can plan, execute, and adapt to achieve goals.
`,
}
const MOCK_ENTRIES: VaultEntry[] = [
{
path: '/Users/luca/Laputa/project/26q1-laputa-app.md',
filename: '26q1-laputa-app.md',
title: 'Build Laputa App',
isA: 'Project',
aliases: ['Laputa App'],
belongsTo: ['[[quarter/q1-2026]]'],
relatedTo: ['[[topic/software-development]]'],
status: 'Active',
owner: 'Luca Rossi',
cadence: null,
modifiedAt: Date.now() / 1000,
createdAt: null,
fileSize: 2048,
snippet: 'This paragraph has bold text, italic text, bold italic, strikethrough, and inline code. Here\'s a regular link and a wiki-link to Matteo Cellini.',
relationships: {
'Belongs to': ['[[quarter/q1-2026]]'],
'Related to': ['[[topic/software-development]]'],
},
},
{
path: '/Users/luca/Laputa/responsibility/grow-newsletter.md',
filename: 'grow-newsletter.md',
title: 'Grow Newsletter',
isA: 'Responsibility',
aliases: [],
belongsTo: [],
relatedTo: ['[[topic/growth]]'],
status: 'Active',
owner: 'Luca Rossi',
cadence: null,
modifiedAt: Date.now() / 1000 - 3600,
createdAt: null,
fileSize: 1024,
snippet: 'Build a sustainable audience through high-quality weekly essays on engineering leadership, AI, and personal systems.',
relationships: {
'Has': [
'[[essay/on-writing-well|On Writing Well]]',
'[[essay/engineering-leadership-101|Engineering Leadership 101]]',
'[[essay/ai-agents-primer|AI Agents Primer]]',
],
'Topics': ['[[topic/growth]]', '[[topic/writing]]'],
'Related to': ['[[topic/growth]]'],
},
},
{
path: '/Users/luca/Laputa/responsibility/manage-sponsorships.md',
filename: 'manage-sponsorships.md',
title: 'Manage Sponsorships',
isA: 'Responsibility',
aliases: [],
belongsTo: [],
relatedTo: [],
status: 'Active',
owner: 'Matteo Cellini',
cadence: null,
modifiedAt: Date.now() / 1000 - 7200,
createdAt: null,
fileSize: 890,
snippet: 'Revenue stream from newsletter sponsorships. Matteo Cellini handles day-to-day operations.',
relationships: {
'Owner': ['[[person/matteo-cellini|Matteo Cellini]]'],
},
},
{
path: '/Users/luca/Laputa/procedure/write-weekly-essays.md',
filename: 'write-weekly-essays.md',
title: 'Write Weekly Essays',
isA: 'Procedure',
aliases: [],
belongsTo: ['[[responsibility/grow-newsletter]]'],
relatedTo: [],
status: 'Active',
owner: 'Luca Rossi',
cadence: 'Weekly',
modifiedAt: Date.now() / 1000 - 86400,
createdAt: null,
fileSize: 512,
snippet: 'Monday: Pick topic, outline Tuesday: First draft Wednesday: Edit and polish Thursday: Schedule for Tuesday send',
relationships: {
'Belongs to': ['[[responsibility/grow-newsletter]]'],
},
},
{
path: '/Users/luca/Laputa/procedure/run-sponsorships.md',
filename: 'run-sponsorships.md',
title: 'Run Sponsorships',
isA: 'Procedure',
aliases: [],
belongsTo: ['[[responsibility/manage-sponsorships]]'],
relatedTo: [],
status: 'Active',
owner: 'Matteo Cellini',
cadence: 'Weekly',
modifiedAt: Date.now() / 1000 - 86400 * 2,
createdAt: null,
fileSize: 640,
snippet: 'Review pipeline in CRM Follow up with pending proposals Schedule confirmed sponsors Send performance reports to completed sponsors',
relationships: {
'Belongs to': ['[[responsibility/manage-sponsorships]]'],
},
},
{
path: '/Users/luca/Laputa/experiment/stock-screener.md',
filename: 'stock-screener.md',
title: 'Stock Screener — EMA200 Wick Bounce',
isA: 'Experiment',
aliases: ['Trading Screener'],
belongsTo: [],
relatedTo: ['[[topic/trading]]', '[[topic/algorithmic-trading]]'],
status: 'Active',
owner: 'Luca Rossi',
cadence: null,
modifiedAt: Date.now() / 1000 - 86400,
createdAt: null,
fileSize: 3200,
snippet: 'Stocks that wick below the 200-day EMA and close above it show a statistically significant bounce in the following 5-10 days.',
relationships: {
'Related to': ['[[topic/trading]]', '[[topic/algorithmic-trading]]'],
'Has Data': ['[[data/ema200-backtest-results]]'],
},
},
{
path: '/Users/luca/Laputa/note/facebook-ads-strategy.md',
filename: 'facebook-ads-strategy.md',
title: 'Facebook Ads Strategy',
isA: 'Note',
aliases: [],
belongsTo: ['[[project/26q1-laputa-app]]'],
relatedTo: ['[[topic/growth]]', '[[topic/ads]]'],
status: null,
owner: null,
cadence: null,
modifiedAt: Date.now() / 1000 - 3600 * 5,
createdAt: null,
fileSize: 847,
snippet: 'Lookalike audiences from newsletter subscribers convert 3x better than interest-based targeting Video ads outperform static images by 40% on engagement',
relationships: {
'Belongs to': ['[[project/26q1-laputa-app]]'],
'Related to': ['[[topic/growth]]', '[[topic/ads]]'],
},
},
{
path: '/Users/luca/Laputa/note/budget-allocation.md',
filename: 'budget-allocation.md',
title: 'Budget Allocation',
isA: 'Note',
aliases: [],
belongsTo: ['[[project/26q1-laputa-app]]'],
relatedTo: [],
status: null,
owner: null,
cadence: null,
modifiedAt: Date.now() / 1000 - 86400,
createdAt: null,
fileSize: 560,
snippet: 'Under budget on ads due to improved targeting efficiency Consider reallocating savings to content production',
relationships: {
'Belongs to': ['[[project/26q1-laputa-app]]'],
},
},
{
path: '/Users/luca/Laputa/person/matteo-cellini.md',
filename: 'matteo-cellini.md',
title: 'Matteo Cellini',
isA: 'Person',
aliases: ['Matteo'],
belongsTo: [],
relatedTo: [],
status: null,
owner: null,
cadence: null,
modifiedAt: Date.now() / 1000 - 86400 * 7,
createdAt: null,
fileSize: 320,
snippet: 'Sponsorship manager — handles all sponsor relationships, proposals, and reporting.',
relationships: {},
},
{
path: '/Users/luca/Laputa/event/2026-02-14-laputa-app-kickoff.md',
filename: '2026-02-14-laputa-app-kickoff.md',
title: 'Laputa App Design Session',
isA: 'Event',
aliases: [],
belongsTo: [],
relatedTo: ['[[project/26q1-laputa-app]]', '[[person/matteo-cellini]]'],
status: null,
owner: null,
cadence: null,
modifiedAt: Date.now() / 1000 - 3600 * 2,
createdAt: null,
fileSize: 1200,
snippet: 'Agreed on four-panel layout inspired by Bear Notes CodeMirror 6 for the editor — live preview is critical MVP by end of Q1.',
relationships: {
'Related to': ['[[project/26q1-laputa-app]]', '[[person/matteo-cellini]]'],
},
},
{
path: '/Users/luca/Laputa/topic/software-development.md',
filename: 'software-development.md',
title: 'Software Development',
isA: 'Topic',
aliases: ['Dev', 'Coding'],
belongsTo: [],
relatedTo: [],
status: null,
owner: null,
cadence: null,
modifiedAt: Date.now() / 1000 - 86400 * 30,
createdAt: null,
fileSize: 256,
snippet: 'A broad topic covering everything from frontend to systems programming.',
relationships: {
'Notes': ['[[note/facebook-ads-strategy]]', '[[note/budget-allocation]]'],
},
},
{
path: '/Users/luca/Laputa/topic/trading.md',
filename: 'trading.md',
title: 'Trading',
isA: 'Topic',
aliases: ['Algorithmic Trading'],
belongsTo: [],
relatedTo: [],
status: null,
owner: null,
cadence: null,
modifiedAt: Date.now() / 1000 - 86400 * 14,
createdAt: null,
fileSize: 180,
snippet: 'Technical analysis (EMA, RSI, volume patterns) Algorithmic screening and alerts Risk management and position sizing',
relationships: {
'Notes': ['[[experiment/stock-screener]]'],
},
},
{
path: '/Users/luca/Laputa/essay/on-writing-well.md',
filename: 'on-writing-well.md',
title: 'On Writing Well',
isA: 'Essay',
aliases: [],
belongsTo: ['[[responsibility/grow-newsletter]]'],
relatedTo: [],
status: null,
owner: null,
cadence: null,
modifiedAt: Date.now() / 1000 - 86400 * 3,
createdAt: null,
fileSize: 4200,
snippet: 'Good writing is lean and confident. Every sentence should serve a purpose.',
relationships: {
'Belongs to': ['[[responsibility/grow-newsletter]]'],
},
},
{
path: '/Users/luca/Laputa/essay/engineering-leadership-101.md',
filename: 'engineering-leadership-101.md',
title: 'Engineering Leadership 101',
isA: 'Essay',
aliases: [],
belongsTo: ['[[responsibility/grow-newsletter]]'],
relatedTo: ['[[topic/software-development]]'],
status: null,
owner: null,
cadence: null,
modifiedAt: Date.now() / 1000 - 86400 * 7,
createdAt: null,
fileSize: 3800,
snippet: 'The transition from IC to manager is the hardest career shift in engineering.',
relationships: {
'Belongs to': ['[[responsibility/grow-newsletter]]'],
'Related to': ['[[topic/software-development]]'],
},
},
{
path: '/Users/luca/Laputa/essay/ai-agents-primer.md',
filename: 'ai-agents-primer.md',
title: 'AI Agents Primer',
isA: 'Essay',
aliases: [],
belongsTo: ['[[responsibility/grow-newsletter]]'],
relatedTo: [],
status: null,
owner: null,
cadence: null,
modifiedAt: Date.now() / 1000 - 86400 * 10,
createdAt: null,
fileSize: 5100,
snippet: 'AI agents are autonomous systems that can plan, execute, and adapt to achieve goals.',
relationships: {
'Belongs to': ['[[responsibility/grow-newsletter]]'],
},
},
]
function mockFileHistory(path: string): GitCommit[] {
const filename = path.split('/').pop()?.replace('.md', '') ?? 'unknown'
const now = Math.floor(Date.now() / 1000)
return [
{
hash: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0',
shortHash: 'a1b2c3d',
message: `Update ${filename} with latest changes`,
author: 'Luca Rossi',
date: now - 86400 * 2,
},
{
hash: 'e4f5g6h7i8j9k0l1m2n3o4p5q6r7s8t9u0v1w2x3',
shortHash: 'e4f5g6h',
message: `Add new section to ${filename}`,
author: 'Luca Rossi',
date: now - 86400 * 5,
},
{
hash: 'i7j8k9l0m1n2o3p4q5r6s7t8u9v0w1x2y3z4a5b6',
shortHash: 'i7j8k9l',
message: `Fix formatting in ${filename}`,
author: 'Luca Rossi',
date: now - 86400 * 12,
},
{
hash: 'm0n1o2p3q4r5s6t7u8v9w0x1y2z3a4b5c6d7e8f9',
shortHash: 'm0n1o2p',
message: `Create ${filename}`,
author: 'Luca Rossi',
date: now - 86400 * 30,
},
]
}
function mockModifiedFiles(): ModifiedFile[] {
return [
{
path: '/Users/luca/Laputa/project/26q1-laputa-app.md',
relativePath: 'project/26q1-laputa-app.md',
status: 'modified',
},
{
path: '/Users/luca/Laputa/note/facebook-ads-strategy.md',
relativePath: 'note/facebook-ads-strategy.md',
status: 'modified',
},
{
path: '/Users/luca/Laputa/note/new-draft.md',
relativePath: 'note/new-draft.md',
status: 'untracked',
},
]
}
function mockFileDiff(path: string): string {
const filename = path.split('/').pop() ?? 'unknown'
return `diff --git a/${filename} b/${filename}
index abc1234..def5678 100644
--- a/${filename}
+++ b/${filename}
@@ -1,8 +1,10 @@
---
title: Example Note
is_a: Note
+status: Active
---
# Example Note
-This is the original content.
+This is the updated content.
+
+A new paragraph has been added.`
}
let mockHasChanges = true
const mockHandlers: Record<string, (args: any) => any> = {
list_vault: () => MOCK_ENTRIES,
get_note_content: (args: { path: string }) => MOCK_CONTENT[args.path] ?? '',
get_all_content: () => MOCK_CONTENT,
get_file_history: (args: { path: string }) => mockFileHistory(args.path),
get_modified_files: () => mockHasChanges ? mockModifiedFiles() : [],
get_file_diff: (args: { path: string }) => mockFileDiff(args.path),
git_commit: (args: { message: string }) => {
mockHasChanges = false
return `[main abc1234] ${args.message}\n 3 files changed`
},
git_push: () => {
return 'Everything up-to-date'
},
ai_chat: (args: { request: { messages: any[]; model?: string; system?: string } }) => {
const lastMsg = args.request.messages[args.request.messages.length - 1]?.content ?? ''
const lower = lastMsg.toLowerCase()
let content = `I can help you with that. Could you provide more details about what you'd like to know?`
if (lower.includes('summarize')) {
content = `Here's a summary of the note:\n\n**Key Points:**\n- The note covers the main topic and its related concepts\n- It includes actionable items and references to other notes\n- Several wiki-links connect it to the broader knowledge base\n\nWould you like me to expand on any of these points?`
} else if (lower.includes('expand')) {
content = `Here are suggestions to expand this note:\n\n1. **Add context** — Include background information\n2. **Link related notes** — Connect to [[related topics]]\n3. **Add examples** — Include concrete examples\n4. **Update status** — Reflect current progress`
} else if (lower.includes('grammar')) {
content = `Grammar review complete. The writing is clear and well-structured. Minor suggestions:\n\n- Consider varying sentence lengths for better rhythm\n- A few passive constructions could be made active`
}
return {
content,
model: args.request.model ?? 'claude-3-5-haiku-20241022',
stop_reason: 'end_turn',
}
},
}
export function isTauri(): boolean {
return typeof window !== 'undefined' && ('__TAURI__' in window || '__TAURI_INTERNALS__' in window)
}
// Initialize window.__mockContent for browser testing
if (typeof window !== 'undefined') {
window.__mockContent = MOCK_CONTENT
}
/** Register content for a new entry in mock mode (for get_note_content calls) */
export function addMockEntry(_entry: VaultEntry, content: string) {
MOCK_CONTENT[_entry.path] = content
if (typeof window !== 'undefined') {
window.__mockContent = MOCK_CONTENT
}
}
/** Update content for an existing entry in mock mode */
export function updateMockContent(path: string, content: string) {
MOCK_CONTENT[path] = content
if (typeof window !== 'undefined') {
window.__mockContent = MOCK_CONTENT
}
}
export async function mockInvoke<T>(cmd: string, args?: any): Promise<T> {
// Try the vault API first for commands that read vault data
const apiAvailable = await checkVaultApi()
if (apiAvailable) {
try {
if (cmd === 'list_vault' && args?.path) {
const res = await fetch(`/api/vault/list?path=${encodeURIComponent(args.path)}`)
if (res.ok) return (await res.json()) as T
}
if (cmd === 'get_note_content' && args?.path) {
const res = await fetch(`/api/vault/content?path=${encodeURIComponent(args.path)}`)
if (res.ok) {
const { content } = await res.json()
return content as T
}
}
if (cmd === 'get_all_content' && args?.path) {
const res = await fetch(`/api/vault/all-content?path=${encodeURIComponent(args.path)}`)
if (res.ok) return (await res.json()) as T
}
} catch (err) {
console.warn(`[mock-tauri] Vault API call failed for ${cmd}, falling back to mock:`, err)
}
}
// Fall back to hardcoded mock handlers
const handler = mockHandlers[cmd]
if (handler) {
await new Promise((r) => setTimeout(r, 100))
return handler(args) as T
}
throw new Error(`No mock handler for command: ${cmd}`)
}