Merge remote-tracking branch 'upstream/main' into 1.0.0-explore - #2350
Merged
Conversation
The sidebar asked the user to pick a conversation *source* — Local or Remote — before showing them anything. That is an implementation detail: where a session runs is not how anyone looks for it, and the switcher cost a permanent row of chrome to express it. Conversations and workspaces now share one scroll. They are different kinds of thing (a timeline and a set of places), so they stack rather than compete for the same pane, and neither carries a label saying which machine it runs on. The workspace section arrives through a `@BuilderParam` slot: the sidebar has no business knowing about remote state, so the host wires it. - Drop `ConversationSource` and `ConversationSourceSwitcher`. `AppRouteContract.conversationSource`/`routeForConversationSource` become `isRemoteRoute`/`remoteSurfaceDestination`, with `isRemoteRoute` stated as the complement of `isGeneralComposerRoute` so the two cannot drift apart. `remoteSurfaceDestination` resumes an in-flight remote session instead of dropping the user on a picker they did not ask for. - Cap the conversation list at six rows behind an overflow row, so the workspace section stays visible on a phone without hunting for it. The shared scroll means the list can no longer take every pixel it wants. - Fade rows out under the floating footer via a new `page_bg_fade` token (PAGE_BG at zero alpha, defined per theme). Without it a workspace row mid-scroll is visually sliced by the chat button sitting on top of it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The workspace section's "connect a desktop" row was wired to `onSidebar.enterCode`, which only moves the visible surface to Remote. Offline that resolves to `AppRoute.RemoteHome`, whose wide-layout detail pane is `RemoteSurfaceHost` in placeholder mode: a header naming the desktop the phone last held and two lines of status text, with no control on it. So the row named the last-known machine on the right and then did nothing, and pressing it again returned early on the `isRemoteRoute` guard. `RemoteSurfaceEntryPolicy` already states that reaching the remote surface and asking to pair are different commands and must not share an entry point. The row was on the wrong side of that line: give it `onSidebar.connectDesktop`, which opens the connect sheet. The section header's "+" shares the same event. Workspaces come from the desktop and the phone cannot create one, so it is relabelled by the connection it adds rather than a workspace it cannot make. Also drop workspace entries with no path. `syncRemotePageSummary` seeds the current workspace with the placeholder name 「未连接」 and an empty path, and the projection turns any named current workspace into an entry. On the Remote surface that never showed, because you only got there connected; in the sidebar it is on screen from launch, so a reconnecting phone with no workspace data yet listed a folder called 「未连接」 that opens nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The back arrow on the desktop picker and on the account page sat visibly left of the circle it lives in. Measured on device: circle centre x=422.5, chevron ink centre x=399.5 — about 7vp adrift. The cause is SymbolGlyph sizing, not the surrounding layout. A chevron's natural advance box is roughly half as wide as it is tall (~11.6vp at fontSize 23, against 23.2vp of height). Forcing .width(26).height(26) does not re-centre the glyph in the enlarged box; it draws left-anchored, so the ink lands 26/2 - 11.6/2 ≈ 7vp left of where the box centre says it should. Vertically it looked fine only because the natural height already matched the forced one. Probing the two glyphs side by side with tinted backgrounds confirmed it: the unsized chevron's box measured 37px wide and centred in the circle, while a magnifier forced to the same 26vp filled its box and stayed centred, because its ink is square. Dropping the forced size lets the Stack/Button centre the natural box. Re-measured after the change: ink centre x=422.5 against a circle centre of 422.5. Trailing chevrons elsewhere keep their forced width — there it acts as a row gutter rather than as a centring box, and removing it would move the rows rather than fix them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The crooked back chevron fixed in b48f60d was not a one-off. Probing on device showed SymbolGlyph draws its ink left-anchored inside any box the caller forces wider than the glyph's natural advance box (vertically it stays centred, so only the horizontal axis is affected). Most symbols have a square advance box of fontSize x 1.013, but chevron_left and chevron_right are only half as wide as they are tall, so every `.width(N).height(N)` on a chevron shifted it left of where the surrounding container promised to put it. Two shapes of the same bug: - Glyphs inside a fixed-size centring Stack or circular Button: the forced box overrode the container's centring and pulled the icon off centre by up to 4.5vp. Dropping the size lets the container centre the natural box. Measured on the settings close button: ink centre now 0.3vp from the circle centre, was ~7vp. - Trailing disclosure chevrons in list rows: the forced 16-18vp box left ~10vp of dead space between the arrow and the row's right padding, so the arrows never lined up with the card edge. The right gap on the model row now measures 18.6vp against the 18vp padding. Collapse indicators that toggle between chevron_right and chevron_down keep their fixed slot -- it is what stops the label jumping between the two states -- but the slot moves to a wrapping Stack and the glyph inside it goes unsized, matching the idiom already used in SubagentTaskCard. Verified on device that the label x is identical in both states. Leading icon slots in list rows are left as they are: because ink is left-anchored, the forced width is what keeps icon and text left edges aligned across rows of differing fontSize. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The previous bullet told authors to "give the glyph its visual size", which is exactly what produced the off-centre chevrons fixed in 257a945. Replace it with the metrics measured on device, the reason forcing a width moves a chevron left, the Stack-wrapper idiom for collapse indicators, and the one case where a forced width is the right call.
An activity group buffers a maximal run of thinking entries and settled tools, then renders every thinking part above every tool it holds. A run is only broken by a non-foldable entry such as assistant text, so a silent chain of thought -> act -> thought collapses into one group and the later thinking is hoisted above tools that had already run when the model wrote it. On device that showed up as a single "已思考" sitting on top of "已运行 4 个工具" for a message whose transcript alternates. Open a new group whenever a thinking entry follows a tool in the same run, and flush buffered tools before pushing a group so tools that ran earlier cannot land below it. Consecutive thinking segments still merge into one header. Test plan: - assembleHap + LocalTest for entry@default, no ERROR lines. - On device (foldable inner screen, dark theme), the same message now renders 已思考 / 已运行 2 个工具 / 已思考 / 已运行 2 个工具 / 已思考 where it previously rendered 已思考 / 已运行 4 个工具. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ToolStatusList built its collapsed summary detail, tool operation labels, Todo/Task fallbacks, and the failure suffix from Chinese string literals while the rest of the component already used RemoteI18n. Move all of them into the message table so the component has no user-visible literals left. Test plan: - assembleHap + LocalTest for entry@default, no ERROR lines. - On device, an expanded tool group still reads "运行命令 · gh auth status 2>&1; gh api ..." and the collapsed row still reads "已运行 2 个工具"; no message keys leak into the UI. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A transcript reads think -> act -> think -> act, but the app rendered one 「已思考」 stacked on top of 「已运行 N 个工具」: `ActivityGroup` merged every thinking part in the group above every tool in it, hoisting later reasoning over tools that had already finished when the model wrote it. Reasoning and the tools it drove are one stretch of work, so they now fold behind a single summary row instead of standing as two cards. `ToolStatusList` takes the group's thinking parts and weaves them among its tool rows: collapsed shows only 「已运行 N 个工具」, expanding reveals thinking and tools in transcript order. A lone tool has no summary to unfold, so its reasoning stays on screen rather than being lost. `ActivityGroupPolicy` gains the placement rules — `mergeThinkingParts` keeps a single header for an uninterrupted stretch of reasoning while pinning blocks on either side of a tool call to where they happened, and `weaveRows` never puts thinking above a summary row, which is the fold's own header. Also drops `collapsedSummaryDetail`, which nothing called, and the four `chat.tools*Count` message keys it was the only reader of. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
feat(harmonyos): replace the sidebar source switcher with workspaces
Add a settings page showing historical model call statistics (requests, tokens, estimated cost) broken down by model, provider group, and endpoint, with a token usage trend chart. - services-core: pure aggregation module with unit tests - core: attribution resolver (config + models.dev catalog) and get_statistics on the token usage service - desktop: get_token_usage_statistics Tauri command - web-ui: UsageStatisticsConfig settings tab + i18n (en/zh-CN/zh-TW)
- services-core: remove price estimation; UsageStatisticsEntry now carries cacheHitRate (cached / reported input); UsageStatistics gains totalCacheReportedInputTokens for the overall hit rate - core: resolver no longer resolves prices - web-ui: summary cards show requests/tokens/cached tokens/hit rate; tables show model/group/endpoint, requests, tokens, cache hit rate; fix table header layout (min column widths + no-wrap) that squeezed Chinese labels; drop cost columns and footnote - i18n: update en/zh-CN/zh-TW copy
…splay - move cache hit rate out of the distribution tables into a dedicated per-model card with a ratio bar - display hit rates truncated to two decimals (never rounded up); only a true 100% hit (cached == reported input, exact in the backend) shows as 100% - trend chart now spans the full row; distribution tables revert to name / requests / tokens
The image understanding tool calls AIClient directly, bypassing the round executor, so its consumption never reached the token usage store and multimodal models were missing from the usage statistics page. Persist the response usage through a global TokenUsageService reference installed by the desktop runtime.
Dynamic var() string building (--bf-appearance-token-${name}) is invisible to
the theme color audit, which flagged an unresolved required CSS var and broke
the CI Frontend Build job. Replace every token reference with a literal var()
so the audit resolves them statically.
The appearance contract audit (part of build:web) requires every data-bf-component to match a registered descriptor id and every data-bf-part to be a string literal declared on the same node as its component. Align the usage statistics page: component id 'usage-statistics-config', literal parts on root/filters/summary/distributions/modelHitRate/trendPanel/empty, and drop appearance attributes from internal chart components.
Filter persisted usage records against exact half-open query bounds instead of including every record from matching daily files. - Resolve today, week, and month boundaries in the caller's time zone - Align trend buckets and labels with the same local calendar - Preserve UTC behavior when older clients omit the time zone - Cover boundary timestamps, local midnight, and DST transitions
Resolve model and provider breakdowns from model_config_id instead of API protocol formats or ambiguous model-name catalog fallback. - Aggregate model statistics with stable configuration IDs - Group provider usage by provider_instance_id and display config names - Isolate deleted or missing configurations with explicit status - Show supplier names in model distribution and cache hit rate panels - Route browser time-zone detection through shared i18n helpers - Add backend and UI regression coverage
Filter usage records before aggregation so every statistics panel reflects the selected provider or model query consistently. - Match providers by configured display name instead of API protocol - Match configured and effective model names - Support deleted configurations through effective model fallback - Add debounced filter controls and localized empty states - Keep requests without filter fields backward compatible
Mark token usage statistics as workspace-agnostic because usage is recorded by the current BitFun runtime rather than routed to the SSH workspace host. Add regression coverage to preserve this host-scoped behavior.
Route usage statistics through the shared transport so Desktop, Peer Device, and Server/Web read data from the active host. - Share request parsing across product surfaces - Add CLI peer and Server RPC support - Negotiate peer capability and reject unsupported older hosts - Clarify that usage data is stored separately on each device
Move dashboard request parsing and time-range resolution into bitfun-services-core so Core remains a compatibility wrapper. Add a dedicated token-usage-statistics feature for IANA time-zone aggregation instead of expanding local-storage with chrono-tz. Preserve UTC queries for local-storage-only consumers and update the feature governance checks and focused tests.
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.
No description provided.