deps: follow felis to v0.1.0 #3
Loading…
Reference in a new issue
No description provided.
Delete branch "follow-felis-105b0899"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Follows felis from
32775067to the v0.1.0 release (https://github.com/felis-terminal/felis, tagv0.1.0), across the wire-baseline reset. The branch keeps its earlier commits (the reset follow to105b0899) and adds the move to the release on top.Closes #8
Closes #1
Pins
git = "https://github.com/felis-terminal/felis", tag = "v0.1.0"(felis is not on crates.io).nix/web.nixoutputHashesrefreshed.felis→github:felis-terminal/felis/v0.1.0;felis-web-gateway→?ref=follow-felis-v0.1.0, the branch of natsukium/felis-web-gateway#4. Re-lock that input to the gateway'smainonce #4 merges.default-features = false) is fixed upstream at v0.1.0; every gate runs unpatched.What changed (v0.1.0 over the earlier commits here)
{"felis_json":1,"kind":"grid"|"image"|"conn"|"session","msg":…}(broker:sidbeside it).applyJson/applyImageJsonreadmsgthrough felis'sjson_v1DTOs (which repack rows into the row codec;transcodeis gone upstream). The TS router uses lowercase kinds and thetypetag; both transports refuse an unknownfelis_jsonrather than paint a screen missing state.inputpayload:key_bytes,paste,resize,viewport,focus_change,jump_prompt,mouse(modifier bit set, snake_case buttons/actions).encodeKeyis replaced bykeyInput, which builds the wireKeyevent (key / text / mods / press-repeat-release / location) and refuses text past the per-keystroke cap. Space is sent as the named key (Ctrl+Space → NUL). The mode getters that only fed the old encoder are removed.SessionInfoJson(id,dims,sequence, lowercase urgency);SessionEntrygainssequenceand the mobile demo restores tabs in creation order.web/public/*.fcastare rewritten as.fcastv3 / felis-json v1 by a one-to-one variant mapping (the old frames hold everything v3 needs; several could not be re-recorded identically).gen-glyphs-fcast.pynow emits v3, and regenerating from it matches the converted file exactly. Newtests/fixtures.rsreplays every fixture frame by frame, so a future format change breakscargo testrather than only the gallery.docs/mobile.md/Cargo comments updated.Verification
cargo fmt --all --check,cargo clippy --all-targets --target wasm32-unknown-unknown -D warnings,cargo test --target wasm32-unknown-unknown(25 apply + 10 key_input + 2 fixtures),cargo deny check, wasm build +wasm-bindgen+tsgo -p . --noEmit,nix build .#web: all pass locally.felis-daemon(isolated socket) through the gateway from natsukium/felis-web-gateway#4, drivingmobile.htmlin headless Chromium: roster → tab attach (wire minor 0), fit resize, typed commands, Enter, Ctrl+C interruptingsleep, ArrowUp history recall, and the shortcut bar'sgit status⏎all reached the shell and rendered.index.htmlgallery: every converted recording replays with no console errors (btm, glyphs, kitty image, OSC 66 sizing, OSC 8 links, OSC 10/11/12 theme checked).tests/apply.rswas re-captured from the v0.1.0 daemon through the gateway.Still open
felis reset its wire baseline to 1.0 on 2026-09-05, so the pin this crate carried (32775067, 2026-07-16) reads a protocol that no longer exists. Bump every felis-* rev in lockstep and adapt the wasm core to what the reset left: - `ShadowGrid` is `ShadowScreen`, built from `(rows, cols)` alone. The per-connection `Encoding` is gone with the proto3-JSON leg, so the shadow always decodes the packed row codec; `applyJson` recodes a frame's structural-JSON rows through `felis_grid::transcode` rather than teaching the view a second row encoding, because the codec's version tag gates a future layout and only one side may know it. - `RowDelta` carries `rows: [(row, payload)]` and absorbed `RowDeltaBatch`; `GridSize` is `Size { dims }`; `RehydrateBegin` is payload-free. - `encode_row_with` / `decode_row_with` split into `encode_row` / `decode_row` (packed) and their `_json` twins, and take a `RowEncode` with an explicit `pad_to`. The replay scrollback ring stores the packed form now: nothing outside it reads those bytes. - `key_encode::encode` takes `KittyKbdFlags` and `ModifyOtherKeys` instead of raw levels; the JS mirror keeps its numeric wire shape and converts at the boundary. - the shadow no longer mirrors OSC 10/11/12, so the view keeps the three theme channels itself for `themeJson`. - image transfers are canonical: `Header` carries an `ImageTarget`, and `total_bytes` / a chunk's `offset` are gone. `ImageShadow::apply` is fallible now, and a malformed sequence surfaces as a JS error. - `virtual_placement(id)` became the `virtual_placements()` slice. `GridMsg::ScrollbackRow` is gone with the family split: a capture reply is `RegionMsg::Row`, which carries text the daemon already resolved. The `captureRow` export decoded that payload and has no source left, so it goes; its real-daemon row fixture moves onto `applyJson`, where the same bytes now arrive.The reset took the capability bitmap with it: a connection now states one `ConnectionMode` and negotiates a protocol minor, and there is nothing per-feature left to intersect. So `Attached.caps` becomes `Attached.minor` and `caps()` becomes `protocolMinor()` — the number a browser feature needing a wire addition gates on. Both transports already carry it: the gateway's attach reply names `minor`, and a `.fcast` v2 header names `proto_minor`. Search and scrollback export left `InputMsg` in the same reset. They are `Search` / `Region` requests now, correlated families whose request ids the connection driver allocates, and the gateway's broker routes neither direction of either — so a browser has no way to ask. Keeping the old `sendInput` calls would be worse than useless: an undecodable frame no longer gets skipped, it ends the connection, so pressing search would have cost the user their session. `search`, `cancelSearch` and `captureText` for anything but `"visible"` refuse locally instead, and the reply plumbing they fed goes with them. `captureText("visible")` still answers from the shadow screen, this side of the wire. The bench workload generator emits `RowDelta`'s new `(row, payload)` shape, and `docs/mobile.md` gains a preface saying which of its wire facts the reset invalidated — the same treatment its paths got when the gateway split out, and for the same reason: it records a decision sequence, so rewriting it would make it claim a wire that did not exist while the work was done. `nix/web.nix`'s vendoring hash follows the rev.felis 105b0899's wire reset moved InputMsg::Resize's four axes behind a `dims` field, wrapped GridMsg::ClipboardSet's body in `write`, and merged Bell/Notify into GridMsg::Attention. The follow commit missed all three, and each fails silently on a live session: - the gateway drops the flat Resize as `missing field dims` with only an eprintln, so `resize="fit"` is inert and the daemon never learns the cell pixel metrics a Kitty-graphics producer reads out of ws_xpixel/ws_ypixel; - every OSC 52 reaches onClipboard as `{text: "", clipboard: false}` — a lie rather than a no-op — and the clipboardMode="write" shortcut, gated on that flag, never fires; - routeGrid's default arm swallows Attention, so onNotify never fires. Attention carries both a bell and a desktop notification, and felis routes the two into one client-side handler, so onNotify fires for either; its doc comment claimed OSC 9/99/777 only and still named the NOTIFY cap the reset removed. Refs natsukium/felis-web-component#4, #5, #6. Assisted-by: Claude CodePushed two commits fixing the four cross-check findings (#4-#7); each shape was re-verified against felis
main's serde definitions before editing.d2c915d—fix(web): follow felis's wire reset for resize, clipboard and attention(#4, #5, #6)sendResizenow emits{"Resize":{"dims":{rows,cols,pixel_w,pixel_h}}}, matchingInputMsg::Resize { dims: RequestedDims }.routeGrid'sClipboardSetarm unwrapswritebefore readingselection/data, matchingClipboardSet { write: ClipboardWrite }(selectionstays the rawu8bits —ClipboardSelection's hand-writtenSerializeis unchanged).Notifyarm becameAttention.AttentionSourceis not passed through: felis routesBellandNotificationinto one client-side handler, soonNotifyfires for either. Its doc comment said "OSC 9/99/777" only and still named theNOTIFYcap the reset removed; both are corrected, as is theREADME.mdbullet and the staleInputMsg::Resize { rows, cols, … }row indocs/mobile.md.02c6764—fix(web): carry the post-exit grace onto broker roster rows(#7)SessionEntrygainsexited: booleanandlastExitCode: number | null;toEntryreadsexited/last_exit_code.web/mobile.htmlmarks a restored exited tab with☠and an exit-code tooltip, kept distinct from the dead-stream⚠— that one promises a re-attach, which an exited shell cannot honour.idleSeconds' doc comment corrected:nullwhile attached, so0is "detached for under a second".Gates
nix develop -c tsgo -p . --noEmit— pass (against a freshly builtweb/pkg).nix develop -c cargo fmt --all --check— pass.nix develop -c cargo deny check— pass.cargo build/test --target wasm32-unknown-unknown— still red for the reason tracked in natsukium/felis#195; untouched here.No test was added: all four fixes live in the TypeScript layer, and the repo has no TS test harness (
tests/iswasm-bindgenRust,bench/is a render harness, and no package script runs a JS runner).tests/apply.rscannot see any of these — it decodes through the typed enum, which is exactly why the follow commit passed its gates.felis v0.1.0 is the first public release, published at github.com/felis-terminal/felis; the crates are not on crates.io, so every felis dependency now pins that tag, and the demo's flake inputs follow: felis at the same tag, and felis-web-gateway at the branch that moves the relay to it (to be re-locked to its main once that lands). The gateway and this component decode the same frames, so the two move together. What the release changed under the component: - The gateway re-exposes frames as felis's own felis-json v1 envelope (`{"felis_json":1,"kind":"grid",…}`), not the derived serde of the Rust enums. `applyJson` / `applyImageJson` read the payload through felis's `json_v1` DTOs, which also repack each row into the row codec the shadow screen decodes, so the component forks no shape; the `transcode` recode it used is gone upstream. The TS router reads the lowercase kind tokens and the `type` tag, and both transports refuse a frame in a felis-json version they do not read instead of painting a screen that is missing state. - Input goes back as the v1 `input` payload (`{"type":"key_bytes",…}`, `{"type":"mouse","event":{…}}` with the modifier bit set, …). - Key encoding moved into the daemon, which owns the keyboard modes, so the wasm `encodeKey` became `keyInput`: it names the key, modifiers, press/repeat/release and location as the wire's `Key` event and refuses text past the per-keystroke cap the daemon would end the connection over. Space goes out as the named key, the only form in which Ctrl+Space encodes. The mode getters that only fed the old encoder are dropped. - Roster rows are `SessionInfoJson` (`id`, `dims`, `sequence`); the mobile demo restores tabs in creation order from `sequence`. - `ThemeColor` carries a set/reset action, and a row naming a cluster that was never installed is now refused by the shadow screen rather than painted as U+FFFD, so that test asserts the refusal. The bench workload generator emits the v1 shapes too. The captured real-daemon row in the apply tests was re-taken from a v0.1.0 daemon through the gateway. Search and scrollback export stay unreachable from a browser: the gateway still routes neither correlated family. Assisted-by: Claude Codedeps: follow felis to 105b0899 (wire-baseline reset)to deps: follow felis to v0.1.0