Repository navigation
feat: connect a Dot. Quote/0 as a third display type - #1
Merged
Merged
Conversation
New Display gains a Quote/0 card beside Hardware and Virtual. The form takes a Dot. API key and the panel's serial number and calls the new POST /api/devices/quote0 (inklet-backend#41); the backend proves the key against the Dot. cloud, seals it, and from then on pushes every rendered picture through the Dot. image API. The app never keeps the key: it lives in the form's state for the length of one request. The device model now reads `transport`: a `dot_cloud` row is a Quote/0, named by its serial (nickname still wins), shown with a cloud glyph in the sidebar, the composer's target picker and the Home card, drawn as a bare 296×152 panel instead of inside the D1 bezel, and described on the detail page as "Quote/0" with a Serial row and a "Dot. cloud" reachability row. `cloudDeliveryError` — the one thing a cloud panel can say about a picture that did not land — is shown under the preview and on the Home card. The unbind copy says what actually happens (inklet forgets the key; the panel keeps what it has). A backend that sends no `transport` still reads as inklet hardware. Bind refusals map the backend's codes to sentences the form shows (bad key, serial not in that Dot. account, owned by another inklet account, Dot. rate limited or down, feature off on the server), with the backend's own message as the fallback for a code this build does not know. Tests: decoding a dot_cloud row (kind, name, identifier, model, glyph, delivery error), nickname precedence, the no-transport fallback, an empty delivery error reading as none, the bind response shape, and the error-code mapping. Not in this change: the iOS app (same wire types, same rules to apply) and any Portal UI. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A Quote/0 fits a picture that is not its own 2:1 onto white; the preview used scaledToFill and cropped it, so the app and the panel disagreed about the same frame. Fit, like the panel. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
26 was what the machine it was written on had, not what the app needs. The one 26-only API in use is ToolbarSpacer, at three sites; each is now gated on #available(macOS 26.0, *), so on 15 the toolbar just has no grouping gaps and the Create button takes the trailing placement on its own. Everything else — @observable, desktop widgets, the presentation cache — already runs on 15. 14 would cost nine more sites; 13 has no desktop widgets. Building still needs Xcode 26 for the widget extension. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…explaining Return used to pick "Just upload" instead of sending: the .defaultAction shortcut sat on the Menu, and a shortcut on a Menu propagates to its items. The key equivalents now live on hidden buttons of their own — ⌘↩ to send, ⌥1…⌥4 to choose the mode — so they fire with the menu closed and never land on an item. The items keep their ⌥n so the menu draws the hint. The labels are "Just upload", "Push to device", "Recent summary" and "Show as-is". What each one costs (an AI run, or none) moved into the tooltip; the face of a split button is not the place for a sentence. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The Portal's Analyses page, in the app: every run inklet has made for the account, newest first, filtered by state and by who started it, and for any one of them the public event timeline — what the agent read, which layout it chose, whether the plan came back, and when the panel showed the picture. Knowledge only says a note was "organized"; this is where. - InkletAPI: analyses (cursor pages), analysis, analysisEvents. - HistoryModel: the paged list and, for the open run, a poll that keeps going while the run is live and for a while after it ends — render and delivery happen after the terminal state and never come with it. - AnalysisCopy: the Portal's analysis-copy and analysis-timeline ported to Swift, so both clients tell the same story: activities merge into one row that changes in place, retries group by attempt, delivery rows name the panel, unknown event types fall back to their summary. - Ink.warn / Ink.working for the two tones the timeline needed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Member
Author
|
Three more commits on this branch, released as v0.2.4-alpha.2 for testing:
Full suite: 49 tests green. |
A cloud glyph said nothing about whose cloud. The card now carries Dot.'s own mark, drawn as a Shape from their single_logo.svg — one filled path — so it takes the foreground style and scales like the SF Symbols beside it. "Nothing to flash, nothing to tap" is gone from the description; it was a sales line, and the card is a button. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… connected The sidebar row and the composer's display picker showed a cloud for a Quote/0; they now carry Dot.'s mark, the same shape the New Display card draws. Device.icon is an Image rather than a symbol name so the rows AppKit draws — menus, pickers — can show it too; the mark is a template NSImage drawn by a handler, so it is crisp at any scale. Connecting a Quote/0 asks for a name. A serial number tells the agent nothing about where the panel is or what it is for, and "let inklet choose" has to choose by something. Blank keeps the serial. The bind route takes no name, so it is a second request; a name that fails to stick is a rename away, the bind is the part that must not be lost. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Knowledge says a note was organized and a display says a card arrived; the run in between — what the agent read, which layout it chose, when the panel showed it — lives on the History page, and there was no way from one to the other. Each history row on a display page now opens its run: click, the Run button on hover, or the context menu. A run the History list does not hold (another filter, an older page) is read on its own, so the jump always lands. The status "Confirmed" reads "Showing": that is what the panel said, and the backend expires the previous picture the moment a newer one takes its place, so only one row ever says it. The timeline's delivery.confirmed line says the same. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
这是什么
macOS 端的 Quote/0 支持,对应后端 inklethq/backend#41。在 New Display 的 Hardware / Virtual 之外新增第三张卡片 Quote/0:输入 Dot. API key 和面板序列号,调用
POST /api/devices/quote0,之后它就是一台普通设备(transport: "dot_cloud"),由后端经 Dot. 云推图。不刷固件、不用 NFC。改了什么
DeviceDTO增加transport/cloudDeviceId/cloudModel/cloudDeliveryError(_At);Device增加kind(.inklet/.quote0)、identifier(序列号或硬件 ID)、modelName、symbol。没有transport的旧后端仍按 inklet 硬件处理。NewDisplayView):三步前置说明(Dot. App 里给面板加 Image API 循环项、创建 key、复制序列号),SecureField输入 key,本地先校验dot_app_前缀,成功后清空 key 并跳到该设备详情。错误按后端code映射成可读句子,未知码回退到后端的message。cloudDeliveryError(云端拒绝时唯一能告诉用户的信息),"Sent" 提示文案区分两种传输,解绑对话框说明会忘掉 key、面板保留现有画面。DisplayFrame按kind绘制——Quote/0 用 296×152 的裸面板加细边框,不再套 D1 边框。cloud图标;首页卡片有投递错误时直接显示。docs/virtual-displays.md的 Quote/0 说明。验证
swift build --product InkletMac通过;swift test(Xcode 26 工具链)41 个测试全绿,新增 6 个 Quote/0 测试(解码、命名、回退、错误映射、绑定响应形状)。DEVICE_CLOUD_CREDENTIAL_KEYS后,用一把 Dot. key 和一台加了 Image API 循环项的面板走一遍:连接 → 详情页 → Push Here → 面板刷新。没做的
iOS 端(wire 类型与规则相同,可平移)与 Portal 的绑定 UI。
🤖 Generated with Claude Code