Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 3 additions & 2 deletions Agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

- 仓库根目录:就是 Koka 源码根目录,编译时直接把 repo root 当成模块搜索根。
- `app.kk`:浏览器入口,只暴露 boot、事件桥接和 runtime snapshot 导入导出。
- `explore/react/core.kk`、`action.kk`、`state.kk`:component authoring surface;业务 view 只从这里获取 elements、actions、stores、keyed lifecycle 与 effects。
- `explore/react.kk`:普通组件唯一的 author import,只 re-export `core/action/state`,不包含 runtime/inspection/renderer。
- `explore/react/core.kk`、`action.kk`、`state.kk`:component authoring 实现模块;advanced host/tests 可以按边界显式导入。
- `explore/react/runtime.kk`:browser/app host 使用的 registered callback、scheduled effect 与 snapshot transport。
- `explore/react/inspection.kk`:tests/devtools 使用的 VDOM、event registry 与 runtime entry 只读查询。
- `explore/react/renderer.kk`:host/tests 使用的 render、diff 与 patch;不要导入业务 view。
Expand Down Expand Up @@ -152,7 +153,7 @@ chrome-devtools take_screenshot --fullPage --filePath .tmp-devtools-full.png

组件交互状态以 **typed store + serializable actions** 为默认方案。它保留 React reducer 的简单心智模型,同时满足 Koka 严格类型、HMR 和 snapshot 恢复需求。

- 普通业务 view 只导入 `explore/react/core`、`explore/react/action`、`explore/react/state` 中实际需要的模块;不得导入 `explore/react/runtime`、`explore/react/inspection` 或 `explore/react/renderer`。
- 普通业务 view 统一 `import explore/react`;不得导入 `explore/react/runtime`、`explore/react/inspection` 或 `explore/react/renderer`。`core/action/state` 保持实现分层,但不再要求组件作者理解三条 import。

- 业务组件用 `(state, dispatch) = use_store(spec, initial = ...)` 读取 reducer pair,不额外暴露 binding record。
- UI 事件优先用 `on_store_click(name, action = ..., dispatch = ...)` / `on_store_input(...)` 发送 typed action,不直接操作 state tree。
Expand Down
39 changes: 21 additions & 18 deletions PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@
### 业务组件优先使用

```koka
import explore/react

div(children, class = ..., key = ...)
button(text, class = ..., click = ...)
input_text(value, input = ..., enter = ..., placeholder = ...)
Expand Down Expand Up @@ -155,31 +157,28 @@ feature_dom_marker(group = ..., key = ..., name = ...)

按用户风险与依赖顺序推进,不并行扩张公共 API:

### 1. 版本化 runtime snapshot(#17)
### 1. 单一 component-author import(#23)

- 当前实现批次:完整 snapshot 已增加 `respo/runtime-snapshot|1` header,并兼容读取现有无 envelope 格式;
- unknown/malformed 顶层版本回退空树,malformed entry 与 entry 非法编码只丢弃自身,不阻断 app boot;
- entry 级 schema/version 校验保持不变,业务 view 不增加 snapshot 参数或 runtime import;
- Koka compatibility tests、旧 snapshot 浏览器启动、整页 reload 与真实 Vite HMR replacement 恢复均已完成验收。
- 当前实现批次:新增 `import explore/react`,只 re-export `core/action/state`;
- 真实 demo business modules 迁移到单一入口,advanced host/tests 继续显式依赖 runtime/inspection/renderer;
- `check:author-surface` 固定 re-export allowlist,并禁止业务模块退回三条内部 imports;
- quick start、component author API、README 与开发指南同步单一入口。

### 2. 定义 effect cleanup 生命周期(#18)
### 2. 可运行 first-component 与错误路径(#24、#25)

- 当前实现批次:`state_effect(...)` 保持轻量 trailing-lambda,`state_resource(...)` 显式声明 setup + cleanup;
- deps change 先 cleanup 后 setup,ordinary child unmount 与 feature reset 释放对应 resource;
- HMR 固定为 snapshot flush、旧 runtime dispose、新 runtime boot,cleanup closure 不进入 snapshot;
- cleanup failure 记录 host error,但不阻断下一次 setup 或新 runtime boot;
- 用一个真实 demo capability、deterministic tests 与浏览器 HMR 回归验证。
- #24 提供 `yarn example:first-component`,让 typed store/action 的首次成功路径可以直接运行;
- #25 将常见 Koka 编译错误整理为 symptom → cause → minimal fix cookbook;
- 文档示例尽量引用持续编译的源码,减少 snippet drift。

### 3. 发布 agent-safe store/action surface(#19)
### 3. 安全 tooling surface(#19、#26)

- 暴露可序列化的只读 catalog,不暴露 raw state entry、closure 或 tree write;
- action dispatch 必须通过已注册 codec 校验,并复用现有 typed reducer/observation 链路;
- 明确区分 domain action、ephemeral component action 与默认不可投递 capability;
- #19 先定义可序列化 catalog 与 validated dispatch,不暴露 raw tree/closure;
- #26 再基于 catalog 提供 dev-only read-only action/store inspector;
- 自动 replay、完整权限 UI 和远程身份认证继续保持非目标。

### 持续约束

- component authoring surface 保持在 `core/action/state`,不增加只做转发的 facade;
- component authoring 实现保持在 `core/action/state`;顶层 `explore/react` 只做 `pub import` allowlist,不新增转发函数;
- controlled component 继续使用“主 domain value 位置参数 + labelled callback/config props”;
- 显式 key/path/store inspection 只有在能移动真实实现且不产生单行 wrapper 时再拆;
- typed listener helper 只在出现第三个真实重复形态时扩展。
Expand All @@ -194,8 +193,12 @@ issue、PR 及影响结论的进度更新统一使用中英双语:标题采用
- [#12 简化组件事件中的 domain 与 local-store transition / Simplify domain and local-store transitions in component events](https://github.com/Respo/explore-react.koka/issues/12):已由 PR #15 合并;四个真实调用点共享 event-independent transition,并覆盖 action 顺序与浏览器回归;
- [#13 发布渐进式组件作者 API / Publish a progressive-disclosure component authoring surface](https://github.com/Respo/explore-react.koka/issues/13):已由 PR #16 合并;四概念 quick start、单页 author API 与 advanced module import 边界已落地;
- [#17 版本化 runtime snapshot 并定义兼容迁移 / Version runtime snapshots and define compatible migration](https://github.com/Respo/explore-react.koka/issues/17):已由 PR #21 合并;snapshot envelope、legacy compatibility 与安全回退已落地;
- [#18 定义组件 effect cleanup 生命周期 / Define the component effect cleanup lifecycle](https://github.com/Respo/explore-react.koka/issues/18):当前实现批次;effect/resource author API、host registry、HMR dispose、测试与文档已完成,等待 PR review;
- [#19 发布 agent-safe store catalog 与校验 action dispatch / Publish an agent-safe store catalog and validated action dispatch](https://github.com/Respo/explore-react.koka/issues/19):后续探索批次;在不暴露 raw runtime tree 的前提下服务 devtools/agents。
- [#18 定义组件 effect cleanup 生命周期 / Define the component effect cleanup lifecycle](https://github.com/Respo/explore-react.koka/issues/18):已由 PR #22 合并;effect/resource author API、host registry、HMR dispose、测试与文档已落地;
- [#23 提供单一组件作者导入入口 / Provide a single component-author import](https://github.com/Respo/explore-react.koka/issues/23):当前开发批次,减少第一次组件所需的框架结构知识;
- [#24 交付可运行的首个组件示例 / Ship a runnable first-component example](https://github.com/Respo/explore-react.koka/issues/24):下一批次,提供可执行且持续编译的首次成功路径;
- [#25 发布组件编译错误 cookbook / Publish a component compile-error cookbook](https://github.com/Respo/explore-react.koka/issues/25):下一批次,按用户看到的错误组织诊断;
- [#19 发布 agent-safe store catalog 与校验 action dispatch / Publish an agent-safe store catalog and validated action dispatch](https://github.com/Respo/explore-react.koka/issues/19):后续探索批次;先稳定人类用户 author surface,再服务 tools/agents;
- [#26 提供只读 action/store 调试面板 / Provide a read-only action/store inspector](https://github.com/Respo/explore-react.koka/issues/26):依赖 #19 的安全 catalog,作为后续调试体验。

## 验证标准

Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ state, and event dispatch.

## Start with component authoring

Ordinary components need one framework import:

```koka
import explore/react
```

This entry exposes elements, typed actions/stores, keyed lifecycle, and effects;
advanced runtime, inspection, and renderer modules remain explicit host/test
imports.

Learn the normal path as four tasks; runtime ownership and snapshot transport
are advanced integration topics, not prerequisites:

Expand Down
3 changes: 1 addition & 2 deletions demo/dialogs.kk
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ module demo/dialogs
import library/dialog
import demo/model
import runtime/dom
import explore/react/core
import explore/react/state
import explore/react

fun apply_dialog_update(owner : model, next_update : dialog_update) : <div> model
owner(dialog = next_update.dialog, notice = next_update.notice)
Expand Down
3 changes: 1 addition & 2 deletions demo/effectspanel.kk
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import demo/model
import demo/tests/support
import demo/shared
import runtime/dom
import explore/react/core
import explore/react/state
import explore/react

fun tiny_task_list(key : string, tasks : list<task>) : <div> vnode
if is-empty(tasks) then p("None", class = "empty") else ul(map(tasks, fn(item) li_text(item.title, key = "tiny-" ++ task/id(item).show)), key = key, class = "tiny-list")
Expand Down
2 changes: 1 addition & 1 deletion demo/lab/events.kk
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import demo/lab/workflow
import library/bridge
import library/maybe
import library/search
import explore/react/action
import explore/react

pub type lab_action
Load_board
Expand Down
3 changes: 1 addition & 2 deletions demo/lab/state.kk
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
module demo/lab/state

import explore/react/action
import explore/react/state
import explore/react

pub struct incident_local_state(expanded : bool, draft : string)
pub type incident_local_action
Expand Down
3 changes: 1 addition & 2 deletions demo/lab/view.kk
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import demo/shared
import demo/lab/state
import library/bridge
import library/search
import explore/react/core
import explore/react/state
import explore/react

fun severity_class(item : incident) : string
match item.severity
Expand Down
2 changes: 1 addition & 1 deletion demo/layout.kk
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import demo/lab/view
import demo/effectspanel
import demo/testspanel
import library/route
import explore/react/core
import explore/react

fun intro_panel(item : model) : <div> vnode
header([
Expand Down
3 changes: 1 addition & 2 deletions demo/model.kk
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import library/bridge
import library/dialog
import library/route
import library/search
import explore/react/action
import explore/react/state
import explore/react

pub type incident_severity
Severity_p1
Expand Down
3 changes: 1 addition & 2 deletions demo/routebar.kk
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ module demo/routebar

import demo/model
import library/route
import explore/react/core
import explore/react/state
import explore/react

fun route_button(label : string, route_name : string, active : bool) : app_view vnode
val class_name = if active then "button route-tab route-tab-active" else "button route-tab"
Expand Down
2 changes: 1 addition & 1 deletion demo/shared.kk
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module demo/shared

import explore/react/core
import explore/react

pub fun panel(children : list<vnode>, title : string, note : string, key : string, class_name : string) : vnode
section([
Expand Down
2 changes: 1 addition & 1 deletion demo/testspanel.kk
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module demo/testspanel

import demo/tests/support
import demo/shared
import explore/react/core
import explore/react

fun test_item(item : test_result) : vnode
val name = test_result/name(item)
Expand Down
2 changes: 1 addition & 1 deletion demo/todo/events.kk
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module demo/todo/events

import demo/model
import library/maybe
import explore/react/action
import explore/react

pub type todo_action
Add_task
Expand Down
3 changes: 1 addition & 2 deletions demo/todo/state.kk
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
module demo/todo/state

import explore/react/action
import explore/react/state
import explore/react

pub struct task_editor_state(editing : bool, draft : string)
pub type task_editor_action
Expand Down
3 changes: 1 addition & 2 deletions demo/todo/view.kk
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import demo/todo/events
import demo/todo/state
import demo/todo/viewmodel
import runtime/dom
import explore/react/core
import explore/react/state
import explore/react

fun task_item_view(item : task) : app_view vnode
val tid = task/id(item)
Expand Down
3 changes: 1 addition & 2 deletions demo/view.kk
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import demo/model
import demo/tests/support
import demo/layout
import library/dialog
import explore/react/core
import explore/react/state
import explore/react

fun append_overlay(shell_node : vnode, overlay_node : vnode) : <div> vnode
shell_node(children = append(vnode/children(shell_node), [overlay_node]))
Expand Down
16 changes: 6 additions & 10 deletions docs/component-authoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@

# 中文

普通业务组件只导入三个模块:
普通业务组件只导入一个稳定入口:

```koka
import explore/react/core
import explore/react/action
import explore/react/state
import explore/react
```

这三个模块分别承载 elements/VDOM、serializable action protocol,以及 component lifecycle/store/effect authoring。不要在业务 view 中导入 `explore/react/runtime`、`explore/react/inspection` 或 `explore/react/renderer`。
该入口 re-export elements/VDOM、serializable action protocol,以及 component lifecycle/store/effect authoring。实现仍拆分在 `core`、`action`、`state`,但这不是组件作者的前置知识。不要在业务 view 中导入 `explore/react/runtime`、`explore/react/inspection` 或 `explore/react/renderer`。

## 1. Elements

Expand Down Expand Up @@ -72,15 +70,13 @@ effect name 在同一 component boundary 内保持稳定。`state_resource(...)`

# English

Ordinary business components import only three modules:
Ordinary business components import one stable entry:

```koka
import explore/react/core
import explore/react/action
import explore/react/state
import explore/react
```

They provide elements/VDOM, the serializable action protocol, and component lifecycle/store/effect authoring. Business views do not import `explore/react/runtime`, `explore/react/inspection`, or `explore/react/renderer`.
It re-exports elements/VDOM, the serializable action protocol, and component lifecycle/store/effect authoring. Implementation remains split across `core`, `action`, and `state`, but component authors do not need that knowledge up front. Business views do not import `explore/react/runtime`, `explore/react/inspection`, or `explore/react/renderer`.

## 1. Elements

Expand Down
26 changes: 18 additions & 8 deletions docs/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@

# 中文

Respo 的日常组件代码只需要按顺序理解四件事:elements、keyed components、typed stores/actions 和 effects。下面用一个 FAQ feature 把它们串在一起。
Respo 的日常组件代码从一个入口开始:

```koka
import explore/react
```

它只公开 component authoring surface,不包含 runtime、inspection 或 renderer。之后按顺序理解四件事:elements、keyed components、typed stores/actions 和 effects。下面用一个 FAQ feature 把它们串在一起。

## 1. 先写普通 view function

element 的主要内容保持第一个位置参数,样式、key 和事件使用 labelled arguments:

```koka
import explore/react/core
import explore/react

fun faq_answer(text : string) : vnode
p(text, class = "faq-answer")
Expand All @@ -22,8 +28,7 @@ fun faq_answer(text : string) : vnode
store 把 state、action、纯 reducer 和恢复方式定义在一起。这个 disclosure 只有一个 toggle action,所以使用内置 bool codec 的 snapshot store:

```koka
import explore/react/action
import explore/react/state
import explore/react

type disclosure_action
Toggle_disclosure
Expand Down Expand Up @@ -97,14 +102,20 @@ fun faq_panel(items : list<faq_item>, key : string = "panel")

# English

Everyday Respo component code can be learned as four concepts in order: elements, keyed components, typed stores/actions, and effects. The following FAQ feature combines all four.
Everyday Respo component code starts from one entry:

```koka
import explore/react
```

It exposes only the component-authoring surface, not runtime, inspection, or renderer. From there, learn four concepts in order: elements, keyed components, typed stores/actions, and effects. The following FAQ feature combines all four.

## 1. Start with an ordinary view function

Keep primary element content positional and use labelled arguments for styling, keys, and events:

```koka
import explore/react/core
import explore/react

fun faq_answer(text : string) : vnode
p(text, class = "faq-answer")
Expand All @@ -117,8 +128,7 @@ An ordinary function call only extracts rendering. A helper without local state
A store groups its state, action, pure reducer, and recovery choice. This disclosure has one toggle action, so it uses a snapshot store with the built-in bool codec:

```koka
import explore/react/action
import explore/react/state
import explore/react

type disclosure_action
Toggle_disclosure
Expand Down
7 changes: 7 additions & 0 deletions explore/react.kk
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module explore/react

// Ordinary component authors need one stable entry point. Runtime execution,
// inspection, and rendering stay in their explicit advanced modules.
pub import explore/react/core
pub import explore/react/action
pub import explore/react/state
2 changes: 1 addition & 1 deletion library/bridge.kk
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module library/bridge

import explore/react/core
import explore/react

pub effect fun request_bridge_resource(payload : string, delay : int) : ()

Expand Down
2 changes: 1 addition & 1 deletion library/dialog.kk
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module library/dialog

import explore/react/core
import explore/react

pub type dialog_kind
Dialog_none
Expand Down
2 changes: 1 addition & 1 deletion library/search.kk
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module library/search

import explore/react/core
import explore/react

pub struct search_case
query : string
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"build:koka": "node ./scripts/build-koka.mjs",
"dev": "yarn build:koka && vite",
"build": "yarn build:koka && vite build",
"check": "yarn build:koka && vite build",
"check:author-surface": "node ./scripts/check-author-surface.mjs",
"check": "yarn check:author-surface && yarn build:koka && vite build",
"test:koka": "node ./scripts/test-koka.mjs",
"preview": "vite preview"
},
Expand Down
Loading
Loading