Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
2a73650
Use RufletApp as the canonical wire control
AdamMusa Aug 11, 2026
5cb8d8d
Route Apple builds through Flutter startup
AdamMusa Aug 12, 2026
531a94c
Exclude package caches from managed client sync
AdamMusa Aug 12, 2026
55913aa
Copy managed Flutter template into client root
AdamMusa Aug 12, 2026
786a19c
Repair invalid managed Flutter client roots
AdamMusa Aug 12, 2026
e592c1d
Ship application Apple extensions through builds
AdamMusa Aug 13, 2026
6a569a2
Exclude generated Apple build artifacts from templates
AdamMusa Aug 13, 2026
f3a4de1
Configure Apple runtime autostart during builds
AdamMusa Aug 16, 2026
8a6c817
Publish native Apple experimental prebuilds
AdamMusa Aug 21, 2026
a6ce9d8
Add in-process Ruflet connection contract
AdamMusa Aug 22, 2026
30c1ee3
Route embedded Ruflet sessions in process
AdamMusa Aug 22, 2026
0aa1d77
Add native in-process protocol queues
AdamMusa Aug 22, 2026
9242cd9
Expose in-process bridge to embedded Ruby
AdamMusa Aug 22, 2026
e24b162
Boot packaged Apple apps without loopback sockets
AdamMusa Aug 22, 2026
d5971b4
Expose embedded protocol messages to Flutter
AdamMusa Aug 22, 2026
3fcd2ab
Restore authoritative Apple renderer sync tool
AdamMusa Aug 22, 2026
5685f81
Make Apple VM builds reproducible
AdamMusa Aug 22, 2026
1f71b41
Test packaged VM transport end to end
AdamMusa Aug 22, 2026
1b8776a
Reject Apple runtime network fallback
AdamMusa Aug 22, 2026
ce70d45
Add RufletRecord to embedded runtimes
AdamMusa Aug 22, 2026
f618be9
Allow testing uninstalled VM artifacts
AdamMusa Aug 22, 2026
d53a9fd
Rebuild Apple VM with in-process transport
AdamMusa Aug 22, 2026
e6be423
Document Apple in-process runtime contract
AdamMusa Aug 22, 2026
c5d641e
Remove server network metadata from embedded Apple builds
AdamMusa Aug 22, 2026
1ff5ff4
Keep embedded Ruby background tasks responsive
AdamMusa Aug 22, 2026
260ca96
Rebuild Apple VM for responsive embedded tasks
AdamMusa Aug 22, 2026
8c01e00
Add lite and full self-contained build profiles
AdamMusa Aug 27, 2026
7430593
Preload embedded runtimes across native platforms
AdamMusa Aug 27, 2026
249c276
Fix Android mruby record extension rebuild
AdamMusa Aug 27, 2026
f0e7994
Shrink Android lite native runtime
AdamMusa Aug 27, 2026
b1f77fa
Shrink Android full CRuby runtime
AdamMusa Aug 27, 2026
a190621
Use in-process transport for Android full builds
AdamMusa Aug 27, 2026
7326b8e
Route all self builds through in-process transport
AdamMusa Aug 27, 2026
d4f9687
Add Flet Ads controls to Ruby DSL
AdamMusa Sep 1, 2026
abf6f85
Add Flet color pickers to Ruby DSL
AdamMusa Sep 1, 2026
139664b
Add Flet DataTable2 controls to Ruby DSL
AdamMusa Sep 1, 2026
f32edc8
Complete Flet Map attribution DSL
AdamMusa Sep 1, 2026
4692802
Complete browser context menu service parity
AdamMusa Sep 1, 2026
4f40abd
Complete Flet Camera service methods
AdamMusa Sep 1, 2026
4118089
Complete Flet CodeEditor Ruby API
AdamMusa Sep 1, 2026
5c68cfd
Complete core control DSL helpers
AdamMusa Sep 1, 2026
bc66db4
Update charts DSL for current Flet API
AdamMusa Sep 1, 2026
6877c2e
Update media extensions for current Flet API
AdamMusa Sep 1, 2026
d3cbe6b
Expose current Flet methods on service objects
AdamMusa Sep 1, 2026
3968407
Add direct DSL helpers for every Flet service
AdamMusa Sep 1, 2026
50a5170
Register every Flet Map component
AdamMusa Sep 1, 2026
626ca58
Expose View through the module DSL
AdamMusa Sep 1, 2026
0432e9e
Declare mruby schema for browser menu service
AdamMusa Sep 1, 2026
68e11ac
Reject incompatible full runtime gem bundles
AdamMusa Sep 1, 2026
b9e52ff
feat: unify platform controls in Ruby DSL
AdamMusa Sep 1, 2026
f056ca7
build: resolve and verify bundled Ruflet engine and extension forks
AdamMusa Sep 7, 2026
204f97e
test: verify RufletApp DSL renderer wire contract
AdamMusa Sep 7, 2026
53027f7
fix: preserve explicit LAN access in self-contained Apple builds
AdamMusa Sep 7, 2026
7eaeb68
fix: keep live client page measurements and brightness current
AdamMusa Sep 7, 2026
23df15f
build: ship iOS runtime with live page measurement updates
AdamMusa Sep 7, 2026
8d27890
perf: prune generated folders before scanning hot reload inputs
AdamMusa Sep 7, 2026
34f95e7
fix: preserve typed dates in canonical picker DSL
AdamMusa Sep 7, 2026
f95e6cb
build: verify pinned engine source before packaging
AdamMusa Sep 7, 2026
5ef1e08
perf: patch only changed controls on page update
AdamMusa Sep 7, 2026
6dc8efd
build: refresh Apple lite runtime updates
AdamMusa Sep 7, 2026
22849e2
refactor: remove legacy Rails web adapters
AdamMusa Sep 7, 2026
42edc2f
release: prepare Ruflet 0.0.22 gems
AdamMusa Sep 7, 2026
16276a3
fix: republish page state after reconnect
AdamMusa Sep 7, 2026
2b53c7b
fix: preserve embedded event payload values
AdamMusa Sep 7, 2026
0de447e
fix: apply page scroll to the active view
AdamMusa Sep 7, 2026
3ddae9b
test: verify storage path callback results
AdamMusa Sep 7, 2026
84b9078
fix: accept client diagnostic messages
AdamMusa Sep 7, 2026
4c00755
feat: add opt-in protocol tracing
AdamMusa Sep 7, 2026
dcd6fa8
fix: make Rails native service actions predictable
AdamMusa Sep 7, 2026
c52011f
feat: honor Rails render and redirect intents
AdamMusa Sep 7, 2026
43ccd1f
feat: expose every registered control to Rails views
AdamMusa Sep 7, 2026
47d65c3
feat: map Rails HTML grids to responsive rows
AdamMusa Sep 7, 2026
6488db2
feat: expand Rails HTML utility style support
AdamMusa Sep 7, 2026
ff07b1f
chore: keep embedded runtime analysis clean
AdamMusa Sep 7, 2026
8542d7d
fix: derive Apple privacy declarations from services
AdamMusa Sep 7, 2026
c1292e9
Merge main into ruflet_on_rails
AdamMusa Sep 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ ruby_runtime/macos/mruby_src/** linguist-vendored
ruby_runtime/ios/vendor/** linguist-vendored
ruby_runtime/macos/vendor/** linguist-vendored
ruby_runtime/vm/mrbgems/mruby-onig-regexp/** linguist-vendored
ruby_runtime/vm/vendor/sqlite/** linguist-vendored

ruby_runtime/ios/build_host/** linguist-generated
ruby_runtime/macos/build_host/** linguist-generated
Expand Down
166 changes: 162 additions & 4 deletions .github/workflows/build-ruflet-client.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ on:
release:
types: [published]
push:
branches: [main, prebuild]
branches: [main, prebuild, experimental]
tags: ['v*']
paths:
- '.github/workflows/build-ruflet-client.yml'
Expand All @@ -42,6 +42,7 @@ concurrency:
env:
EXPLORER_REPOSITORY: AdamMusa/ruflet_explorer
EXPLORER_REF: ${{ github.event.inputs.explorer_ref || 'main' }}
EXPERIMENTAL_TEMPLATE_REF: experimental

jobs:
verify-explorer:
Expand Down Expand Up @@ -412,8 +413,8 @@ jobs:

- name: Package iOS simulator artifact
run: |
app=$(find explorer/build -path '*iphonesimulator*' -name '*.app' -maxdepth 8 | head -n 1)
if [ -z "$app" ]; then
app=explorer/build/client/build/ios/iphonesimulator/Runner.app
if [ ! -d "$app" ]; then
echo "No simulator .app produced"
exit 1
fi
Expand Down Expand Up @@ -453,6 +454,126 @@ jobs:
overwrite_files: true
files: ruflet_explorer-ios-simulator.zip

build-experimental-apple:
name: Build experimental ${{ matrix.target }}
needs: verify-explorer
runs-on: macos-latest
strategy:
fail-fast: false
matrix:
include:
- target: macos
build_args: macos --exp --verbose
artifact: ruflet_explorer-macos-experimental-universal.zip
- target: ios
build_args: ios --self --exp --simulator --verbose
artifact: ruflet_explorer-ios-experimental-simulator.zip

steps:
- name: Checkout ruflet
uses: actions/checkout@v5
with:
path: ruflet

- name: Checkout Ruflet Explorer
uses: actions/checkout@v5
with:
repository: ${{ env.EXPLORER_REPOSITORY }}
ref: ${{ env.EXPLORER_REF }}
path: explorer

- name: Checkout Ruflet template
uses: actions/checkout@v5
with:
repository: AdamMusa/ruflet-template
ref: ${{ env.EXPERIMENTAL_TEMPLATE_REF }}
path: ruflet-template

- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3'

- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
cache: true

- name: Build experimental ${{ matrix.target }} client
working-directory: explorer
env:
LANG: en_US.UTF-8
LC_ALL: en_US.UTF-8
RUFLET_TEMPLATE_ROOT: ${{ github.workspace }}/ruflet-template
RUFLET_RUBY_RUNTIME_PATH: ${{ github.workspace }}/ruflet/ruby_runtime
run: ../ruflet/packages/ruflet/bin/ruflet build ${{ matrix.build_args }}

- name: Package experimental macOS artifact
if: matrix.target == 'macos'
run: |
app=$(find explorer/build/client/build/macos/Build/Products/Release -maxdepth 1 -name '*.app' | head -n 1)
if [ -z "$app" ]; then
echo "No experimental macOS .app produced"
exit 1
fi
ditto -c -k --sequesterRsrc --keepParent "$app" "${{ matrix.artifact }}"

- name: Package experimental iOS simulator artifact
if: matrix.target == 'ios'
run: |
app=explorer/build/client/build/ios/iphonesimulator/Runner.app
if [ ! -d "$app" ]; then
echo "No experimental iOS simulator .app produced"
exit 1
fi
ditto -c -k --sequesterRsrc --keepParent "$app" "${{ matrix.artifact }}"

- name: Upload workflow artifact
uses: actions/upload-artifact@v5
with:
name: ruflet_explorer-${{ matrix.target }}-experimental
path: ${{ matrix.artifact }}

- name: Attach to GitHub release
if: github.event_name == 'release'
uses: softprops/action-gh-release@v2
with:
files: ${{ matrix.artifact }}

- name: Publish experimental Apple prebuild
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'prebuild'))
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name == 'prebuild' && 'prebuild' || 'prebuild-main' }}
name: ${{ github.ref_name == 'prebuild' && 'Ruflet Explorer Prebuild (prebuild)' || 'Ruflet Explorer Prebuild (main)' }}
prerelease: true
make_latest: false
overwrite_files: true
files: ${{ matrix.artifact }}

- name: Publish experimental branch Apple prebuild
if: github.ref_name == 'experimental' && (github.event_name == 'workflow_dispatch' || github.event_name == 'push')
uses: softprops/action-gh-release@v2
with:
tag_name: prebuild-experimental
name: Ruflet Explorer Prebuild (experimental)
prerelease: true
make_latest: false
overwrite_files: true
files: ${{ matrix.artifact }}

- name: Publish versioned experimental Apple client
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
name: Ruflet Explorer ${{ github.ref_name }}
prerelease: true
make_latest: false
overwrite_files: true
files: ${{ matrix.artifact }}

publish-prebuild-manifest:
name: Publish completed prebuild revision
if: >-
Expand All @@ -461,11 +582,13 @@ jobs:
startsWith(github.ref, 'refs/tags/v')))) &&
needs.build-desktop-and-web.result == 'success' &&
needs.build-ios-simulator.result == 'success' &&
needs.build-android.result == 'success'
needs.build-android.result == 'success' &&
needs.build-experimental-apple.result == 'success'
needs:
- build-desktop-and-web
- build-ios-simulator
- build-android
- build-experimental-apple
runs-on: ubuntu-latest
steps:
- name: Write completed-build manifest
Expand Down Expand Up @@ -504,3 +627,38 @@ jobs:
make_latest: true
overwrite_files: true
files: ruflet_explorer-manifest.json

publish-experimental-prebuild-manifest:
name: Publish completed experimental prebuild revision
if: >-
github.ref_name == 'experimental' &&
(github.event_name == 'workflow_dispatch' || github.event_name == 'push') &&
needs.build-experimental-apple.result == 'success'
needs:
- build-experimental-apple
runs-on: ubuntu-latest
steps:
- name: Write completed experimental build manifest
env:
RUFLET_REVISION: ${{ github.sha }}
RUFLET_RUN_ID: ${{ github.run_id }}
RUFLET_REF: ${{ github.ref_name }}
RUFLET_EXPLORER_REF: ${{ env.EXPLORER_REF }}
run: |
ruby -rjson -rtime -e 'File.write("ruflet_explorer-manifest.json", JSON.pretty_generate({schema: 1, revision: ENV.fetch("RUFLET_REVISION"), run_id: ENV.fetch("RUFLET_RUN_ID"), ref: ENV.fetch("RUFLET_REF"), explorer_ref: ENV.fetch("RUFLET_EXPLORER_REF"), completed_at: Time.now.utc.iso8601}))'

- name: Upload completed experimental manifest
uses: actions/upload-artifact@v5
with:
name: ruflet-client-experimental-prebuild-manifest
path: ruflet_explorer-manifest.json

- name: Mark experimental prebuild channel complete
uses: softprops/action-gh-release@v2
with:
tag_name: prebuild-experimental
name: Ruflet Explorer Prebuild (experimental)
prerelease: true
make_latest: false
overwrite_files: true
files: ruflet_explorer-manifest.json
84 changes: 75 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ no rebuild in between. It is itself written in Ruflet.
Broken edits keep the last working interface visible while Ruflet reports the
error in the terminal.
- **Server-driven or self-contained.** Keep application logic on a Ruby server,
or embed the Ruby runtime and app into a native package with `--self`.
or embed either the small Ruflet VM (`--lite`) or a full CRuby distribution
with the project's locked gems (`--full`).
- **Real device capabilities.** Work with camera, location, motion, storage,
sharing, haptics, sensors, and protected native permissions through Ruby APIs.
- **Targeted live updates.** Update mounted controls without rebuilding the
Expand Down Expand Up @@ -119,13 +120,37 @@ ruflet run --web
ruflet run --desktop
```

On macOS, launch the prebuilt experimental client in the already-booted iOS
Simulator with either spelling:

```bash
ruflet run --experimental
ruflet run --exp
```

The first run downloads the experimental Ruflet Explorer simulator build from
the selected client release channel. Later runs reuse the versioned local
cache, install it on the currently booted simulator, and pass the Ruby
backend URL at launch.

To run the same experimental client as a native macOS desktop app, use:

```bash
ruflet run --desktop --exp
```

Its macOS prebuild is downloaded into a separate cache from the standard
Flutter desktop client and receives the local Ruby backend URL at launch.

### Development targets

| Target | Command | What happens |
| --- | --- | --- |
| Mobile | `ruflet run` | Starts the backend and prints a connection QR code. |
| Experimental iOS | `ruflet run --experimental` | Downloads/reuses and launches the native Apple Explorer in the booted simulator. |
| Web | `ruflet run --web` | Starts the backend and opens the Ruflet web client. |
| Desktop | `ruflet run --desktop` | Starts the backend and launches the host desktop client. |
| Experimental macOS | `ruflet run --desktop --exp` | Downloads/reuses and launches the native Apple desktop Explorer. |

Hot reload is enabled by default. Press `r` for a manual Ruby UI reload or `R`
for a complete backend restart. The current route survives a reload; in-memory
Expand All @@ -137,7 +162,8 @@ wanted.
| Mode | Best for | How it works |
| --- | --- | --- |
| **Server-driven** | Connected applications, shared business logic, Rails, and centrally deployed updates | The Ruflet client connects to a Ruby backend and receives UI patches over its live connection. |
| **Self-contained** | Native distribution and local or offline Ruby execution | Ruflet packages the Ruby runtime, application files, and the Ruflet client together with `ruflet build <target> --self`. |
| **Self-contained lite** | Small native packages and Ruflet's supported embedded API | `--lite` packages precompiled application bytecode with the Ruflet mruby VM. |
| **Self-contained full** | CRuby compatibility and project Gemfile dependencies | `--full` packages the locked bundle and selects a target-specific full CRuby runtime distribution. |

Both modes use the same Ruby controls and event handlers.

Expand Down Expand Up @@ -334,7 +360,7 @@ accepted under `build`, which older projects use, and under a platform section
that overrides it — see [Per-platform icon and splash](#per-platform-icon-and-splash).

`backend_url` is required for server-driven production builds, and ignored when
building with `--self`. Clients that are told which server to use at launch may
building with `--lite`, `--full`, or the legacy `--self` alias. Clients that are told which server to use at launch may
omit it: a web client resolves the origin it is served from, and a desktop
client takes the URL its launcher passes. Baking one in would pin them to a
single host and port.
Expand Down Expand Up @@ -397,16 +423,54 @@ ruflet build linux
ruflet build web
```

Add `--self` to package the Ruby runtime and application into a native build:
Choose a self-contained runtime profile for a native build:

```bash
ruflet build apk --self
ruflet build ios --self
ruflet build apk --lite
ruflet build ios --lite
ruflet build macos --full
```

`ruflet build ios --self` prepares both the physical-device and simulator app
`--lite` uses Ruflet's compact mruby VM and precompiles project Ruby to bytecode
when the matching compiler is available. `--self` remains a compatibility alias
for `--lite`.
Use `--self --full` when you want the legacy self-contained flag together with
the full profile; the explicit `--full` selection wins.

`--full` uses CRuby and packages `Gemfile`, `Gemfile.lock`, and the locked
production gem bundle. Full runtime binaries are platform-specific. Point
`RUFLET_FULL_RUNTIME_PATH` (or `build.full_runtime_path` in `ruflet.yaml`) at a
Flutter `ruby_runtime` package whose `ruflet-full-runtime.json` declares the
target platform. Ruflet rejects a missing or mruby-only runtime instead of
silently producing a build labeled CRuby.

All embedded profiles write a content-addressed runtime manifest. Android
extracts the project only once per installed build, while Apple and desktop
read bundled files directly. The VM starts in parallel with Flutter: before
`Application.onCreate` on Android, at dynamic-library load on Apple, and during
plugin registration on Linux and Windows.

The included CRuby builders currently produce tested Android and macOS
distributions. See [runtime profile measurements](docs/runtime_profiles.md) for
per-device mobile size, warm launch, and memory comparisons. Other full-runtime
targets are rejected until a matching CRuby distribution is supplied.

`ruflet build ios --lite` prepares both the physical-device and simulator app
bundles. You do not need a separate simulator build command.

On iOS and macOS, add `--experimental` to build from the experimental Apple
release channel. The ordinary build configuration still selects services,
extensions, permissions, identity, assets, and runtime mode:

```bash
ruflet build ios --experimental
ruflet build macos --lite --exp
```

The build resolves services and extensions before Flutter package resolution,
keeping only the declared Flutter extension packages and removing stale
external extension registrations before `flutter pub get` and bundling.

Install the latest mobile build on a connected device:

```bash
Expand Down Expand Up @@ -475,13 +539,13 @@ would not exist without it. Thank you.
```text
ruflet --version
ruflet new <appname>
ruflet run [scriptname|path] [--web|--desktop] [--port PORT] [--no-reload]
ruflet run [scriptname|path] [--web|--desktop] [--experimental|--exp] [--port PORT] [--no-reload]
ruflet debug [scriptname|path]
ruflet doctor [--fix] [--verbose]
ruflet devices
ruflet emulators
ruflet update [web|desktop|all] [--check] [--force] [--platform PLATFORM]
ruflet build <apk|android|ios|aab|web|macos|windows|linux> [--self] [--verbose]
ruflet build <apk|android|ios|ipa|aab|web|macos|windows|linux> [--lite|--full|--self] [--experimental|--exp] [--verbose]
ruflet install [--device DEVICE_ID] [--verbose]
```

Expand All @@ -500,6 +564,8 @@ ruflet install [--device DEVICE_ID] [--verbose]
and live client connections.
- [`ruflet_rails`](packages/ruflet_rails/README.md) — Rails mounting,
generators, native builds, and WebView integration.
- [`ruflet_record`](packages/ruflet_record/README.md) — a compact, lazy SQLite
ORM for CRuby and Ruflet's embedded mruby runtime.
- `ruby_runtime` — embedded Ruby runtime for self-contained native builds.
- [`ruflet_explorer`](https://github.com/AdamMusa/ruflet_explorer) — the preview
client, written in Ruflet and built by this repository's release workflow.
Expand Down
Loading
Loading