全部六个平台,功能完全对等
当我们发布 Perry 原生 UI 系统的第一个版本时,"跨平台" 意味着 macOS 运行良好,而其他五个平台还只是存根。如今,在 v0.2.162 版本中,情况 不再如此。所有六个平台 —— macOS、iOS、iPadOS、Android、Linux 和 Windows —— 现在 共享完整的功能对等。相同的 TypeScript 代码在每个目标上编译为原生组件。
This post walks through what we shipped between v0.2.152 and v0.2.164: a Canvas widget, a full NSTableView implementation, 20+ total UI widgets, the perry/system module, multi-window support, system notifications, keychain access, automatic binary size reduction, and a compile-time plugin system. A lot happened.
组件冲刺:20+ 个原生 UI 组件
The biggest single jump came in v0.2.155, which landed 20+ UI widgets across all platforms. Perry's TypeScript UI API now covers the components you actually need to ship a real app:
- Layout — VStack, HStack, ZStack, LazyVStack, ScrollView, SplitView
- Input — Button, TextField, TextEditor, Checkbox, Toggle, Slider, Picker
- Display — Text, Label, Image, ProgressView, Divider, Spacer
- Data — List, Table (NSTableView / GTK4 TreeView / Win32 ListView)
- Overlay — Alert, Sheet, Popover, Toolbar, NavigationBar
- Drawing — Canvas (2D drawing API, hardware-accelerated per platform)
These aren't wrappers around a custom renderer. Each widget compiles to the platform's own native component: NSButton on macOS, UIButton on iOS, GtkButton on Linux, android.widget.Button on Android via JNI, and CreateWindowEx on Windows. The OS draws them, themes them, and handles accessibility — Perry just wires up the TypeScript API.
Canvas:从 TypeScript 进行 2D 绘图
One of the more technically interesting additions is the Canvas widget (v0.2.152). It exposes a familiar 2D drawing API directly from TypeScript — bezier curves, fills, strokes, image blitting — and compiles to the platform's accelerated 2D backend: Core Graphics on macOS/iOS, Cairo on Linux, Direct2D on Windows, and Skia on Android.
import { Canvas, Color } from 'perry/ui';
// Compiles to Core Graphics on macOS, Cairo on Linux, etc.
const canvas = new Canvas({ width: 400, height: 300 });
canvas.onDraw((ctx) => {
ctx.fillStyle = Color.amber;
ctx.fillRect(10, 10, 100, 60);
ctx.strokeStyle = Color.blue;
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(200, 150, 80, 0, Math.PI * 2);
ctx.stroke();
});
Table 组件:NSTableView 来到 TypeScript
v0.2.163 landed the Table widget — the most complex component in the library. On macOS it maps to NSTableView with full delegate/data source wiring. On Linux it uses GTK4's GtkTreeView. On Windows, Win32's ListView control. On Android it binds to RecyclerView through JNI.
The TypeScript API is declarative: you define columns, provide a data source, and Perry handles the platform-specific wiring at compile time. Column sorting, selection handling, and row height customization all work out of the box.
import { Table, Column } from 'perry/ui';
const table = new Table({
columns: [
new Column({ title: "Name", key: "name", width: 200 }),
new Column({ title: "Size", key: "size", width: 80 }),
],
rows: files, // TypeScript array of objects
onSelect: (row) => console.log(row.name),
});
perry/system 模块
v0.2.155 also introduced perry/system — a TypeScript module that exposes platform system APIs without any runtime: file dialogs, save dialogs, alerts, sheets, keychain access, system notifications, and multi-window management.
system.showOpenDialog()— native file picker (NSOpenPanel / GTK FileChooser / Win32 OPENFILENAME)system.showSaveDialog()— native save dialogsystem.showAlert()— native alert panelsystem.notify()— OS notification (UserNotifications / libnotify / WinRT)system.keychain.get/set()— Keychain Services / Secret Service / Windows Credential Storesystem.openWindow()— multi-window management
All of these call native platform APIs directly — no Electron IPC, no web view bridge. Perry compiles the TypeScript call site to a direct native function call into the platform SDK.
六平台功能对等:v0.2.162
The v0.2.162 milestone was about closing gaps. Before this release, macOS had the fullest feature set, iOS was mostly there, and Linux/Windows/Android lagged. v0.2.162 brought all six platforms to the same level:
- macOS — AppKit, complete widget set, Keychain, notifications, multi-window, toolbar
- iOS / iPadOS — UIKit, full widget parity with macOS, scene lifecycle
- Android — JNI bridge, all widgets via Android Views, NDK cross-compilation
- Linux — GTK4, full widget set including Table, file dialogs, libsecret keychain
- Windows — Win32, all widgets, Windows Credential Store, WinRT notifications
This is the milestone that makes "one codebase, six platforms" real rather than aspirational. The same TypeScript file compiles to native apps on all six targets with no platform-specific code paths required for common use cases.
自动减小二进制大小
v0.2.153 shipped automatic binary size reduction — the compiler now aggressively dead-strips unused code paths, eliminates unreachable stdlib functions, and deduplicates symbol definitions during linking. A typical CLI tool that previously compiled to ~4 MB now comes in under 2 MB with zero changes to your source.
This matters for real deployments. When your binary is the unit of deployment — copied to a server, distributed as a single file, embedded in a container — size directly affects transfer time and storage cost. Halving the binary size for free is a meaningful improvement.
编译时插件系统
v0.2.152 introduced Perry's plugin system — and it's architecturally unlike every other plugin system in the TypeScript ecosystem. There's no runtime plugin loading, no IPC, no dynamic require(). Plugins are TypeScript modules that Perry resolves and compiles at build time.
The result: plugins have exactly zero runtime overhead. They compile into the same binary as your application code, with direct function calls between plugin code and host code. If you don't use a plugin, it doesn't appear in your binary at all. If you do use it, it's inlined like any other module.
We wrote about the philosophy behind this in Plugin Systems Are a Performance Tax. The short version: runtime plugin architectures trade performance for extensibility. Build-time composition gives you both.
语言改进
The UI sprint didn't happen in isolation — the compiler itself kept getting more capable. Across these releases:
- Class expressions —
const Foo = class extends Bar {}now compiles correctly - Generator transforms —
function*andyieldcompile to native state machines - Map/Set as class fields —
private items = new Map()works in codegen - FFI param type coercion — native library calls handle type coercion automatically
- Bound method references —
this.methodreferences work for native modules (fs, os, path) string.match()— now fully supportedpath.isAbsolute(), multi-argpath.join(),path.resolve()- Web target — Perry can now compile to a web-compatible output for hybrid deployments
下一步
With six-platform UI parity shipped, the next phase is depth over breadth. We're working on:
- Full RegExp support (
regex.test(),string.matchAll()) - Drag and drop, custom context menus, and accessibility labels in the widget system
- A VS Code extension for Perry diagnostics and compile-on-save
- Package manager integration — install and compile Perry-native packages with one command
- WASM compilation target for browser deployment
- Multi-threading via
Workerthreads
如果你想跟进, Perry repo 已开放。查看 showcase 看看已经在构建的项目,或浏览 roadmap 了解完整情况。