Docs
KEd Studio
Applications
KEd Studio
Overview
IntroductionArchitectureDevelopment setup
KEd JSON contractKEd language assistDesktop integration planCloud books auth planBook editor data integration
Platform integration
HomeBrowse applications

© 2026 Wekify LLC · Kifuliiru Lab

Homellms.txtkifuliiru.com

GuidesDesktop integration plan

Desktop integration plan

Desktop app integration planning.

How kifuliiru-data-generators (web) hands off to KEd Studio (desktop) without port conflicts.


Problem

Two approaches that do not scale well:

| Approach | Why it fails | |----------|----------------| | Generator opens http://localhost:3000?data=... | Port clash if either app uses 3000; URL size limits for large datasets | | sessionStorage + navigate to another origin | Different apps = different origins; storage does not transfer | | Fixed localhost bridge | User must run both servers; firewall and port conflicts |

Goal: User clicks Open in Editor in the generator → KEd Studio opens with the book loaded. No manual import, no port coordination.


Recommended approach: desktop app + file handoff

Code
sequenceDiagram
 participant User
 participant Generator as kifuliiru-data-generators (web)
 participant OS as OS / browser
 participant Studio as KEd Studio (desktop)

 User->>Generator: Generate numbers / sentences
 User->>Generator: Click "Open in Editor"
 Generator->>Generator: Build .ked.json payload
 Generator->>OS: Write temp file book.ked.json
 Generator->>OS: Open kedstudio://open?path=...
 OS->>Studio: Launch or focus app
 Studio->>Studio: Read file, loadPackage()
 Studio->>User: Book preview ready
```text

### Why desktop-first is the right call

1. **No port dependency** — generator stays a normal deployed web app; studio runs its own embedded server on `127.0.0.1` with an ephemeral port inside the desktop shell.
2. **Reliable export** — Puppeteer PDF and Node APIs already need a server; desktop avoids serverless limits.
3. **File system** — temp packages, recent files, native Save/Open dialogs.
4. **Custom URL protocol** — standard pattern (`vscode://`, `slack://`, `figma://`); browser can trigger it from the generator with one click.
5. **Same codebase** — wrap existing Next.js app; no rewrite of book logic.

### Trade-offs

| Cost | Mitigation | |------|------------| | Install step for studio | Offer download link + keep **Import .ked.json** fallback | | macOS/Windows signing | Start with dev builds; notarize before public release | | Two deliverables (web optional, desktop primary) | Web remains for dev/CI; desktop is the publisher build |

---

## Technical design

### 1. Desktop shell: Tauri 2 (recommended)

| Option | Verdict | |--------|---------| | **Tauri** | Small binary, Rust shell, WebView — good fit | | Electron | Heavier; fine if team already uses it | | PWA only | Cannot register custom protocols reliably; weak file access |

**Runtime layout:**

```text
┌─────────────────────────────────────┐
│ Tauri window (WebView) │
│ → http://127.0.0.1:<random>/ │
├─────────────────────────────────────┤
│ Sidecar: next start (or node server) │
│ - export-pdf / export-docx │
│ - puppeteer │
└─────────────────────────────────────┘
```text

- On app start: spawn Next production server on `127.0.0.1:0`, open WebView to that URL.
- On quit: stop child process.
- Dev mode: `npm run dev` + Tauri dev pointing at `localhost:3000`.

### 2. Custom URL protocol

Register **`kedstudio://`** (or `kifuliiru-ked://`).

**Open package:**

```text
kedstudio://open?path=/Users/me/Library/Caches/kifuliiru/studio-inbox/numbers-0-100.ked.json
```text

**Optional query params (v2):**

```text
kedstudio://open?path=...&page=cover
```text

**Studio handler (pseudo):**

```ts
// On deep link / second instance:
const filePath = decodeURIComponent(new URL(url).searchParams.get("path"));
const raw = await readFile(filePath, "utf8");
loadPackage(JSON.parse(raw), basename(filePath));
```text

Tauri: use `tauri-plugin-deep-link` + single-instance plugin so a second link focuses the existing window.

### 3. Generator side (`kifuliiru-data-generators`)

**Button: Open in Editor**

```ts
async function openInKedStudio(editorData: KedPackage) {
 const payload = buildKedJson(editorData); // ked_version, book_type, rows, ...

 // 1. POST to a small "handoff" endpoint that returns a signed temp URL — OR —
 // 2. Client-side: trigger download + protocol (desktop-only path)

 // Recommended: generator API writes temp file server-side is NOT needed.
 // Client writes via File System Access API where available, else download fallback.

 const blob = new Blob([JSON.stringify(payload, null, 2)], { type: "application/json" });
 const filename = `kifuliiru-${payload.book_type}-${Date.now()}.ked.json`;

 // Desktop path: save to known inbox via optional native helper, or:
 const url = URL.createObjectURL(blob);
 const a = document.createElement("a");
 a.href = url;
 a.download = filename;
 a.click(); // ensures file exists in Downloads

 // Then attempt protocol open with path — desktop app registers handler for Downloads scan
 // BETTER: Tauri / native helper exposes kedstudio://import with base64 for small payloads
 // BEST: write to OS temp via @tauri-apps/api from a tiny companion — see below
}
```text

**Practical v1 handoff (simplest):**

1. Generator builds `.ked.json` in memory.
2. `POST` to generator backend `/api/ked-handoff` → stores blob 5 min → returns `handoff_id`.
3. Open `kedstudio://open?handoff=https://generators.../api/ked-handoff/{id}`.
4. Desktop app fetches handoff URL, loads package, deletes handoff.

This avoids writing local paths from the browser (browsers cannot pass arbitrary file paths to custom protocols). **File path protocol works when the desktop app writes the file** (e.g. generator downloads, user picks "Open with KEd Studio") or when using **handoff URL**.

**Recommended v1 for browser → desktop:**

| Step | Action | |------|--------| | 1 | Generator creates `.ked.json` | | 2 | `POST /api/ked-handoff` → `{ id, url }` (short-lived, auth optional) | | 3 | `window.location.href = 'kedstudio://open?url=' + encodeURIComponent(handoffUrl)` | | 4 | Desktop app fetches URL, `loadPackage()`, shows success |

No local ports. No huge URLs. Works from any deployed generator.

**v2:** Add `path=` for when both apps are local and a native bridge writes the file.

### 4. Shared contract (already in place)

- [KED_JSON_CONTRACT.md](/ked-studio/guides/ked-json-contract)
- `book_settings` + `template` round-trip on Save
- Legacy `editorData` normalization in `lib/package.ts`

Generator must emit:

```json
{
 "ked_version": "1.0",
 "source_app": "kifuliiru-data-generators",
 "source_app_version": "1.2.0",
 "book_type": "numbers",
 "title": "...",
 "subtitle": "...",
 "languages": ["kifuliiru", "english"],
 "columns": [...],
 "rows": [...],
 "metadata": { "range": [0, 100], "author": "..." }
}
```text

Studio applies defaults for `book_settings` and `template` if omitted.

### 5. Detect desktop app installed

Generator UI:

```ts
const INSTALLED_KEY = "ked-studio-available";

async function tryOpenInEditor(payload: KedPackage) {
 const handoff = await createHandoff(payload);
 const deepLink = `kedstudio://open?url=${encodeURIComponent(handoff.url)}`;

 // Timeout trick: if app opens, page may blur; else show install CTA
 window.location.href = deepLink;

 setTimeout(() => {
 if (!document.hidden) {
 showDialog({
 title: "KEd Studio not found",
 actions: [
 { label: "Download desktop app", href: RELEASES_URL },
 { label: "Download .ked.json instead", onClick: () => downloadJson(payload) },
 ],
 });
 }
 }, 1500);
}
```text

---

## Implementation phases

### Phase A — Desktop MVP (this repo)

- [ ] Add `src-tauri/` (Tauri 2) wrapping Next.js production build
- [ ] Spawn `next start` on random localhost port; load WebView
- [ ] Register `kedstudio://` protocol (macOS + Windows)
- [ ] Handle `open?url=` → fetch handoff → `loadPackage()`
- [ ] Menu: Open file, Recent files, Quit
- [ ] CI: build `.dmg` / `.msi`

**Estimate:** 3–5 days for MVP shell + protocol.

### Phase B — Generator handoff (kifuliiru-data-generators)

- [ ] `buildKedJson()` helper per data type (numbers first)
- [ ] `POST /api/ked-handoff` (Redis or filesystem temp, 10 min TTL)
- [ ] **Open in Editor** button on numbers page (then sentences, etc.)
- [ ] Install detection + fallback download
- [ ] Link to desktop releases README

**Estimate:** 2–3 days for numbers end-to-end.

### Phase C — Polish

- [ ] Handoff auth (signed tokens, single use)
- [ ] Pass `book_settings` presets from generator (optional)
- [ ] Auto-update desktop app (Tauri updater)
- [ ] Linux `.AppImage` if needed

### Phase D — Web studio (optional)

Keep `npm run dev` for development. Production web deploy is optional (export APIs need Node + Chromium). **Desktop is the primary publisher artifact.**

---

## What not to do

- Do not depend on both apps sharing `localhost:3000`.
- Do not put full `rows` arrays in query strings.
- Do not rely on `sessionStorage` across separate web apps.
- Do not block manual Import — always keep file picker + sample data.

---

## Open questions

1. **Release hosting** — GitHub Releases vs kifuliiru.com download page?
2. **Handoff TTL** — 10 minutes enough for slow networks?
3. **Offline generator** — is a local-only generator build needed later?
4. **Code signing** — Apple Developer ID timeline for macOS Gatekeeper?

---

## References

- [BOOK_EDITOR_DATA_INTEGRATION.md](./reference/web-editor/BOOK_EDITOR_DATA_INTEGRATION.md) — legacy sessionStorage approach (superseded by this plan)
- [ROADMAP.md](./ROADMAP.md) — product phases
- [Tauri deep link plugin](https://v2.tauri.app/plugin/deep-link/)
- [Tauri single instance](https://v2.tauri.app/plugin/single-instance/)
PreviousKEd language assistNextCloud books auth plan

On this page

  • Problem
  • Recommended approach: desktop app + file handoff