No description
Find a file
Dunemask e863cd0d6c [FEATURE] inappwebview demo+example shells + scaffold connectivity
- migrate demo/web-vs-native + examples/todo shells: webview_all -> flutter_inappwebview
- scaffold: emit {type:connectivity, online} via connectivity_plus on network flips + web ready
- replace stock widget_test.dart (referenced nonexistent MyApp) with harness smoke tests
- untrack generated ios/Flutter/ephemeral + add gitignore rule
- docs/bridge-protocol: document the connectivity message

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 12:56:39 -06:00
.claude [FEATURE] Initial Demo 2026-05-06 17:27:54 -06:00
demo/web-vs-native [FEATURE] inappwebview demo+example shells + scaffold connectivity 2026-05-25 12:56:39 -06:00
docs [FEATURE] inappwebview demo+example shells + scaffold connectivity 2026-05-25 12:56:39 -06:00
examples/todo [FEATURE] inappwebview demo+example shells + scaffold connectivity 2026-05-25 12:56:39 -06:00
packages/vite-plugin [FEATURE] inappwebview demo+example shells + scaffold connectivity 2026-05-25 12:56:39 -06:00
scripts [CHORE] slim root scripts; menu-driven sub-clis; biome + tsgo (#1) 2026-05-07 00:09:33 +00:00
.gitignore [CHORE] Minor adjustments 2026-05-07 11:48:42 -06:00
biome.json [CHORE] slim root scripts; menu-driven sub-clis; biome + tsgo (#1) 2026-05-07 00:09:33 +00:00
bun.lock [FEATURE] Neutral Plugins (#5) 2026-05-14 16:26:33 +00:00
package.json [CHORE] slim root scripts; menu-driven sub-clis; biome + tsgo (#1) 2026-05-07 00:09:33 +00:00
README.md [FEATURE] Initial Demo 2026-05-06 17:27:54 -06:00
tsconfig.base.json [FEATURE] wire VitePWA + workbox-window into todo example + web-vs-native demo 2026-05-07 13:03:51 -06:00

lion-turtle

Mobile-shell platform. Flutter native shell wraps any Vite + React app via a single plugin. Local-first storage with sync hooks. Browser-side dev mock so you can iterate without rebuilding the native shell. Full Flutter control + log viewer + tunnel + device picker built into the dev tooling.

Pieces

Package Purpose
@lion-turtle/vite Vite plugin (dev-proxy, DevShell iframe mock, web-side bridge runtime, Flutter control plane, env sniff, tunnel) + bundled Flutter scaffold template. Bridge protocol + store + extension types re-exported from @lion-turtle/vite/runtime.

Layout

demo/                  spike — native widgets vs React-in-WebView, no server, no auth
  web/ + shell/
examples/
  todo/                full reference app — auth, offline-first, sync, prod build
    web/ + server/ + shell-app/  + dev.ts + build.ts
docs/                  AI-agent docs (read first)
packages/              @lion-turtle/vite

Quick start (consumer)

// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { lionTurtle } from '@lion-turtle/vite';

export default defineConfig({
  plugins: [
    react(),
    lionTurtle({
      devProxy: { preferredPort: 9999, portRange: 100 },
      devShell: { enabled: true, frame: 'iphone' },
      scaffold: { shellDir: '../shell-app' },
      flutter: {
        enabled: true,
        shellDir: '../shell-app',
        prodRootTarget: 'https://app.example.com',  // injected at build time
      },
    }),
  ],
});
// src/main.tsx
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import {
  DevShell,
  ShellProvider,
  isNative,
  installDevShellControl,
  installLogPipe,
} from '@lion-turtle/vite/runtime';
import { App } from './App';

if (import.meta.env.DEV) {
  installLogPipe();
  installDevShellControl();
}

const params = new URLSearchParams(location.search);
const showDevShell = import.meta.env.DEV && !isNative() && !params.has('_embed');

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <ShellProvider>{showDevShell ? <DevShell /> : <App />}</ShellProvider>
  </StrictMode>,
);

On first vite dev:

  • dev-proxy probes ports 9999..10098, takes first free
  • if <project>/<shellDir>/ is empty → scaffolds @lion-turtle/vite/scaffold/ Flutter app into it (consumer owns the copy after that)
  • DevShell renders at /, the iframe inside loads /?_embed=1

Run the demo (spike)

Native vs React rendering, same screens, side-by-side. No server.

bun install
bun run demo                    # http://localhost:5173

Run the example (TODO with auth + offline + sync)

bun run todo                    # interactive: pick device, asks about flutter shell
# OR
bun run todo:server             # server alone (port 3001)
bun run todo:web                # vite alone (port 5173 + dev-proxy 9999)

Build for production (ROOT_TARGET injected at build time):

PROD_ROOT_TARGET=https://todo.example.com bun run todo:build:apk
PROD_ROOT_TARGET=https://todo.example.com bun run todo:build:ios
# or:
bun examples/todo/build.ts ipa --root-target https://todo.example.com

What's in the box

  • DevShell — phone-frame iframe mock with bridge log, console viewer, env editor, dev-forward routes editor, device picker, tunnel control, Flutter control. Auto-scales phone to fit canvas. Sleek scrollbars.
  • Dev-proxy — Bun child process on port 9999 (auto-bumps if taken). /__via/<scheme>/<host>:<port>/ path forwarding, vite fallback for unmapped paths.
  • Local-first storeuseStore(table) hook, localStorage backing in v0.1, sync replay + pull against a server, lenient reconciliation.
  • Auto ROOT_TARGET resolution — picks the right URL for android emulator (10.0.2.2) vs physical (localhost + adb reverse) vs iOS sim vs iOS phone (tunnel).
  • Offline-first WebView — scaffold runs a local cache+proxy server on 127.0.0.1:<random>; WebView loads through it. GETs cached on disk, non-GETs proxied. On cache miss + offline → 503 → Flutter shows "MAINFRAME UNREACHABLE" screen.
  • Flutter control plane — start / stop / hot-reload / hot-restart from DevShell. Logs streamed live, persisted, copyable as raw text or AI-prompt markdown.
  • Tunnel — cloudflared / ngrok integration. Auto-feeds tunnel URL into Flutter ROOT_TARGET when a physical iOS device is selected.
  • Env sniff — reads VITE_* from process.env, lets you override per-key in DevShell, exposes useEnv("VITE_FOO") reactive hook.
  • Build scriptbun run todo:build:apk injects prodRootTarget via --dart-define=ROOT_TARGET=… at build time.
  • Interactive bun run todo — prompts for device, sets up adb reverse if Android physical, launches server + vite + offers Flutter via DevShell.

Docs

File Topic
docs/architecture.md Layout + extension points
docs/devshell.md DevShell tabs, controls, persistence, native-mode overlay
docs/flutter-shell.md Flutter integration, ROOT_TARGET resolution, cache server, prod builds, debug checklist
docs/integration-guide.md Plug into a Vite project from scratch
docs/bridge-protocol.md Wire format + message kinds + handshake
docs/store-api.md Local-first store CRUD + replay
docs/extension-api.md Custom bridge handlers, store schemas
docs/file-map.md Layout reference
docs/conventions.md Naming, patterns, style
docs/glossary.md Domain terms

Status

Pre-1.0. Local-only, no remote. APIs may shift.