Skip to content

Commit d294c8c

Browse files
authored
Merge pull request #16 from Respo/codex/component-authoring-surface
发布渐进式组件作者 API / Publish a progressive component authoring surface
2 parents 86d40ae + 2c9fca5 commit d294c8c

17 files changed

Lines changed: 596 additions & 207 deletions

Agents.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,19 @@
77
第一次回到仓库时,优先按这个顺序走:
88

99
1. 先看 `package.json` 里的脚本,确认日常入口还是 `yarn dev``yarn build``yarn test:koka`
10-
2. 再看 `app.kk`,确认当前浏览器桥只暴露哪些 Koka 入口。
11-
3. 然后看 `explore/react/*``demo/*` 的边界:前者是库,后者是 demo 和业务。
12-
4. 开始改代码前,先跑一次 `yarn build`,确认自己不是站在坏状态上继续开发。
10+
2. 组件作者先看 `docs/quick-start.md``docs/component-authoring.md`;不要从 runtime snapshot 实现反推日常 API。
11+
3. 再看 `app.kk`,确认当前浏览器桥只暴露哪些 Koka 入口。
12+
4. 然后看 `explore/react/*``demo/*` 的边界:前者是库,后者是 demo 和业务。
13+
5. 开始改代码前,先跑一次 `yarn build`,确认自己不是站在坏状态上继续开发。
1314

1415
## 仓库结构
1516

1617
- 仓库根目录:就是 Koka 源码根目录,编译时直接把 repo root 当成模块搜索根。
1718
- `app.kk`:浏览器入口,只暴露 boot、事件桥接和 runtime snapshot 导入导出。
18-
- `explore/react/*`:核心 VDOM、typed component store、listener registry、render、diff/patch。这里尽量保持通用。
19+
- `explore/react/core.kk``action.kk``state.kk`:component authoring surface;业务 view 只从这里获取 elements、actions、stores、keyed lifecycle 与 effects。
20+
- `explore/react/runtime.kk`:browser/app host 使用的 registered callback、scheduled effect 与 snapshot transport。
21+
- `explore/react/inspection.kk`:tests/devtools 使用的 VDOM、event registry 与 runtime entry 只读查询。
22+
- `explore/react/renderer.kk`:host/tests 使用的 render、diff 与 patch;不要导入业务 view。
1923
- `demo/*`:具体 demo、布局、组件、路由和测试辅助。
2024
- `demo/runtimeframe.kk`:app 边界的 runtime owner,同时持有业务 model 和框架 state tree。
2125
- `runtime/*`:只放 DOM 和系统边界的 FFI,不要把业务逻辑塞进来。
@@ -148,6 +152,8 @@ chrome-devtools take_screenshot --fullPage --filePath .tmp-devtools-full.png
148152

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

155+
- 普通业务 view 只导入 `explore/react/core``explore/react/action``explore/react/state` 中实际需要的模块;不得导入 `explore/react/runtime``explore/react/inspection``explore/react/renderer`
156+
151157
- 业务组件用 `(state, dispatch) = use_store(spec, initial = ...)` 读取 reducer pair,不额外暴露 binding record。
152158
- UI 事件优先用 `on_store_click(name, action = ..., dispatch = ...)` / `on_store_input(...)` 发送 typed action,不直接操作 state tree。
153159
- domain action 事件优先用 `on_action_click(name, action = ..., dispatch = ...)` / `on_action_input(...)` / `on_action_enter(...)`,不要在每个 element 内重复 forwarding closure。

PLAN.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,9 @@ feature_dom_marker(group = ..., key = ..., name = ...)
170170

171171
### 3. 拆分 component facade 与 runtime/testing API
172172

173-
- 将普通组件需要的 `component/components/use_store/state_effect/on_*` 收敛到 facade;
174-
- runner、registry、snapshot 与 tree inspection 移到 runtime/testing 模块
175-
- opaque feature identity 已进入 `feature_root`下一步把显式 key/path inspection API 物理拆到 runtime/testing module
173+
- 普通组件的 authoring surface 已明确收敛在 `core/action/state`,不额外增加只做转发的 facade;
174+
- registered callback runner、scheduled effect、snapshot transport 已物理移到 `explore/react/runtime`;VDOM、event registry 与 entry count 查询已物理移到 `explore/react/inspection`
175+
- opaque feature identity 已进入 `feature_root`显式 key/path 与 store inspection 目前仍和 state tree 实现共享私有依赖,只有能移动真实实现且不产生单行 forwarding wrapper 时再继续拆分
176176
- 为 controlled component 固定“主 domain value 位置参数 + labelled callback/config props”的签名模板,避免每个 feature 再造 props adapter;
177177
- scope 计算只保留在确有跨组件协调的 state 模块;
178178
- reducer、codec、store spec 尽量同模块定义,view 只 import typed surface;
@@ -200,8 +200,8 @@ issue、PR 及影响结论的进度更新统一使用中英双语:标题采用
200200
- [#7 Hide feature identity and remove cross-component store path coordination](https://github.com/Respo/explore-react.koka/issues/7):已由 PR #10 合并;
201201
- [#8 减少 typed store 样板代码并显式选择 replay 恢复 / Reduce typed store boilerplate with explicit replay recovery](https://github.com/Respo/explore-react.koka/issues/8):已由 PR #14 合并;snapshot/replay recovery 选择与 Todo editor session 语义已落地;
202202
- [#9 Define lifecycle cleanup for unreachable child component stores](https://github.com/Respo/explore-react.koka/issues/9):已由 PR #11 合并;
203-
- [#12 简化组件事件中的 domain 与 local-store transition / Simplify domain and local-store transitions in component events](https://github.com/Respo/explore-react.koka/issues/12)当前实现批次;以四个真实调用点、action 顺序测试和组件定义可读性作为是否保留公共 API 的标准
204-
- [#13 发布渐进式组件作者 API / Publish a progressive-disclosure component authoring surface](https://github.com/Respo/explore-react.koka/issues/13)等待 #12 的 transition API 完成使用者评估后,整理 quick start、authoring APImodule 边界
203+
- [#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 顺序与浏览器回归
204+
- [#13 发布渐进式组件作者 API / Publish a progressive-disclosure component authoring surface](https://github.com/Respo/explore-react.koka/issues/13)当前实现批次;以四概念 quick start、单页 author API、advanced module import 边界作为验收标准
205205

206206
## 验证标准
207207

README.md

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,29 @@ This repo explores a React-like component runtime in Koka, with Algebraic
77
Effects used for browser capabilities, test substitution, component-local
88
state, and event dispatch.
99

10+
## Start with component authoring
11+
12+
Learn the normal path as four tasks; runtime ownership and snapshot transport
13+
are advanced integration topics, not prerequisites:
14+
15+
1. **Elements:** write ordinary view functions with positional content and
16+
labelled attributes/events.
17+
2. **Keyed components:** add `component(...)` / `components(...)` only when a
18+
child needs stable lifecycle identity.
19+
3. **Typed stores/actions:** define a serializable action and use
20+
`(state, dispatch) = use_store(spec, initial = ...)`.
21+
4. **Effects:** declare post-render work with `state_effect(...)`, and keep
22+
browser/service capabilities as explicit Koka effects.
23+
24+
Start with the bilingual [component quick start](docs/quick-start.md), then use
25+
the [one-page component author API](docs/component-authoring.md) as the normal
26+
reference. Persistent feature identity, recovery, and cross-domain/local
27+
transitions are introduced only when needed:
28+
29+
- [component lifecycle](docs/component-lifecycle.md)
30+
- [store recovery](docs/store-recovery.md)
31+
- [action/store transitions](docs/action-store-transitions.md)
32+
1033
The current design deliberately separates two kinds of state:
1134

1235
- domain state lives in the application `model` and changes through typed
@@ -472,8 +495,14 @@ diffing, and patch planning remain in Koka.
472495
- `explore/react/action.kk`: serializable action codecs, envelopes, and the
473496
observation effect.
474497
- `explore/react/state.kk`: component scopes, typed stores, listeners, effects,
475-
snapshots, and runtime handlers.
498+
and lifecycle authoring.
499+
- `explore/react/runtime.kk`: host-only registered callback execution,
500+
scheduled effects, and snapshot transport.
501+
- `explore/react/inspection.kk`: read-only VDOM/event-registry/runtime queries
502+
for tests and devtools.
476503
- `explore/react/renderer.kk`: rendering, diffing, and patch planning.
504+
- `docs/quick-start.md` and `docs/component-authoring.md`: progressive
505+
component tutorial and the compact preferred API.
477506
- `demo/*`: application shell, features, actions, stores, and workflows.
478507
- `demo/runtimeframe.kk`: pairs the domain model with the framework runtime tree
479508
and runs render/action transitions at the app boundary.

boilerplate/browserapp.kk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import library/bridge
1414
import library/route
1515
import explore/react/action
1616
import explore/react/state
17+
import explore/react/runtime
18+
import explore/react/inspection
1719

1820
noinline val root_id_ref : ref<global,string> = unsafe-total { ref("app") }
1921
noinline val frame_ref : ref<global,runtime_frame> = unsafe-total { ref(current_runtime_frame(initial_model(), Nil)) }

demo/tests/appharness.kk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import demo/tests/support
88
import library/bridge
99
import explore/react/core
1010
import explore/react/state
11+
import explore/react/runtime
12+
import explore/react/inspection
1113

1214
fun app_harness_render(previous_owner : maybe<model>, frame : runtime_frame) : <div> component_harness
1315
val (next_frame, tree, effects, registry) = run_runtime_render(frame, fn(owner) render_app_with_runtime(previous_owner, owner, Nil))

demo/tests/basics.kk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import explore/react/action
1515
import explore/react/core
1616
import explore/react/renderer
1717
import explore/react/state
18+
import explore/react/runtime
19+
import explore/react/inspection
1820

1921
fun timer_logic() : <browser_host> string
2022
wait_ms(20)

demo/tests/dialogcases.kk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import library/dialog
1111
import library/route
1212
import explore/react/core
1313
import explore/react/state
14+
import explore/react/inspection
1415

1516
pub fun dialog_alert_open_test() : <div> test_result
1617
with with_test_browser(False, "T+0")

demo/tests/labcases.kk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import demo/tests/support
1515
import explore/react/core
1616
import explore/react/action
1717
import explore/react/state
18+
import explore/react/inspection
1819

1920
pub fun lab_local_routing_test() : <div> test_result
2021
with with_test_browser(False, "T+120")

demo/tests/statecases.kk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import demo/todo/state
66
import explore/react/action
77
import explore/react/core
88
import explore/react/state
9+
import explore/react/runtime
10+
import explore/react/inspection
911

1012
pub fun state_tree_test() : <div> test_result
1113
val incident_scope = "lab/incidents/101"

demo/tests/support.kk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import library/search
1515
import explore/react/action
1616
import explore/react/core
1717
import explore/react/state
18+
import explore/react/runtime
19+
import explore/react/inspection
1820

1921
pub struct test_result(name : string, detail : string, passed : bool)
2022

0 commit comments

Comments
 (0)