Kobo 现在可以运行应用程序了。
Kobo can run apps now

原始链接: https://bandarlabs.github.io/Cobalt/

Kobo SDK 为在电子墨水(E-ink)设备上构建应用提供了声明式框架。它负责处理布局、电子墨水刷新周期、生命周期事件和返回导航等核心复杂问题,使开发者能够专注于应用逻辑。 主要特性包括: * **功能门控访问**:应用不直接访问硬件,而是请求资源(网络、存储等),由运行时安全地处理权限结果。 * **强大的工具链**:包含用于布局诊断的模拟器,支持异步操作(HTTPS、可取消任务)以及原子键值存储。 * **部署**:应用以已签名的静态 ARMv7 二进制文件形式交付,通过简单的命令行接口(`kobo new`、`kobo dev`)集成到开发工作流中。 该 SDK 使用基于特征(trait)的模式(`KoboApp`),开发者通过 `ScreenBuilder` 定义状态并实现 UI 更新。这种架构确保了设备的独特显示限制和导航模式由运行时自动处理,同时让开发者能够编写简洁、模块化的代码。

**Cobalt** 项目在 Hacker News 上引发了一场热烈讨论,其目标是将 Kobo 电子书阅读器转变为类似 Android 的“应用平台”。虽然目前已有 **NickelMenu** 和 **KOReader** 等成熟方案来扩展 Kobo 功能,但 Cobalt 的开发者通过提供一个简化应用开发、沙盒化及非特权进程管理的 SDK,将其项目与现有方案区分开来。 社区对此反应两极分化。支持者赞赏在电子墨水屏上运行自定义应用(如 ArXiv 阅读器、国际象棋游戏或 RSS 客户端)的能力,无需依赖全功能的笔记本电脑。他们认为这体现了探索硬件极限的“黑客精神”。 相反,许多用户表示怀疑,认为电子书阅读器应保持专注且无干扰的单一用途。讨论中很大一部分内容演变成了对“氛围编程”(vibe-coding)的批评,即作者大量使用大语言模型(LLM)来编写文档和项目文案。批评者认为,这种由机器生成的语气令人感到“反感”且缺乏个性,削弱了项目的可信度。尽管在实现方式和营销上存在争议,但该项目已成功吸引了足够的关注,并推动了资深用户群体中的新销售和协作。
相关文章

原文

The SDK

Implement KoboApp, describe screens declaratively, and the runtime handles layout, e-ink refresh planning, Back navigation and lifecycle.

Apps don't open device resources; they ask. Network, storage, audio, frontlight and Wi-Fi are capability-gated, and a refusal comes back as a value the app can handle.

E-ink UI
Text, tiles, dialogs, keyboards, pagination, partial refresh planning

Simulators
Browser and runtime simulators with layout diagnostics

Async work
HTTPS, ranged downloads, cancellable tasks, scheduled wakes

State
Atomic per-app keyed storage

Shipping
Signed static ARMv7 binaries, published when an app PR merges

kobo new my-app
cd my-app
kobo dev

Read the SDK docs

use kobo_sdk::{
    ActionId, Context, KoboApp, ScreenBuilder,
};

#[derive(Default)]
struct Hello { taps: u32 }

impl KoboApp for Hello {
    fn on_start(&mut self, ctx: &mut Context) {
        self.show(ctx);
    }

    fn on_action(
        &mut self, ctx: &mut Context, a: ActionId,
    ) {
        if a == kobo_sdk::action_id("tap") {
            self.taps += 1;
        }
        self.show(ctx);
    }
}

impl Hello {
    fn show(&self, ctx: &mut Context) {
        let screen = ScreenBuilder::new("hello")
            .top_bar("Hello")
            .heading(format!("{} taps", self.taps))
            .button("tap", "Tap me")
            .build();
        ctx.set_screen(screen);
    }
}

fn main() {
    let app = Hello::default();
    let _ = kobo_sdk::run("hello", app);
}
联系我们 contact @ memedata.com