feat: add iPad/iOS prototype via Tauri v2 mobile target

Initialize Tauri iOS support with conditional compilation:
- Desktop-only features (git CLI, menu, MCP, Claude CLI) gated behind #[cfg(desktop)]
- Mobile stubs return graceful errors so frontend degrades smoothly
- Vault read/write, AI chat, search, settings work unchanged on both platforms
- Xcode project generated, Rust cross-compiles cleanly to aarch64-apple-ios-sim
- All 581 Rust tests and 2201 frontend tests still pass

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Test
2026-03-27 17:18:34 +01:00
parent 41edd75837
commit 5464da9c6e
42 changed files with 37050 additions and 24 deletions

View File

@@ -898,3 +898,36 @@ const enabled = useFeatureFlag('example_flag') // boolean
- `localStorage` overrides allow dev/QA testing without rebuilding
- Type-safe flag names via TypeScript union type
- API surface is compatible with future migration to remote flags
## Platform Support — iOS / iPadOS (Prototype)
Tauri v2 supports iOS as a beta target. The Rust backend cross-compiles to `aarch64-apple-ios-sim` (simulator) and `aarch64-apple-ios` (device) with zero code changes to vault/frontmatter/search logic.
**Conditional compilation strategy:**
```
#[cfg(desktop)] — git CLI, menu bar, MCP server, Claude CLI, updater
#[cfg(mobile)] — stub commands returning graceful errors or empty results
```
Desktop-only modules gated at the crate level:
- `pub mod menu` — macOS menu bar (entire module)
Desktop-only features gated at the function level in `commands.rs`:
- Git operations (commit, pull, push, status, history, diff, conflicts)
- GitHub operations (clone, list repos, device flow auth)
- Claude CLI streaming (check, chat, agent)
- MCP registration and status
- Menu state updates
Features that work on both platforms without changes:
- Vault scan, note read/write, rename, delete, trash, archive
- Frontmatter read/write/delete
- AI chat (Anthropic API via `reqwest`)
- Search (pure Rust in-memory)
- Settings persistence
- Vault list management
**Capabilities:** `src-tauri/capabilities/default.json` targets desktop; `mobile.json` targets iOS/Android with a minimal permission set.
**Detailed feasibility report:** `docs/IPAD-PROTOTYPE.md`

111
docs/IPAD-PROTOTYPE.md Normal file
View File

@@ -0,0 +1,111 @@
# iPad Prototype — Tauri v2 iOS Feasibility Report
**Date:** 2026-03-27
**Status:** Prototype — Rust cross-compiles to iOS, Xcode project generated, awaiting simulator runtime
## Summary
Laputa can be ported to iPad using Tauri v2 iOS (beta) with **minimal code changes**. The React frontend stays identical. The Rust backend compiles for iOS with conditional compilation to gate desktop-only features (git CLI, menu bar, MCP, Claude CLI). Vault read/write operations work without changes.
## What Works
| Feature | Status | Notes |
|---------|--------|-------|
| Rust backend cross-compilation | Compiles cleanly for `aarch64-apple-ios-sim` | Zero errors, zero warnings |
| Desktop build (no regressions) | 581 Rust tests pass, 2201 frontend tests pass | Lint and type check clean |
| Tauri iOS project generation | Xcode project generated via `tauri ios init` | CocoaPods, xcodegen, libimobiledevice installed |
| Vault file read/write | Expected to work | Pure filesystem operations, no process spawning |
| AI chat (Anthropic API) | Expected to work | Uses `reqwest` HTTP, no CLI dependency |
| Search | Expected to work | Pure Rust in-memory search |
| Settings persistence | Expected to work | JSON file read/write |
| React UI in WebView | Expected to work | Tauri v2 uses WKWebView on iOS |
## What Doesn't Work (Yet)
| Feature | Blocker | Recommended Solution |
|---------|---------|---------------------|
| Git operations | No `git` binary on iOS | **Option B (Working Copy)** for prototype; **Option A (isomorphic-git)** for production |
| GitHub clone/push/pull | Depends on git CLI | Same as above |
| Claude CLI streaming | No `claude` binary on iOS | Use Anthropic API directly (already available via `ai_chat`) |
| MCP server / WS bridge | Spawns Node.js child process | Skip for mobile; explore in-process MCP later |
| macOS menu bar | Desktop-only API | Touch-native navigation (already handled by React) |
| Updater plugin | Desktop-only | Use TestFlight for updates |
| File open dialog | Different on iOS | `tauri-plugin-dialog` supports iOS — needs testing |
## Changes Made
### `src-tauri/src/lib.rs`
- Gated `WsBridgeChild`, `run_startup_tasks`, `spawn_ws_bridge`, `log_startup_result` behind `#[cfg(desktop)]`
- Mobile build skips MCP registration, WS bridge, and vault migrations at startup
- Run event handler gated for desktop (child process cleanup)
### `src-tauri/src/commands.rs`
- Git commands: desktop implementations remain unchanged; mobile stubs return graceful errors or empty results
- GitHub commands: desktop-only; mobile stubs return errors
- Claude CLI commands: desktop-only; mobile stubs return `installed: false`
- MCP commands: desktop-only; mobile stubs return `NotInstalled`
- Menu commands: desktop-only; mobile stub is a no-op
- Vault, frontmatter, search, AI chat, settings: **unchanged** (work on both platforms)
### `src-tauri/src/lib.rs`
- `pub mod menu` gated behind `#[cfg(desktop)]`
### `src-tauri/capabilities/`
- `default.json`: scoped to desktop platforms (`linux`, `macOS`, `windows`)
- `mobile.json`: new file with iOS/Android permissions (core, dialog)
### `src-tauri/gen/apple/`
- Full Xcode project generated by `tauri ios init`
- iPad support: all orientations enabled, arm64 architecture
## Architecture: How Mobile Stubs Work
```
Frontend (React) ──invoke──> Tauri Commands ──> Rust Backend
┌──────────┴──────────┐
#[cfg(desktop)] #[cfg(mobile)]
│ │
Real impl Stub (error/empty)
(git CLI, (graceful degradation)
menu, MCP)
```
The frontend code doesn't change at all. Commands that aren't available on mobile return errors that the UI can handle gracefully (e.g., hiding the git sync panel, disabling commit buttons).
## Git Strategy for iPad
### Phase 1: Working Copy (Recommended for prototype)
- User manages vault with [Working Copy](https://workingcopy.app/) (git client for iPad)
- Laputa opens the vault via iOS Files API / FileProvider
- Zero git code needed in Laputa — Working Copy handles sync
- **Pro:** Works immediately, robust git implementation
- **Con:** Requires separate app, split UX for sync
### Phase 2: isomorphic-git (Production)
- Pure JS git implementation running in the WebView
- Replace `invoke("git_commit")` etc. with JS-side git operations
- **Pro:** Integrated UX, no external dependency
- **Con:** Slower on large vaults (~9200 files), limited advanced git features
### Phase 3: git2-rs / gitoxide (Future)
- Pure Rust git library compiled into the Tauri binary
- Best performance, no JS bridge overhead
- **Con:** Larger binary size, more integration work
## Next Steps
1. **Install iOS simulator runtime**`xcodebuild -downloadPlatform iOS` (downloading ~7GB)
2. **Full Xcode build**`npx tauri ios build --target aarch64-sim`
3. **Launch on iPad simulator**`npx tauri ios dev`
4. **Test vault read/write** — open a vault, read/edit a note
5. **Test AI chat** — verify Anthropic API calls work from iOS WebView
6. **Evaluate touch UX** — identify layout issues on iPad screen size
7. **File Provider integration** — test opening vaults from Working Copy via iOS Files
## Prerequisites Installed
- Rust iOS targets: `aarch64-apple-ios`, `aarch64-apple-ios-sim`, `x86_64-apple-ios`
- Xcode 16.2 with iOS 18.3.1 simulator runtime (downloading)
- CocoaPods 1.16.2, xcodegen 2.45.3, libimobiledevice 1.4.0
- Tauri CLI 2.10.0 with iOS support

View File

@@ -6,6 +6,7 @@
"main",
"note-*"
],
"platforms": ["linux", "macOS", "windows"],
"permissions": [
"core:default",
"core:window:allow-create",

View File

@@ -0,0 +1,15 @@
{
"$schema": "../gen/schemas/mobile-schema.json",
"identifier": "mobile",
"description": "permissions for iOS/iPadOS",
"windows": [
"main"
],
"platforms": ["iOS", "android"],
"permissions": [
"core:default",
"core:window:allow-close",
"core:window:allow-set-title",
"dialog:default"
]
}

3
src-tauri/gen/apple/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
xcuserdata/
build/
Externals/

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -0,0 +1,116 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "AppIcon-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "AppIcon-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "AppIcon-29x29@2x-1.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "AppIcon-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "AppIcon-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "AppIcon-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "AppIcon-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "AppIcon-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "AppIcon-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "AppIcon-20x20@2x-1.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "AppIcon-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "AppIcon-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "AppIcon-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "AppIcon-40x40@2x-1.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "AppIcon-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "AppIcon-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "AppIcon-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "AppIcon-512@2x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>debugging</string>
</dict>
</plist>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17150" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Y6W-OH-hqX">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17122"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="s0d-6b-0kx">
<objects>
<viewController id="Y6W-OH-hqX" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="5EZ-qb-Rvc">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<viewLayoutGuide key="safeArea" id="vDu-zF-Fre"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Ief-a0-LHa" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
<resources>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>

View File

@@ -0,0 +1,21 @@
# Uncomment the next line to define a global platform for your project
target 'laputa_iOS' do
platform :ios, '14.0'
# Pods for laputa_iOS
end
target 'laputa_macOS' do
platform :osx, '11.0'
# Pods for laputa_macOS
end
# Delete the deployment target for iOS and macOS, causing it to be inherited from the Podfile
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings.delete 'IPHONEOS_DEPLOYMENT_TARGET'
config.build_settings.delete 'MACOSX_DEPLOYMENT_TARGET'
end
end
end

View File

@@ -0,0 +1,8 @@
#pragma once
namespace ffi {
extern "C" {
void start_app();
}
}

View File

@@ -0,0 +1,6 @@
#include "bindings/bindings.h"
int main(int argc, char * argv[]) {
ffi::start_app();
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
{"type":"commonjs"}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,566 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 77;
objects = {
/* Begin PBXBuildFile section */
1662961784944D479C9E1B0D /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E3FC10F8A144BB158574C774 /* Metal.framework */; };
1C55EF711F7ACEAC173C344E /* libapp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CF0D094B920E757356FC3AD /* libapp.a */; };
4A94802D0295ECD102C21BA1 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A34681D9D4CE45CDF83ABA59 /* UIKit.framework */; };
534929E6730B15780117F21F /* MetalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C322F5E5E2ACB0CBC644EE /* MetalKit.framework */; };
5AAD7159BB4D20588044B3BC /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43C29783A8194F596483AD35 /* Security.framework */; };
74A9072960AF0CB774BEB749 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AB6F4BD2E768B4B6EDF7EA4 /* QuartzCore.framework */; };
799F0FF5DD8053B287C9CA46 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3D8616031E45547278A88B60 /* main.mm */; };
7EF9AA3C7F58D96F21743FB4 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27D33799362DF41B226D7635 /* WebKit.framework */; };
9BD440BEBCDBD633C9EE2C26 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A70EB7513BFD74428B2C6ED1 /* CoreGraphics.framework */; };
A2279B6B796C960A2951E7C5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F8E52CF938A81FD9ABC1B23F /* Assets.xcassets */; };
DACF8A23100E74CEA0BE1ED9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9274F38B9AA0A9A44A3CBEAA /* LaunchScreen.storyboard */; };
E0FB400669725FC03B8E9B39 /* assets in Resources */ = {isa = PBXBuildFile; fileRef = 1BC846C6372AE88FE28EA984 /* assets */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
05C322F5E5E2ACB0CBC644EE /* MetalKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalKit.framework; path = System/Library/Frameworks/MetalKit.framework; sourceTree = SDKROOT; };
077AD05C7FB4A784D360399B /* commands.rs */ = {isa = PBXFileReference; path = commands.rs; sourceTree = "<group>"; };
0AE733EAA82134418C0AC89F /* api.rs */ = {isa = PBXFileReference; path = api.rs; sourceTree = "<group>"; };
0CA7012668D8606FE9709D6D /* yaml.rs */ = {isa = PBXFileReference; path = yaml.rs; sourceTree = "<group>"; };
0FB1053AF590466CC8971679 /* parsing.rs */ = {isa = PBXFileReference; path = parsing.rs; sourceTree = "<group>"; };
16CCC72C07657AEBC320B099 /* auth.rs */ = {isa = PBXFileReference; path = auth.rs; sourceTree = "<group>"; };
1BC846C6372AE88FE28EA984 /* assets */ = {isa = PBXFileReference; lastKnownFileType = folder; path = assets; sourceTree = SOURCE_ROOT; };
1D65CB9525835A85C38D3136 /* migration.rs */ = {isa = PBXFileReference; path = migration.rs; sourceTree = "<group>"; };
27D33799362DF41B226D7635 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; };
2D31CB45BB367FD6A7FA0B67 /* rename.rs */ = {isa = PBXFileReference; path = rename.rs; sourceTree = "<group>"; };
2D38A9417EA6B0A133D0D55F /* mod.rs */ = {isa = PBXFileReference; path = mod.rs; sourceTree = "<group>"; };
34F1C40770F83FDC21036505 /* laputa_iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = laputa_iOS.entitlements; sourceTree = "<group>"; };
3AB6F4BD2E768B4B6EDF7EA4 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
3CF0D094B920E757356FC3AD /* libapp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libapp.a; sourceTree = "<group>"; };
3D8616031E45547278A88B60 /* main.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = "<group>"; };
3E23075B718E12551904D3B6 /* frontmatter.rs */ = {isa = PBXFileReference; path = frontmatter.rs; sourceTree = "<group>"; };
43C29783A8194F596483AD35 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
4AAEE16E1AC0E472E9EAF907 /* conflict.rs */ = {isa = PBXFileReference; path = conflict.rs; sourceTree = "<group>"; };
4F4C2FE8D3697D180E1D4304 /* entry.rs */ = {isa = PBXFileReference; path = entry.rs; sourceTree = "<group>"; };
50B53E0FE0FC2A5515B48A28 /* main.rs */ = {isa = PBXFileReference; path = main.rs; sourceTree = "<group>"; };
517A653F5A674050430C1167 /* commit.rs */ = {isa = PBXFileReference; path = commit.rs; sourceTree = "<group>"; };
5800BA1A3E5C03124E02E946 /* title_sync.rs */ = {isa = PBXFileReference; path = title_sync.rs; sourceTree = "<group>"; };
5F28F393EB20BA7F8F2102EF /* config_seed.rs */ = {isa = PBXFileReference; path = config_seed.rs; sourceTree = "<group>"; };
6288FD61B10C36115B12B4A4 /* cache.rs */ = {isa = PBXFileReference; path = cache.rs; sourceTree = "<group>"; };
692CE75FC8E670478C4209C8 /* remote.rs */ = {isa = PBXFileReference; path = remote.rs; sourceTree = "<group>"; };
6A5C3DA38846B440DD56E98A /* claude_cli.rs */ = {isa = PBXFileReference; path = claude_cli.rs; sourceTree = "<group>"; };
6AC39E9370FCCB01E81EC824 /* ai_chat.rs */ = {isa = PBXFileReference; path = ai_chat.rs; sourceTree = "<group>"; };
6F67F5CDD6BB137126C5AABB /* menu.rs */ = {isa = PBXFileReference; path = menu.rs; sourceTree = "<group>"; };
71EDBD38A067DA56FD3E375F /* getting_started.rs */ = {isa = PBXFileReference; path = getting_started.rs; sourceTree = "<group>"; };
7D94F0F22CD503903B63FFE7 /* file.rs */ = {isa = PBXFileReference; path = file.rs; sourceTree = "<group>"; };
7FCDD5B9C3D021A45C402A43 /* mod.rs */ = {isa = PBXFileReference; path = mod.rs; sourceTree = "<group>"; };
81C8EBDD5CAD14B7F366AA54 /* trash.rs */ = {isa = PBXFileReference; path = trash.rs; sourceTree = "<group>"; };
84ECD74B7841E52AD6549346 /* image.rs */ = {isa = PBXFileReference; path = image.rs; sourceTree = "<group>"; };
87F0375DE367A8192708C2D8 /* pulse.rs */ = {isa = PBXFileReference; path = pulse.rs; sourceTree = "<group>"; };
9274F38B9AA0A9A44A3CBEAA /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
94B5B4BBA850E2BCAE3E4CC7 /* mod.rs */ = {isa = PBXFileReference; path = mod.rs; sourceTree = "<group>"; };
A17AA2469F233241B0DF8BE3 /* vault_list.rs */ = {isa = PBXFileReference; path = vault_list.rs; sourceTree = "<group>"; };
A34681D9D4CE45CDF83ABA59 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
A70EB7513BFD74428B2C6ED1 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
AA64014FA2D16950AD4DF619 /* mod.rs */ = {isa = PBXFileReference; path = mod.rs; sourceTree = "<group>"; };
ACCD8318FA6543BA013E37E4 /* ops.rs */ = {isa = PBXFileReference; path = ops.rs; sourceTree = "<group>"; };
BA9FA5D7A7A7574FF74589C8 /* telemetry.rs */ = {isa = PBXFileReference; path = telemetry.rs; sourceTree = "<group>"; };
BB4E3DD2D3BA34BB86C908B6 /* status.rs */ = {isa = PBXFileReference; path = status.rs; sourceTree = "<group>"; };
C39B460DE1A3B78A0937BFDD /* settings.rs */ = {isa = PBXFileReference; path = settings.rs; sourceTree = "<group>"; };
C80E8FDDA822F4F167B601C1 /* mod_tests.rs */ = {isa = PBXFileReference; path = mod_tests.rs; sourceTree = "<group>"; };
CEDEEAE68B0DB4CD43ED89A8 /* laputa_iOS.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = laputa_iOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
D0900BA51E68887E5DA395B7 /* bindings.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = bindings.h; sourceTree = "<group>"; };
DDC76F9C00A29C8C52345249 /* history.rs */ = {isa = PBXFileReference; path = history.rs; sourceTree = "<group>"; };
E1428C0A0CC22F741B7A4A0C /* lib.rs */ = {isa = PBXFileReference; path = lib.rs; sourceTree = "<group>"; };
E36BAA25D76F212157A06395 /* search.rs */ = {isa = PBXFileReference; path = search.rs; sourceTree = "<group>"; };
E3FC10F8A144BB158574C774 /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; };
F87684B058BB66A215F98DCF /* mcp.rs */ = {isa = PBXFileReference; path = mcp.rs; sourceTree = "<group>"; };
F8E52CF938A81FD9ABC1B23F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
F9FF9FD3CEB0EB658CA17305 /* clone.rs */ = {isa = PBXFileReference; path = clone.rs; sourceTree = "<group>"; };
FBB08655AD50E526ACEAE8A2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
50978AC1976616D3342459ED /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
1C55EF711F7ACEAC173C344E /* libapp.a in Frameworks */,
9BD440BEBCDBD633C9EE2C26 /* CoreGraphics.framework in Frameworks */,
1662961784944D479C9E1B0D /* Metal.framework in Frameworks */,
534929E6730B15780117F21F /* MetalKit.framework in Frameworks */,
74A9072960AF0CB774BEB749 /* QuartzCore.framework in Frameworks */,
5AAD7159BB4D20588044B3BC /* Security.framework in Frameworks */,
4A94802D0295ECD102C21BA1 /* UIKit.framework in Frameworks */,
7EF9AA3C7F58D96F21743FB4 /* WebKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
021259EC384B5D897634CFF8 /* Products */ = {
isa = PBXGroup;
children = (
CEDEEAE68B0DB4CD43ED89A8 /* laputa_iOS.app */,
);
name = Products;
sourceTree = "<group>";
};
109126B51C7DDFCA7CD8B9EA /* Sources */ = {
isa = PBXGroup;
children = (
DC6461BBBF0BDFBB28352CF9 /* laputa */,
);
path = Sources;
sourceTree = "<group>";
};
1189728653A76416E01221D7 /* Frameworks */ = {
isa = PBXGroup;
children = (
A70EB7513BFD74428B2C6ED1 /* CoreGraphics.framework */,
3CF0D094B920E757356FC3AD /* libapp.a */,
E3FC10F8A144BB158574C774 /* Metal.framework */,
05C322F5E5E2ACB0CBC644EE /* MetalKit.framework */,
3AB6F4BD2E768B4B6EDF7EA4 /* QuartzCore.framework */,
43C29783A8194F596483AD35 /* Security.framework */,
A34681D9D4CE45CDF83ABA59 /* UIKit.framework */,
27D33799362DF41B226D7635 /* WebKit.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
314C2D9104FDC1DE81AC36AD = {
isa = PBXGroup;
children = (
1BC846C6372AE88FE28EA984 /* assets */,
F8E52CF938A81FD9ABC1B23F /* Assets.xcassets */,
9274F38B9AA0A9A44A3CBEAA /* LaunchScreen.storyboard */,
FD3AA61436162714FAEC418D /* Externals */,
56AC65AB30CBAEB08FD782A6 /* laputa_iOS */,
109126B51C7DDFCA7CD8B9EA /* Sources */,
FA63A5561E1F084B5F13E265 /* src */,
1189728653A76416E01221D7 /* Frameworks */,
021259EC384B5D897634CFF8 /* Products */,
);
sourceTree = "<group>";
};
56AC65AB30CBAEB08FD782A6 /* laputa_iOS */ = {
isa = PBXGroup;
children = (
FBB08655AD50E526ACEAE8A2 /* Info.plist */,
34F1C40770F83FDC21036505 /* laputa_iOS.entitlements */,
);
path = laputa_iOS;
sourceTree = "<group>";
};
5D80057D94E84FA08C34A8B0 /* frontmatter */ = {
isa = PBXGroup;
children = (
94B5B4BBA850E2BCAE3E4CC7 /* mod.rs */,
ACCD8318FA6543BA013E37E4 /* ops.rs */,
0CA7012668D8606FE9709D6D /* yaml.rs */,
);
path = frontmatter;
sourceTree = "<group>";
};
6A109CE663DEBC9C99A47D0B /* github */ = {
isa = PBXGroup;
children = (
0AE733EAA82134418C0AC89F /* api.rs */,
16CCC72C07657AEBC320B099 /* auth.rs */,
F9FF9FD3CEB0EB658CA17305 /* clone.rs */,
2D38A9417EA6B0A133D0D55F /* mod.rs */,
);
path = github;
sourceTree = "<group>";
};
85465A1346B035D3C3DA6236 /* git */ = {
isa = PBXGroup;
children = (
517A653F5A674050430C1167 /* commit.rs */,
4AAEE16E1AC0E472E9EAF907 /* conflict.rs */,
DDC76F9C00A29C8C52345249 /* history.rs */,
7FCDD5B9C3D021A45C402A43 /* mod.rs */,
87F0375DE367A8192708C2D8 /* pulse.rs */,
692CE75FC8E670478C4209C8 /* remote.rs */,
BB4E3DD2D3BA34BB86C908B6 /* status.rs */,
);
path = git;
sourceTree = "<group>";
};
A44D28635D2D14F8A9E644BC /* bindings */ = {
isa = PBXGroup;
children = (
D0900BA51E68887E5DA395B7 /* bindings.h */,
);
path = bindings;
sourceTree = "<group>";
};
AE0DF532B1E0DADFEDFE383A /* vault */ = {
isa = PBXGroup;
children = (
6288FD61B10C36115B12B4A4 /* cache.rs */,
5F28F393EB20BA7F8F2102EF /* config_seed.rs */,
4F4C2FE8D3697D180E1D4304 /* entry.rs */,
7D94F0F22CD503903B63FFE7 /* file.rs */,
3E23075B718E12551904D3B6 /* frontmatter.rs */,
71EDBD38A067DA56FD3E375F /* getting_started.rs */,
84ECD74B7841E52AD6549346 /* image.rs */,
1D65CB9525835A85C38D3136 /* migration.rs */,
C80E8FDDA822F4F167B601C1 /* mod_tests.rs */,
AA64014FA2D16950AD4DF619 /* mod.rs */,
0FB1053AF590466CC8971679 /* parsing.rs */,
2D31CB45BB367FD6A7FA0B67 /* rename.rs */,
5800BA1A3E5C03124E02E946 /* title_sync.rs */,
81C8EBDD5CAD14B7F366AA54 /* trash.rs */,
);
path = vault;
sourceTree = "<group>";
};
DC6461BBBF0BDFBB28352CF9 /* laputa */ = {
isa = PBXGroup;
children = (
3D8616031E45547278A88B60 /* main.mm */,
A44D28635D2D14F8A9E644BC /* bindings */,
);
path = laputa;
sourceTree = "<group>";
};
FA63A5561E1F084B5F13E265 /* src */ = {
isa = PBXGroup;
children = (
6AC39E9370FCCB01E81EC824 /* ai_chat.rs */,
6A5C3DA38846B440DD56E98A /* claude_cli.rs */,
077AD05C7FB4A784D360399B /* commands.rs */,
E1428C0A0CC22F741B7A4A0C /* lib.rs */,
50B53E0FE0FC2A5515B48A28 /* main.rs */,
F87684B058BB66A215F98DCF /* mcp.rs */,
6F67F5CDD6BB137126C5AABB /* menu.rs */,
E36BAA25D76F212157A06395 /* search.rs */,
C39B460DE1A3B78A0937BFDD /* settings.rs */,
BA9FA5D7A7A7574FF74589C8 /* telemetry.rs */,
A17AA2469F233241B0DF8BE3 /* vault_list.rs */,
5D80057D94E84FA08C34A8B0 /* frontmatter */,
85465A1346B035D3C3DA6236 /* git */,
6A109CE663DEBC9C99A47D0B /* github */,
AE0DF532B1E0DADFEDFE383A /* vault */,
);
name = src;
path = ../../src;
sourceTree = "<group>";
};
FD3AA61436162714FAEC418D /* Externals */ = {
isa = PBXGroup;
children = (
);
path = Externals;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
DAF25BBC5B5E1A3750AF7F8E /* laputa_iOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = F8ED295B21C1E1D8BE1CBAB2 /* Build configuration list for PBXNativeTarget "laputa_iOS" */;
buildPhases = (
72D053FB9C1C9BF219367CA2 /* Build Rust Code */,
2FA764A26841E6D98B919C1A /* Sources */,
46BE17F676877ABAEEFB1ACF /* Resources */,
50978AC1976616D3342459ED /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = laputa_iOS;
packageProductDependencies = (
);
productName = laputa_iOS;
productReference = CEDEEAE68B0DB4CD43ED89A8 /* laputa_iOS.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
2E2C059AB505791AB84204D8 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1430;
TargetAttributes = {
};
};
buildConfigurationList = 02446155D1A36B8D10250861 /* Build configuration list for PBXProject "laputa" */;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
Base,
en,
);
mainGroup = 314C2D9104FDC1DE81AC36AD;
minimizedProjectReferenceProxies = 1;
preferredProjectObjectVersion = 77;
productRefGroup = 021259EC384B5D897634CFF8 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
DAF25BBC5B5E1A3750AF7F8E /* laputa_iOS */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
46BE17F676877ABAEEFB1ACF /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
A2279B6B796C960A2951E7C5 /* Assets.xcassets in Resources */,
DACF8A23100E74CEA0BE1ED9 /* LaunchScreen.storyboard in Resources */,
E0FB400669725FC03B8E9B39 /* assets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
72D053FB9C1C9BF219367CA2 /* Build Rust Code */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Build Rust Code";
outputFileListPaths = (
);
outputPaths = (
"$(SRCROOT)/Externals/x86_64/${CONFIGURATION}/libapp.a",
"$(SRCROOT)/Externals/arm64/${CONFIGURATION}/libapp.a",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "npm run -- tauri ios xcode-script -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --framework-search-paths \"${FRAMEWORK_SEARCH_PATHS:?}\" --header-search-paths \"${HEADER_SEARCH_PATHS:?}\" --gcc-preprocessor-definitions \"${GCC_PREPROCESSOR_DEFINITIONS:-}\" --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?}";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
2FA764A26841E6D98B919C1A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
799F0FF5DD8053B287C9CA46 /* main.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
3CC8721F138177C2FE029D95 /* debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"DEBUG=1",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
};
name = debug;
};
44D6538EB5E448ECCC49165C /* release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
};
name = release;
};
487BF5A97BEC469EBD248159 /* debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ARCHS = (
arm64,
);
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = laputa_iOS/laputa_iOS.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphoneos*]" = x86_64;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\".\"",
);
INFOPLIST_FILE = laputa_iOS/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
"LIBRARY_SEARCH_PATHS[arch=arm64]" = "$(inherited) $(PROJECT_DIR)/Externals/arm64/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)";
"LIBRARY_SEARCH_PATHS[arch=x86_64]" = "$(inherited) $(PROJECT_DIR)/Externals/x86_64/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)";
PRODUCT_BUNDLE_IDENTIFIER = club.refactoring.laputa;
PRODUCT_NAME = laputa;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALID_ARCHS = arm64;
};
name = debug;
};
FE69C38FBEB0C14D7D5AC27E /* release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ARCHS = (
arm64,
);
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = laputa_iOS/laputa_iOS.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphoneos*]" = x86_64;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\".\"",
);
INFOPLIST_FILE = laputa_iOS/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
"LIBRARY_SEARCH_PATHS[arch=arm64]" = "$(inherited) $(PROJECT_DIR)/Externals/arm64/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)";
"LIBRARY_SEARCH_PATHS[arch=x86_64]" = "$(inherited) $(PROJECT_DIR)/Externals/x86_64/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)";
PRODUCT_BUNDLE_IDENTIFIER = club.refactoring.laputa;
PRODUCT_NAME = laputa;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALID_ARCHS = arm64;
};
name = release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
02446155D1A36B8D10250861 /* Build configuration list for PBXProject "laputa" */ = {
isa = XCConfigurationList;
buildConfigurations = (
3CC8721F138177C2FE029D95 /* debug */,
44D6538EB5E448ECCC49165C /* release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = debug;
};
F8ED295B21C1E1D8BE1CBAB2 /* Build configuration list for PBXNativeTarget "laputa_iOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
487BF5A97BEC469EBD248159 /* debug */,
FE69C38FBEB0C14D7D5AC27E /* release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = debug;
};
/* End XCConfigurationList section */
};
rootObject = 2E2C059AB505791AB84204D8 /* Project object */;
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildSystemType</key>
<string>Original</string>
<key>DisableBuildSystemDeprecationDiagnostic</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
runPostActionsOnFailure = "NO">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAF25BBC5B5E1A3750AF7F8E"
BuildableName = "laputa_iOS.app"
BlueprintName = "laputa_iOS"
ReferencedContainer = "container:laputa.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "NO"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAF25BBC5B5E1A3750AF7F8E"
BuildableName = "laputa_iOS.app"
BlueprintName = "laputa_iOS"
ReferencedContainer = "container:laputa.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
<EnvironmentVariables>
<EnvironmentVariable
key = "RUST_BACKTRACE"
value = "full"
isEnabled = "YES">
</EnvironmentVariable>
<EnvironmentVariable
key = "RUST_LOG"
value = "info"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
</TestAction>
<LaunchAction
buildConfiguration = "debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAF25BBC5B5E1A3750AF7F8E"
BuildableName = "laputa_iOS.app"
BlueprintName = "laputa_iOS"
ReferencedContainer = "container:laputa.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
<EnvironmentVariables>
<EnvironmentVariable
key = "RUST_BACKTRACE"
value = "full"
isEnabled = "YES">
</EnvironmentVariable>
<EnvironmentVariable
key = "RUST_LOG"
value = "info"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
<ProfileAction
buildConfiguration = "release"
shouldUseLaunchSchemeArgsEnv = "NO"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAF25BBC5B5E1A3750AF7F8E"
BuildableName = "laputa_iOS.app"
BlueprintName = "laputa_iOS"
ReferencedContainer = "container:laputa.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
<EnvironmentVariables>
<EnvironmentVariable
key = "RUST_BACKTRACE"
value = "full"
isEnabled = "YES">
</EnvironmentVariable>
<EnvironmentVariable
key = "RUST_LOG"
value = "info"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.1.0</string>
<key>CFBundleVersion</key>
<string>0.1.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
<string>metal</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>

View File

@@ -0,0 +1,88 @@
name: laputa
options:
bundleIdPrefix: club.refactoring.laputa
deploymentTarget:
iOS: 14.0
fileGroups: [../../src]
configs:
debug: debug
release: release
settingGroups:
app:
base:
PRODUCT_NAME: laputa
PRODUCT_BUNDLE_IDENTIFIER: club.refactoring.laputa
targetTemplates:
app:
type: application
sources:
- path: Sources
scheme:
environmentVariables:
RUST_BACKTRACE: full
RUST_LOG: info
settings:
groups: [app]
targets:
laputa_iOS:
type: application
platform: iOS
sources:
- path: Sources
- path: Assets.xcassets
- path: Externals
- path: laputa_iOS
- path: assets
buildPhase: resources
type: folder
- path: LaunchScreen.storyboard
info:
path: laputa_iOS/Info.plist
properties:
LSRequiresIPhoneOS: true
UILaunchStoryboardName: LaunchScreen
UIRequiredDeviceCapabilities: [arm64, metal]
UISupportedInterfaceOrientations:
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight
UISupportedInterfaceOrientations~ipad:
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationPortraitUpsideDown
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight
CFBundleShortVersionString: 0.1.0
CFBundleVersion: "0.1.0"
entitlements:
path: laputa_iOS/laputa_iOS.entitlements
scheme:
environmentVariables:
RUST_BACKTRACE: full
RUST_LOG: info
settings:
base:
ENABLE_BITCODE: false
ARCHS: [arm64]
VALID_ARCHS: arm64
LIBRARY_SEARCH_PATHS[arch=x86_64]: $(inherited) $(PROJECT_DIR)/Externals/x86_64/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)
LIBRARY_SEARCH_PATHS[arch=arm64]: $(inherited) $(PROJECT_DIR)/Externals/arm64/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES: true
EXCLUDED_ARCHS[sdk=iphoneos*]: x86_64
groups: [app]
dependencies:
- framework: libapp.a
embed: false
- sdk: CoreGraphics.framework
- sdk: Metal.framework
- sdk: MetalKit.framework
- sdk: QuartzCore.framework
- sdk: Security.framework
- sdk: UIKit.framework
- sdk: WebKit.framework
preBuildScripts:
- script: npm run -- tauri ios xcode-script -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --framework-search-paths "${FRAMEWORK_SEARCH_PATHS:?}" --header-search-paths "${HEADER_SEARCH_PATHS:?}" --gcc-preprocessor-definitions "${GCC_PREPROCESSOR_DEFINITIONS:-}" --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?}
name: Build Rust Code
basedOnDependencyAnalysis: false
outputFiles:
- $(SRCROOT)/Externals/x86_64/${CONFIGURATION}/libapp.a
- $(SRCROOT)/Externals/arm64/${CONFIGURATION}/libapp.a

View File

@@ -1,9 +1,9 @@
use std::borrow::Cow;
use crate::ai_chat::{AiChatRequest, AiChatResponse};
use crate::claude_cli::{
AgentStreamRequest, ChatStreamRequest, ClaudeCliStatus, ClaudeStreamEvent,
};
use crate::claude_cli::{AgentStreamRequest, ChatStreamRequest, ClaudeCliStatus};
#[cfg(desktop)]
use crate::claude_cli::ClaudeStreamEvent;
use crate::frontmatter::FrontmatterValue;
use crate::git::{
GitCommit, GitPullResult, GitPushResult, GitRemoteStatus, LastCommitInfo, ModifiedFile,
@@ -14,7 +14,9 @@ use crate::search::SearchResponse;
use crate::settings::Settings;
use crate::vault::{RenameResult, VaultEntry};
use crate::vault_list::VaultList;
use crate::{frontmatter, git, github, menu, search, vault, vault_list};
#[cfg(desktop)]
use crate::menu;
use crate::{frontmatter, git, search, vault, vault_list};
/// Expand a leading `~` or `~/` in a path string to the user's home directory.
/// Returns the original string unchanged if it doesn't start with `~` or if the
@@ -222,6 +224,7 @@ pub fn batch_trash_notes(paths: Vec<String>) -> Result<usize, String> {
// ── Git commands ────────────────────────────────────────────────────────────
#[cfg(desktop)]
#[tauri::command]
pub fn get_file_history(vault_path: String, path: String) -> Result<Vec<GitCommit>, String> {
let vault_path = expand_tilde(&vault_path);
@@ -229,12 +232,14 @@ pub fn get_file_history(vault_path: String, path: String) -> Result<Vec<GitCommi
git::get_file_history(&vault_path, &path)
}
#[cfg(desktop)]
#[tauri::command]
pub fn get_modified_files(vault_path: String) -> Result<Vec<ModifiedFile>, String> {
let vault_path = expand_tilde(&vault_path);
git::get_modified_files(&vault_path)
}
#[cfg(desktop)]
#[tauri::command]
pub fn get_file_diff(vault_path: String, path: String) -> Result<String, String> {
let vault_path = expand_tilde(&vault_path);
@@ -242,6 +247,7 @@ pub fn get_file_diff(vault_path: String, path: String) -> Result<String, String>
git::get_file_diff(&vault_path, &path)
}
#[cfg(desktop)]
#[tauri::command]
pub fn get_file_diff_at_commit(
vault_path: String,
@@ -253,6 +259,7 @@ pub fn get_file_diff_at_commit(
git::get_file_diff_at_commit(&vault_path, &path, &commit_hash)
}
#[cfg(desktop)]
#[tauri::command]
pub fn get_vault_pulse(
vault_path: String,
@@ -265,18 +272,21 @@ pub fn get_vault_pulse(
git::get_vault_pulse(&vault_path, limit, skip)
}
#[cfg(desktop)]
#[tauri::command]
pub fn git_commit(vault_path: String, message: String) -> Result<String, String> {
let vault_path = expand_tilde(&vault_path);
git::git_commit(&vault_path, &message)
}
#[cfg(desktop)]
#[tauri::command]
pub fn get_last_commit_info(vault_path: String) -> Result<Option<LastCommitInfo>, String> {
let vault_path = expand_tilde(&vault_path);
git::get_last_commit_info(&vault_path)
}
#[cfg(desktop)]
#[tauri::command]
pub async fn git_pull(vault_path: String) -> Result<GitPullResult, String> {
let vault_path = expand_tilde(&vault_path).into_owned();
@@ -285,18 +295,21 @@ pub async fn git_pull(vault_path: String) -> Result<GitPullResult, String> {
.map_err(|e| format!("Task panicked: {e}"))?
}
#[cfg(desktop)]
#[tauri::command]
pub fn get_conflict_files(vault_path: String) -> Result<Vec<String>, String> {
let vault_path = expand_tilde(&vault_path);
git::get_conflict_files(&vault_path)
}
#[cfg(desktop)]
#[tauri::command]
pub fn get_conflict_mode(vault_path: String) -> String {
let vault_path = expand_tilde(&vault_path);
git::get_conflict_mode(&vault_path)
}
#[cfg(desktop)]
#[tauri::command]
pub fn git_resolve_conflict(
vault_path: String,
@@ -307,12 +320,14 @@ pub fn git_resolve_conflict(
git::git_resolve_conflict(&vault_path, &file, &strategy)
}
#[cfg(desktop)]
#[tauri::command]
pub fn git_commit_conflict_resolution(vault_path: String) -> Result<String, String> {
let vault_path = expand_tilde(&vault_path);
git::git_commit_conflict_resolution(&vault_path)
}
#[cfg(desktop)]
#[tauri::command]
pub async fn git_push(vault_path: String) -> Result<GitPushResult, String> {
let vault_path = expand_tilde(&vault_path).into_owned();
@@ -321,6 +336,7 @@ pub async fn git_push(vault_path: String) -> Result<GitPushResult, String> {
.map_err(|e| format!("Task panicked: {e}"))?
}
#[cfg(desktop)]
#[tauri::command]
pub async fn git_remote_status(vault_path: String) -> Result<GitRemoteStatus, String> {
let vault_path = expand_tilde(&vault_path).into_owned();
@@ -329,41 +345,192 @@ pub async fn git_remote_status(vault_path: String) -> Result<GitRemoteStatus, St
.map_err(|e| format!("Task panicked: {e}"))?
}
// ── GitHub commands ─────────────────────────────────────────────────────────
// ── Git commands (mobile stubs) ─────────────────────────────────────────────
#[cfg(mobile)]
#[tauri::command]
pub async fn github_list_repos(token: String) -> Result<Vec<GithubRepo>, String> {
github::github_list_repos(&token).await
pub fn get_file_history(_vault_path: String, _path: String) -> Result<Vec<GitCommit>, String> {
Err("Git history is not available on mobile".into())
}
#[cfg(mobile)]
#[tauri::command]
pub fn get_modified_files(_vault_path: String) -> Result<Vec<ModifiedFile>, String> {
Ok(vec![])
}
#[cfg(mobile)]
#[tauri::command]
pub fn get_file_diff(_vault_path: String, _path: String) -> Result<String, String> {
Err("Git diff is not available on mobile".into())
}
#[cfg(mobile)]
#[tauri::command]
pub fn get_file_diff_at_commit(
_vault_path: String,
_path: String,
_commit_hash: String,
) -> Result<String, String> {
Err("Git diff is not available on mobile".into())
}
#[cfg(mobile)]
#[tauri::command]
pub fn get_vault_pulse(
_vault_path: String,
_limit: Option<usize>,
_skip: Option<usize>,
) -> Result<Vec<PulseCommit>, String> {
Ok(vec![])
}
#[cfg(mobile)]
#[tauri::command]
pub fn git_commit(_vault_path: String, _message: String) -> Result<String, String> {
Err("Git commit is not available on mobile".into())
}
#[cfg(mobile)]
#[tauri::command]
pub fn get_last_commit_info(_vault_path: String) -> Result<Option<LastCommitInfo>, String> {
Ok(None)
}
#[cfg(mobile)]
#[tauri::command]
pub async fn git_pull(_vault_path: String) -> Result<GitPullResult, String> {
Err("Git pull is not available on mobile".into())
}
#[cfg(mobile)]
#[tauri::command]
pub fn get_conflict_files(_vault_path: String) -> Result<Vec<String>, String> {
Ok(vec![])
}
#[cfg(mobile)]
#[tauri::command]
pub fn get_conflict_mode(_vault_path: String) -> String {
"none".to_string()
}
#[cfg(mobile)]
#[tauri::command]
pub fn git_resolve_conflict(
_vault_path: String,
_file: String,
_strategy: String,
) -> Result<(), String> {
Err("Git conflict resolution is not available on mobile".into())
}
#[cfg(mobile)]
#[tauri::command]
pub fn git_commit_conflict_resolution(_vault_path: String) -> Result<String, String> {
Err("Git conflict resolution is not available on mobile".into())
}
#[cfg(mobile)]
#[tauri::command]
pub async fn git_push(_vault_path: String) -> Result<GitPushResult, String> {
Err("Git push is not available on mobile".into())
}
#[cfg(mobile)]
#[tauri::command]
pub async fn git_remote_status(_vault_path: String) -> Result<GitRemoteStatus, String> {
Ok(GitRemoteStatus {
branch: String::new(),
has_remote: false,
ahead: 0,
behind: 0,
})
}
// ── GitHub commands ─────────────────────────────────────────────────────────
#[cfg(desktop)]
#[tauri::command]
pub async fn github_list_repos(token: String) -> Result<Vec<GithubRepo>, String> {
crate::github::github_list_repos(&token).await
}
#[cfg(desktop)]
#[tauri::command]
pub async fn github_create_repo(
token: String,
name: String,
private: bool,
) -> Result<GithubRepo, String> {
github::github_create_repo(&token, &name, private).await
crate::github::github_create_repo(&token, &name, private).await
}
#[cfg(desktop)]
#[tauri::command]
pub fn clone_repo(url: String, token: String, local_path: String) -> Result<String, String> {
let local_path = expand_tilde(&local_path);
github::clone_repo(&url, &token, &local_path)
crate::github::clone_repo(&url, &token, &local_path)
}
#[cfg(desktop)]
#[tauri::command]
pub async fn github_device_flow_start() -> Result<DeviceFlowStart, String> {
github::github_device_flow_start().await
crate::github::github_device_flow_start().await
}
#[cfg(desktop)]
#[tauri::command]
pub async fn github_device_flow_poll(device_code: String) -> Result<DeviceFlowPollResult, String> {
github::github_device_flow_poll(&device_code).await
crate::github::github_device_flow_poll(&device_code).await
}
#[cfg(desktop)]
#[tauri::command]
pub async fn github_get_user(token: String) -> Result<GitHubUser, String> {
github::github_get_user(&token).await
crate::github::github_get_user(&token).await
}
// ── GitHub commands (mobile stubs) ──────────────────────────────────────────
#[cfg(mobile)]
#[tauri::command]
pub async fn github_list_repos(_token: String) -> Result<Vec<GithubRepo>, String> {
Err("GitHub integration is not available on mobile".into())
}
#[cfg(mobile)]
#[tauri::command]
pub async fn github_create_repo(
_token: String,
_name: String,
_private: bool,
) -> Result<GithubRepo, String> {
Err("GitHub integration is not available on mobile".into())
}
#[cfg(mobile)]
#[tauri::command]
pub fn clone_repo(_url: String, _token: String, _local_path: String) -> Result<String, String> {
Err("Git clone is not available on mobile".into())
}
#[cfg(mobile)]
#[tauri::command]
pub async fn github_device_flow_start() -> Result<DeviceFlowStart, String> {
Err("GitHub integration is not available on mobile".into())
}
#[cfg(mobile)]
#[tauri::command]
pub async fn github_device_flow_poll(_device_code: String) -> Result<DeviceFlowPollResult, String> {
Err("GitHub integration is not available on mobile".into())
}
#[cfg(mobile)]
#[tauri::command]
pub async fn github_get_user(_token: String) -> Result<GitHubUser, String> {
Err("GitHub integration is not available on mobile".into())
}
// ── AI / Claude commands ────────────────────────────────────────────────────
@@ -373,11 +540,13 @@ pub async fn ai_chat(request: AiChatRequest) -> Result<AiChatResponse, String> {
crate::ai_chat::send_chat(request).await
}
#[cfg(desktop)]
#[tauri::command]
pub fn check_claude_cli() -> ClaudeCliStatus {
crate::claude_cli::check_cli()
}
#[cfg(desktop)]
#[tauri::command]
pub async fn stream_claude_chat(
app_handle: tauri::AppHandle,
@@ -393,6 +562,7 @@ pub async fn stream_claude_chat(
.map_err(|e| format!("Task failed: {e}"))?
}
#[cfg(desktop)]
#[tauri::command]
pub async fn stream_claude_agent(
app_handle: tauri::AppHandle,
@@ -408,6 +578,35 @@ pub async fn stream_claude_agent(
.map_err(|e| format!("Task failed: {e}"))?
}
// ── Claude CLI (mobile stubs) ───────────────────────────────────────────────
#[cfg(mobile)]
#[tauri::command]
pub fn check_claude_cli() -> ClaudeCliStatus {
ClaudeCliStatus {
installed: false,
version: None,
}
}
#[cfg(mobile)]
#[tauri::command]
pub async fn stream_claude_chat(
_app_handle: tauri::AppHandle,
_request: ChatStreamRequest,
) -> Result<String, String> {
Err("Claude CLI is not available on mobile".into())
}
#[cfg(mobile)]
#[tauri::command]
pub async fn stream_claude_agent(
_app_handle: tauri::AppHandle,
_request: AgentStreamRequest,
) -> Result<String, String> {
Err("Claude CLI is not available on mobile".into())
}
// ── Search commands ─────────────────────────────────────────────────────────
#[tauri::command]
@@ -426,6 +625,7 @@ pub async fn search_vault(
// ── MCP commands ────────────────────────────────────────────────────────────
#[cfg(desktop)]
#[tauri::command]
pub async fn register_mcp_tools(vault_path: String) -> Result<String, String> {
let vault_path = expand_tilde(&vault_path).into_owned();
@@ -434,6 +634,7 @@ pub async fn register_mcp_tools(vault_path: String) -> Result<String, String> {
.map_err(|e| format!("Registration task failed: {e}"))?
}
#[cfg(desktop)]
#[tauri::command]
pub async fn check_mcp_status() -> Result<crate::mcp::McpStatus, String> {
tokio::task::spawn_blocking(crate::mcp::check_mcp_status)
@@ -441,6 +642,18 @@ pub async fn check_mcp_status() -> Result<crate::mcp::McpStatus, String> {
.map_err(|e| format!("MCP status check failed: {e}"))
}
#[cfg(mobile)]
#[tauri::command]
pub async fn register_mcp_tools(_vault_path: String) -> Result<String, String> {
Err("MCP is not available on mobile".into())
}
#[cfg(mobile)]
#[tauri::command]
pub async fn check_mcp_status() -> Result<crate::mcp::McpStatus, String> {
Ok(crate::mcp::McpStatus::NotInstalled)
}
#[tauri::command]
pub fn repair_vault(vault_path: String) -> Result<String, String> {
let vault_path = expand_tilde(&vault_path);
@@ -463,6 +676,7 @@ pub fn get_build_number(app_handle: tauri::AppHandle) -> String {
parse_build_label(&version)
}
#[cfg(desktop)]
#[tauri::command]
pub fn update_menu_state(
app_handle: tauri::AppHandle,
@@ -480,6 +694,17 @@ pub fn update_menu_state(
Ok(())
}
#[cfg(mobile)]
#[tauri::command]
pub fn update_menu_state(
_app_handle: tauri::AppHandle,
_has_active_note: bool,
_has_modified_files: Option<bool>,
_has_conflicts: Option<bool>,
) -> Result<(), String> {
Ok(())
}
#[tauri::command]
pub fn get_settings() -> Result<Settings, String> {
crate::settings::get_settings()

View File

@@ -5,6 +5,7 @@ pub mod frontmatter;
pub mod git;
pub mod github;
pub mod mcp;
#[cfg(desktop)]
pub mod menu;
pub mod search;
pub mod settings;
@@ -12,11 +13,15 @@ pub mod telemetry;
pub mod vault;
pub mod vault_list;
#[cfg(desktop)]
use std::process::Child;
#[cfg(desktop)]
use std::sync::Mutex;
#[cfg(desktop)]
struct WsBridgeChild(Mutex<Option<Child>>);
#[cfg(desktop)]
fn log_startup_result(label: &str, result: Result<usize, String>) {
match result {
Ok(n) if n > 0 => log::info!("{}: {} files", label, n),
@@ -26,6 +31,7 @@ fn log_startup_result(label: &str, result: Result<usize, String>) {
}
/// Run startup housekeeping on the default vault (purge old trash, migrate legacy frontmatter).
#[cfg(desktop)]
fn run_startup_tasks() {
let vault_path = dirs::home_dir()
.map(|h| h.join("Laputa"))
@@ -54,6 +60,7 @@ fn run_startup_tasks() {
}
}
#[cfg(desktop)]
fn spawn_ws_bridge(app: &mut tauri::App) {
use tauri::Manager;
let vault_path = dirs::home_dir()
@@ -71,8 +78,12 @@ fn spawn_ws_bridge(app: &mut tauri::App) {
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.manage(WsBridgeChild(Mutex::new(None)))
let builder = tauri::Builder::default();
#[cfg(desktop)]
let builder = builder.manage(WsBridgeChild(Mutex::new(None)));
builder
.setup(|app| {
if cfg!(debug_assertions) {
app.handle().plugin(
@@ -96,8 +107,13 @@ pub fn run() {
if telemetry::init_sentry_from_settings() {
log::info!("Sentry initialized (crash reporting enabled)");
}
run_startup_tasks();
spawn_ws_bridge(app);
#[cfg(desktop)]
{
run_startup_tasks();
spawn_ws_bridge(app);
}
Ok(())
})
.invoke_handler(tauri::generate_handler![
@@ -162,14 +178,17 @@ pub fn run() {
])
.build(tauri::generate_context!())
.expect("error while building tauri application")
.run(|app_handle, event| {
use tauri::Manager;
if let tauri::RunEvent::Exit = event {
let state: tauri::State<'_, WsBridgeChild> = app_handle.state();
let mut guard = state.0.lock().unwrap();
if let Some(ref mut child) = *guard {
let _ = child.kill();
log::info!("ws-bridge child process killed on exit");
.run(|_app_handle, _event| {
#[cfg(desktop)]
{
use tauri::Manager;
if let tauri::RunEvent::Exit = _event {
let state: tauri::State<'_, WsBridgeChild> = _app_handle.state();
let mut guard = state.0.lock().unwrap();
if let Some(ref mut child) = *guard {
let _ = child.kill();
log::info!("ws-bridge child process killed on exit");
}
}
}
});