{"id":50721980,"url":"https://github.com/sazardev/fugo","last_synced_at":"2026-06-10T01:01:21.715Z","repository":{"id":363190553,"uuid":"1262275308","full_name":"sazardev/fugo","owner":"sazardev","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-07T20:06:06.000Z","size":48,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-07T21:23:09.079Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Makefile","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sazardev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-07T19:47:35.000Z","updated_at":"2026-06-07T20:06:10.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/sazardev/fugo","commit_stats":null,"previous_names":["sazardev/fugo"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/sazardev/fugo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sazardev%2Ffugo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sazardev%2Ffugo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sazardev%2Ffugo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sazardev%2Ffugo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sazardev","download_url":"https://codeload.github.com/sazardev/fugo/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sazardev%2Ffugo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34132030,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-10T01:01:21.121Z","updated_at":"2026-06-10T01:01:21.698Z","avatar_url":"https://github.com/sazardev.png","language":"Makefile","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cpicture\u003e\n  \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"assets/logo.svg\"\u003e\n  \u003cimg alt=\"Fugo\" src=\"assets/logo.svg\" width=\"64\" height=\"64\"\u003e\n\u003c/picture\u003e\n\n# Fugo\n\n**Server-Driven UI framework for desktop applications — write your logic in Go, render with Flutter.**\n\n[![Go Version](https://img.shields.io/badge/Go-1.26.3-blue?logo=go)](https://go.dev)\n[![Flutter](https://img.shields.io/badge/Flutter-3.24+-blue?logo=flutter)](https://flutter.dev)\n[![gRPC](https://img.shields.io/badge/gRPC-bidirectional-purple)](https://grpc.io)\n[![Protobuf](https://img.shields.io/badge/Protobuf-typed-orange)](https://protobuf.dev)\n[![UDS](https://img.shields.io/badge/UDS-5%E2%80%9310%C2%B5s-brightgreen)](#)\n[![License](https://img.shields.io/badge/license-MIT-green)](#)\n[![Version](https://img.shields.io/badge/version-0.4.0-brightgreen)](VERSION)\n[![go install](https://img.shields.io/badge/go%20install-cmd%2Ffugo-00ADD8?logo=go)](#installation)\n\n---\n\n## What is Fugo?\n\nFugo is a **local Server-Driven UI (SDUI)** framework that lets you build native desktop applications writing **exclusively in Go**. Business logic, state management, and routing live entirely in a Go process, while a precompiled Flutter engine acts as a pure rendering terminal — communicating over **Unix Domain Sockets** (TCP on Windows) via **gRPC** with **Protocol Buffers**.\n\n```\n┌──────────────────────┐     IPC (UDS/TCP)   ┌──────────────────────┐\n│      Go Process      │◄══════════════════►│   Flutter Process     │\n│                      │   gRPC + Protobuf   │                      │\n│  ┌────────────────┐  │                      │  ┌────────────────┐  │\n│  │ Business Logic   │  │   Widget Tree Diff  │  │ Widget Registry│  │\n│  │ Retained Tree   │──┼────────────────────►│  │ Render Pipeline│  │\n│  │ Diffing Engine  │  │                     │  │ Event Debouncer│  │\n│  │ gRPC Server     │  │   User Events       │  │ gRPC Client    │  │\n│  └────────────────┘  │◄─────────────────────│  └────────────────┘  │\n└──────────────────────┘                      └──────────────────────┘\n```\n\n**Go is the absolute source of truth.** Flutter is a dumb terminal — no business logic, no state, just pixels at 60/120 fps via Impeller.\n\n---\n\n## Why Fugo?\n\n| Problem | Fugo's Answer |\n|---------|---------------|\n| Electron apps consume \u003e150MB RAM | Native rendering via Flutter/Impeller, no Chromium |\n| Go GUI libraries (Fyne, Gio) lack widget ecosystem | Flutter's world-class typography, layout, animations |\n| Flutter forces you into Dart for everything | Write all logic in Go, use any Go library |\n| Remote SDUI suffers 50-200ms network latency | Local IPC via UDS: **5-10µs** round-trip |\n| JSON parsing kills frame budgets | Compact **Protobuf** framing; only diffs cross the wire |\n\n---\n\n## Installation\n\nInstall the `fugo` CLI straight from source (requires **Go 1.26+**):\n\n```bash\ngo install github.com/sazardev/fugo/cmd/fugo@latest\n```\n\nThis drops the `fugo` binary in `$(go env GOPATH)/bin` — make sure that's on your `PATH`, then:\n\n```bash\nfugo --version\nfugo doctor      # checks the toolchain + (in a project) its health\n```\n\nThe generated protobuf bindings are **committed**, so a clean module fetch compiles without `protoc` or any code-gen step.\n\n\u003e **Rendering prerequisite.** `fugo init`, `fugo doctor` and `fugo widgets` work standalone. But because Fugo renders through a precompiled **Flutter** client, `fugo run` / `fugo build` additionally require the [Flutter SDK](https://docs.flutter.dev/get-started/install) with desktop support enabled. The CLI builds the render client on first `run`; alternatively, point **`FUGO_FLUTTER_BINARY`** at a prebuilt client binary. `go install` ships the Go CLI only — not the Flutter engine.\n\n**From a clone** (e.g. to hack on the framework):\n\n```bash\ngit clone https://github.com/sazardev/fugo \u0026\u0026 cd fugo\ngo build ./cmd/fugo      # or: make cli   (Go bindings are committed; no protoc needed)\n```\n\n---\n\n## Quick Start\n\n```go\npackage main\n\nimport (\n\t\"strconv\"\n\n\t\"github.com/sazardev/fugo\"\n\t\"github.com/sazardev/fugo/fg\"\n)\n\nfunc main() {\n\tfugo.RunStandalone(fugo.AppOptions{\n\t\tTitle:  \"Fugo Desktop\",\n\t\tWidth:  800,\n\t\tHeight: 600,\n\t}, buildUI)\n}\n\nfunc buildUI(ctx *fugo.Context) fg.Widget {\n\tcounter := 0\n\tcounterText := fg.Text(\"0\").FontSize(48)\n\n\tincBtn := fg.Button(\"+\").\n\t\tBgColor(fg.Hex(\"#10B981\")).\n\t\tFontSize(20).\n\t\tOnClick(func(_ fg.Event) {\n\t\t\tcounter++\n\t\t\tcounterText.SetText(strconv.Itoa(counter))\n\t\t\tctx.Update() // mark dirty → diff → patch streamed to Flutter\n\t\t})\n\n\treturn fg.Container(\n\t\tfg.Column(\n\t\t\tcounterText,\n\t\t\tfg.SizedBox(0, 16),\n\t\t\tincBtn,\n\t\t),\n\t).BgColor(fg.Hex(\"#1A1A2E\")).Pad(fg.EdgeAll(24))\n}\n```\n\nThe widget tree is **built once and retained**. Event handlers are Go closures that mutate\nwidget fields in place (e.g. `counterText.SetText(...)`) and call `ctx.Update()`; the scheduler\nre-walks the same tree each frame, diffs it, and streams only the patches.\n\n\u003e Constructors are **prefix-free**: `fg.Text(...)`, `fg.Button(...)`, `fg.Container(...)` —\n\u003e not `NewText`. Each returns a concrete `*fg.TextWidget` / `*fg.ButtonWidget` / … with\n\u003e chainable setters.\n\n---\n\n## Theming \u0026 Material 3\n\nFugo renders with **Material 3** and a **light** color scheme by default. The active `fg.Theme`'s\nprimary color seeds Flutter's `ColorScheme.fromSeed`, so widgets get native M3 colors\nautomatically — a `fg.FilledButton` looks like a real filled button without setting any color.\nPer-widget setters still override the theme.\n\n```go\nfg.UseTheme(fg.DarkTheme()) // light is active by default — call before RunStandalone\n\nt := fg.CurrentTheme()\nfg.Text(\"Title\").FontSize(t.Typography.Heading)\nfg.SizedBox(0, t.Spacing.LG)\n```\n\n**Buttons** mirror Material 3 — `fg.FilledButton`, `fg.FilledTonalButton`, `fg.OutlinedButton`,\n`fg.TextButton`, `fg.ElevatedButton`, `fg.IconButton` (and `fg.Button`, an alias of\n`FilledButton`). Other native Material widgets: `fg.Card`, `fg.Scaffold`, `fg.AppBar`\n(title + `.Leading` / `.Actions`), `fg.FloatingActionButton`, `fg.ListTile`, `fg.Chip`, and\n`fg.ProgressCircular` / `fg.ProgressLinear`, `fg.NavigationBar`, and `fg.Tabs` (a `TabBar` +\n`TabBarView`, switched client-side) — plus `fg.Tooltip`, `fg.Badge`, `fg.CircleAvatar`,\n`fg.SegmentedButton`, `fg.Spacer`, `fg.AspectRatio`, `fg.ClipRRect`, `fg.FittedBox`, `fg.Flexible`,\n`fg.ExpansionTile`, `fg.PopupMenuButton`, `fg.RichText`, `fg.DataTable`, and `fg.Stepper`. A scaffold\ncomposes them —\nan app bar, the body, a FAB, a slide-in `.Drawer`, and a bottom `.BottomBar`:\n\n```go\nfg.Scaffold(body).\n    AppBar(fg.AppBar(\"Inbox\").Actions(fg.IconButton(fg.Icons.Search))).\n    Drawer(fg.Column(fg.ListTile(\"Home\").Leading(fg.Icons.Home))).\n    BottomBar(fg.NavigationBar().\n        Item(fg.Icons.Home, \"Home\").\n        Item(fg.Icons.Person, \"Profile\").\n        OnChange(func(e fg.Event) { /* e.Data is the selected index */ })).\n    FAB(fg.FloatingActionButton(fg.Icons.Add))\n```\n\nA bare `fg.Column` (or any intrinsically-sized root) auto-centers in the window; wrap a region in\n`fg.Scaffold`/`fg.Container` to fill it instead. Tokens live under `Colors` (Primary, Surface,\nOnSurface, Muted, Border, …), `Typography` (Heading/Body/Caption), `Spacing` (XS→XL), and\n`Radius` (SM/MD/LG).\n\n### Skip the boilerplate: `fg.Icons`, `fg.Colors`, `fg.TextSize`\n\nUse Flutter's constants instead of hand-written strings, hex, and magic numbers:\n\n```go\nfg.IconButton(fg.Icons.Favorite)                       // ~2,200 Material icons: fg.Icons.Home, .Coffee, .Settings…\nfg.Container(child).BgColor(fg.Colors.Amber)           // the Material palette: fg.Colors.Blue, .RedAccent, .Grey800…\nfg.Text(\"Title\").FontSize(fg.TextSize.HeadlineMedium)  // the M3 type scale: .DisplayLarge, .BodyMedium…\n```\n\n`fg.Icons.*` mirrors Flutter's `Icons` (generated from the installed SDK via `go run ./cmd/gen-icons`); `fg.Colors.*` mirrors `Colors`; `fg.TextSize.*` is the Material 3 type scale.\n\n---\n\n## Tech Stack\n\n| Layer | Technology | Why |\n|-------|-----------|-----|\n| **Language** | Go 1.26+ | Goroutines, strong ecosystem, systems-level performance |\n| **Rendering** | Flutter 3.24+ / Impeller | 60/120 fps native, world-class layout engine |\n| **IPC Transport** | Unix Domain Sockets (TCP fallback on Windows) | 5-10µs latency, kernel-level throughput |\n| **RPC** | gRPC bidirectional streaming | Typed contracts, health checking, keepalive |\n| **Serialization** | Protocol Buffers (`google.golang.org/protobuf`) | Per-widget props marshaled as nested protobuf inside each node |\n| **Wire updates** | Tree diff (ID/positional) | Only changed nodes stream as patches, never the full tree |\n| **Process Mgmt** | `os/exec` + signals | Subprocess lifecycle, zombie prevention |\n| **Window Mgmt** | `window_manager` | Cross-platform frameless windows, custom chrome |\n\n---\n\n## Current Status\n\n**Version 0.4.0 — engine + widget API + transport + CLI + Flutter client are implemented and run end-to-end, the CLI is installable via `go install`, and the client renders native Material 3.**\n\n- [x] Installable: `go install github.com/sazardev/fugo/cmd/fugo@latest` (generated protobuf bindings committed; builds on a clean fetch)\n- [x] Native **Material 3** (light by default), seeded from `fg.Theme`; Material button variants (Filled/Tonal/Outlined/Text/Elevated/Icon) + Card/Scaffold/FAB/ListTile/Chip/Progress\n- [x] Diffing engine, reconciler, 60 fps scheduler with priority (`Update` / `UpdateNow`)\n- [x] gRPC transport (UDS / TCP on Windows), health check, keepalive, opt-in auth token\n- [x] 36+ widgets in `fg/` with a fluent, prefix-free API + a `Theme` system\n- [x] Flutter render client (background gRPC isolate, widget registry, auto-reconnect)\n- [x] CLI: `fugo init` (templates) / `run` (hot reload by default) / `build` / `doctor` (`--fix`) / `widgets` / `upgrade` (self-update)\n- [x] Runtime window control (`Context.Window()`), `window_manager`-backed\n- [x] OS host services: clipboard (`Context.Clipboard()`), native file dialogs (`Context.Files()`)\n- [x] Imperative overlays: `ctx.ShowSnackBar(...)`, `ctx.ShowDialog(...)`\n- [x] Performance: object-pooled diff, GC tuning (`FUGO_GOGC` / `FUGO_GOMEMLIMIT`), Go + Dart benchmarks with a CI perf gate\n\nSee [ROADMAP](./ROADMAP/) and [SPEC.md](./SPEC.md) for the full design vision. **Note:** the\nroadmap describes a FlatBuffers transport; the shipped implementation uses standard\n**Protocol Buffers** (`google.golang.org/protobuf`) instead — per-widget props are a protobuf\nmessage marshaled into each node's `bytes` field. `CLAUDE.md` is the canonical, up-to-date guide.\n\n---\n\n## Packages\n\n```\nfugo/                   # App, Context, lifecycle (RunStandalone, scheduler)\n├── fg/                 # Declarative widgets (fg.Container, fg.Text, fg.Button, ...) + Theme\n├── style/              # Styling primitives (Color, EdgeInsets, TextStyle, Border, ...)\n├── engine/             # Diffing engine, Reconciler, Scheduler (16ms tick)\n├── transport/          # gRPC server (UDS/TCP), health, keepalive\n├── supervisor/         # Flutter subprocess lifecycle, signals\n└── flutter_client/     # Precompiled Flutter rendering client\n```\n\n---\n\n## CLI\n\n```bash\nfugo init \u003cname\u003e          # Scaffold a project (use --template app for a themed multi-page starter)\nfugo run                  # Build + run; hot-reloads on .go changes (window stays open). Auto-builds the Flutter client the first time.\nfugo run --no-watch       # Build and run once, without hot reload\nfugo build                # Build + bundle the Flutter client into a self-contained dist/\nfugo doctor               # Check the toolchain; inside a project, validate fugo.toml + structure + that it compiles\nfugo upgrade              # Self-update the CLI to the latest release (go install ...@latest)\nfugo --version            # Print version information\n```\n\n`fugo init` scaffolds a **recommended layout** and initializes a git repo (initial commit; skip with `--no-git`):\n\n```\nmyapp/\n├─ main.go        # entrypoint: sets the theme, then fugo.RunStandalone(fugo.ConfigOptions(\"fugo.toml\"), ui.Build)\n├─ ui/            # your screens (package ui); ui.Build is the root widget\n│  └─ home.go\n├─ fugo.toml      # window title/size + gRPC address — read by the CLI and the app\n├─ bin/           # dev builds        (gitignored)\n├─ dist/          # release bundle    (gitignored)\n├─ logs/          # fugo run → logs/run.log (gitignored)\n├─ README.md\n└─ .gitignore\n```\n\nEdit **`fugo.toml`** to change the window or server address — no recompiling the config into Go:\n\n```toml\nname = \"myapp\"\n\n[window]\ntitle  = \"My App\"\nwidth  = 800\nheight = 600\n\n[server]\naddr = \"127.0.0.1:9510\"   # fugo run uses this unless you pass --addr\n```\n\n**Hot reload is on by default**: `fugo run` watches `.go` files and rebuilds the Go server on every\nchange while the Flutter window stays open and reconnects (~500ms), so your edits show up live. The\nin-memory state resets across reloads — full state restore would need a managed-state layer and is\nnot implemented yet. Use `fugo run --no-watch` for a single build-and-run.\n\n**Stateful components** are an alternative to a buildUI closure — implement `Render(ctx)` and pass\nthe value to `fugo.RunComponent`. **Routing** supports `:params` (e.g. `/user/:id`), read with\n`ctx.Param(\"id\")`. Set **`FUGO_AUTH=1`** to mint a per-run token that hardens the local transport.\n\n**OS host services** run on the client and answer asynchronously: `ctx.Clipboard().Write/Read`,\n`ctx.Files().Open/Save(fg.FileDialog{...}, func(path string){...})`. The callback runs on the\nevent goroutine, so mutate widgets and call `ctx.Update()` from it like any handler. For frameless\nwindows, wrap a region in **`fg.WindowDragArea(...)`** to make it drag the window, and use\n**`fg.AnimatedPositioned(...)`** inside a `Stack` to animate a child between positions.\n\n**Overlays** are imperative, driven from Go over the same command channel: `ctx.ShowSnackBar(\"Saved\")`\n(snackbar), `ctx.ShowDialog(\"Title\", \"Message\")` (alert dialog), and `ctx.ShowBottomSheet(\"Title\",\n\"Message\")` (bottom sheet). The native pickers return a value to a callback: `ctx.PickDate(func(d string){…})`\n(ISO `YYYY-MM-DD`) and `ctx.PickTime(func(t string){…})` (`HH:MM`), empty if cancelled.\n\n---\n\n## Design Principles\n\n- **Go is the source of truth** — all logic, state, and routing in Go\n- **No shared memory** — strict message passing via gRPC\n- **Stream only diffs** — ID/positional tree diffing, patches over gRPC, never full re-renders\n- **Opinionated on state, themed by default, unopinionated on design system**\n- **Performance is a requirement, not an afterthought**\n- **Terminal-native DX** — `fugo init` → `fugo run` → `fugo build`\n\n---\n\n## License\n\nMIT — see [LICENSE](./LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsazardev%2Ffugo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsazardev%2Ffugo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsazardev%2Ffugo/lists"}