♻️ refactor: migrate modals to @lobehub/ui/base-ui (LOBE-9711 + eval batch)#15416
Conversation
Move 5 root createModal sites (LibraryModal/AddFilesToKnowledgeBase,
LibraryModal/CreateNew, Electron/AuthRequiredModal, SkillStore,
SkillStore/SkillDetail) to base-ui imperative createModal. Drop
allowFullscreen/destroyOnHidden/getContainer (base-ui handles them),
rename children→content, afterClose→onOpenChangeComplete, styles.body
→styles.content.
For AuthRequiredModal, base-ui imperative ModalInstance.update only
accepts Partial<BaseModalProps>, so the previous closable/keyboard
dynamic lock is reduced to maskClosable only — Esc/X close cannot be
blocked during sign-in.
Convert 11 declarative <Modal open … /> sites under eval/bench to
imperative createXxxModal factories, splitting each into Content.tsx
(body) + index.tsx (factory). Update callers in eval/index.tsx,
bench/[id]/{datasets/[id],features/{BenchmarkHeader,DatasetsTab,
RunsTab,TestCasesTab},runs/[id]/{index,features/RunHeader}} to call
factories on click instead of toggling local open state.
Delete unused TestCasePreviewModal.tsx (dead code); extract the
inline preview Modal from TestCasesTab into a new
TestCasePreviewModal feature folder.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Sorry @Innei, your pull request is larger than the review limit of 150000 diff characters
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4c29cc805b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| closable: false, | ||
| footer: null, | ||
| keyboard: false, | ||
| maskClosable: false, |
There was a problem hiding this comment.
Preserve non-dismissible auth modal
When the Electron auth-required dialog is shown for an unauthenticated user, @lobehub/ui/base-ui modals still default to closable: true and allow Escape unless keyboard: false is set, so keeping only maskClosable: false lets the user dismiss the required-login prompt via the close button or Esc. The same omission in the signing-in update means the dialog remains dismissible while sign-in is in progress; carry over closable: false/keyboard: false initially and update them with maskClosable.
Useful? React with 👍 / 👎.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## canary #15416 +/- ##
===========================================
- Coverage 89.74% 71.03% -18.71%
===========================================
Files 862 3211 +2349
Lines 105577 321072 +215495
Branches 10216 28283 +18067
===========================================
+ Hits 94746 228080 +133334
- Misses 10656 92817 +82161
Partials 175 175
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
…padding overrides Per @Innei feedback on PR #15416: - base-ui's ModalContent already has 12px/16px default padding; remove manual paddingBlock/paddingInline wrappers in Content components and drop styles.content.padding=0 overrides in factories. - Move OK/Cancel (and other actions) into the createModal({footer}) slot using base-ui's ModalFooter atom for proper flex/justify-end styling. - Form submit wired via antd Form's name + Button form=name htmlType=submit so the footer button outside Form can submit it. Shared loading state flows from Content to Footer via a per-modal closure that calls instance.update({footer: ...}). New helper src/utils/createFormModal.tsx encapsulates the common pattern for plain form modals (Cancel + Submit). Custom factories (RunCreate split-button, BatchResume with selection counter, DatasetImport step-aware footer, AuthRequired sign-in flow) use inline closure plumbing. Touched files: 11 modal pairs (Content + Footer + index) + 1 helper.
`useAddFilesToKnowledgeBaseModal` exported from `src/features/LibraryModal/AddFilesToKnowledgeBase/` had no callers in the main codebase — only its own test referenced it. Remove the entire folder (index, SelectForm, index.test) and drop the re-export from `LibraryModal/index.ts`.
base-ui ModalContent has 12px/16px default padding, which insets the SkillStore scroll viewport and makes the scrollbar look blocked. Pull the body wrapper out with negative margins (marginInline: -16, marginBlockEnd: -12) so the inner scroll container sits flush with the modal edge. Grid items inside the scroll keep their own 16px padding.
base-ui's ModalInstance.update is typed as Partial<BaseModalProps>, which excludes the `footer` and `content` fields that only ImperativeModalProps carries. At runtime the imperative updateModal spreads any shape, so the cast is sound — narrow it at each call site. Also delete src/routes/(main)/eval/bench/[benchmarkId]/features/ DatasetRunCreateModal/, an orphaned re-export of RunCreateModal's removed default export.
# 🚀 LobeHub Release (20260604) **Release Date:** June 4, 2026 **Since v2.2.1:** 88 merged PRs · 11 contributors > This week brings Execution Devices out of the lab — run agents and Claude Code on any configured local or remote machine — alongside Claude Opus 4.8, token-usage analytics, and Page sharing. --- ## ✨ Highlights - **Execution Devices** — Pick where an agent runs. Desktop and CLI devices auto-register with a stable machine ID, route through the gateway by channel, and surface a device switcher in the chat input. Run remote Claude Code on a configured device, with a recent-directory picker you can drag to reorder. (#15300, #15315, #15322, #15343, #15351, #15371) - **Claude Opus 4.8** — Day-one support for Anthropic's latest model. (#15314) - **Token-usage analytics** — A new token-usage mode on the activity heatmap, backed by a denormalized topic usage/cost rollup so totals stay accurate without recomputing from messages. (#15365, #15417, #15425) - **Page sharing** — Share a Page through a dedicated document share flow, plus new Workspace and Agent share tables. (#15309, #15439) - **Self-iteration agents** — Agent Signal's execAgent migration lands a server-runtime bridge, async memory writer, and a registered self-iteration tool package, with a CLI trigger command for testing. (#15360, #15364, #15392) - **Knowledge search** — BM25 search now extends to file-backed documents, and the portal ships an editable CodeMirror viewer for local files with document highlighting. (#15247, #15298) --- ## 🏗️ Core Agent & Architecture ### Agent Signal & Runtime - **execAgent migration** — Server-runtime bridge, completion projection, async memory writer, and removal of the legacy `executeSelfIteration` path. (#15392) - Registered the self-iteration builtin tool package and restored the three mode-specific self-iteration agent slugs. (#15202, #15364) - Added a CLI trigger command with a golden-snapshot fixture for Agent Signal. (#15360) - **Skill priority** — Agent Builder now emits a skill-priority instruction with matching server runtime. (#15409) - Retry empty LLM completions instead of silently finishing the turn. (#15355) - Classify topic/agent/session foreign-key violations as `ConversationParentMissing` for clearer recovery. (#15408) - Persist canonical nested usage/performance on assistant messages, and re-link orphan tool messages at the raw bucket write boundary. (#15359, #15438) - Guard `createAgent` against LLM double-encoded array fields. (#15381) --- ## 🖥️ Execution Devices & Gateway - Auto-register desktop and CLI devices with a stable machine ID, and add the `@lobechat/device-identity` package. (#15300, #15321) - New Devices settings page behind the Execution Device Switcher lab, with a device switcher shown for all agents in the chat input. (#15315, #15371) - `connectionId` + channel routing across the gateway client and device list; preset the local device on the first LLM request for the 本机 target. (#15322, #15435) - Run remote Claude Code on a configured device, with drag-to-reorder recent-directory management and client renders for device tool results. (#15343, #15351, #15437) - Preserve content and state across gateway tool calls, and prevent duplicate streaming from stale reconnects. (#15114, #15354) --- ## 🖥️ CLI & Desktop - Preserve content/state for connect local file and shell tools; render the `runCommand` tool result card. (#15441, #15442) - New `lh topic view` command; CLI now auto-registers its device on login, matching desktop. (#15340, #15377) - Resolve CLI tools from the shell `PATH`, and clarify local command session handling. (#15368, #15389) - Relocate visual-ref helpers to `@lobechat/const` to fix a renderer crash; upload `.blockmap` files to S3 for differential updates. (#15326, #15369) - Fix a market OAuth expiry that triggered the wrong re-login modal, and kill dev child processes on parent shutdown. (#15246, #15290) --- ## 🗂️ Pages, Library & Knowledge - Document share flow with business slot stubs, plus Workspace and Agent share tables. (#15309, #15439) - Export Agent profiles as Markdown, preserving an empty agent prompt on export. (#15312, #15316) - Editable CodeMirror viewer for local files with document highlighting; BM25 search extended to file-backed documents. (#15247, #15298) - Default new Agent-doc files to `.md` and preserve IME composition; refresh folder data on slug switch and dedupe breadcrumb fetches. (#15335, #15427) --- ## 💬 Chat & User Experience - Group-by-status mode for the Topic sidebar; dropped the legacy session→agentId compatibility path from Topic queries. (#15366, #15378) - Restore editor focus after the file picker closes, and close the skill dropdown before navigating to settings. (#15391, #15394) - Strip markdown tokens from fallback Topic titles; keep an open ActionBar popup when hovering another message. (#15303, #15372) - Stabilize home starter loading and stop transliterating model names in the home starter; show artifact source while streaming. (#15310, #15324, #15386) - Group the sidebar spacer with recents and agents. (#15373) --- ## 📊 Analytics, Tasks & Notifications - Token-usage mode on the activity heatmap, backed by a denormalized topic usage/cost rollup. (#15365, #15417, #15425) - Push: new `PushChannel`, receipt cron, and `pushToken` tRPC API. (#15233) - Tasks now support file and image attachments. (#15141) --- ## 🧩 Models & Providers - Support Claude Opus 4.8 and configurable model routing with starters. (#15314, #15384) - MiniMax M3: new model entry and an Anthropic video runtime. (#15380, #15403) - Add `intern-s2-preview` with `thinking_mode`, and `step-3.7-flash` support. (#15308, #15317) - Block disabling the official provider; fix default provider setup in business mode. (#15379, #15382) --- ## 🎨 UI & Modals - Migrate modals to `@lobehub/ui/base-ui` (LOBE-9711 + eval batch), including the create-custom-model and feedback/changelog modals. (#15401, #15416) - Restructure confirmModal title and content across deletion flows; polish the service-model form and migrate its Switch to base-ui. (#15426, #15440) - Wrap the BlueBubbles bridge config into a connection card; update `@lobehub/ui` to v5.15.5. (#15325, #15342) --- ## 🔒 Reliability - Replace hardcoded `session_context` values with template variables in credentials. (#15352) - Point `CHANGELOG_URL` to `/changelog`. (#15428) --- ## 👥 Contributors Huge thanks to **11 contributors** who shipped **88 merged PRs** this cycle. @hezhijie0327 · @qybaihe · @sxjeru · @arvinxx · @Innei · @tjx666 · @lijian · @sudongyuer · @cy948 · @rivertwilight · @AmAzing129 Plus @lobehubbot and renovate[bot] for maintenance. --- **Full Changelog**: v2.2.1...release/weekly-20260604
💻 Change Type
🔗 Related Issue
Fixes LOBE-9711
Part of LOBE-9710, LOBE-9713 (eval/benchmark sub-batch of [3] heavy)
🔀 Description of Change
Two related migrations to the
@lobehub/ui/base-uiimperative modal API, bundled together for review economy.Part A — LOBE-9711 (5 root
createModalsites)src/features/LibraryModal/AddFilesToKnowledgeBase/index.tsxsrc/features/LibraryModal/CreateNew/index.tsxsrc/features/Electron/AuthRequiredModal/index.tsxsrc/features/SkillStore/index.tsxsrc/features/SkillStore/SkillDetail/index.tsxChanges per file:
createModal/useModalContext/ModalInstanceimport from@lobehub/uito@lobehub/ui/base-uichildren→content(base-ui slot)afterClose→onOpenChangeComplete: (open) => !open && afterClose()styles.body→styles.contentallowFullscreen,destroyOnHidden,getContainer,centered,focusTriggerAfterClose(base-ui handles these or they are defaults)Caveat for
AuthRequiredModal: the base-ui imperativeModalInstance.updateonly acceptsPartial<BaseModalProps>, which does not includeclosableorkeyboard. The previous dynamic lock during sign-in (closable: !isSigningIn, keyboard: !isSigningIn, maskClosable: !isSigningIn) is reduced tomaskClosableonly — Esc and the X button can still close the modal during sign-in. TheuseWatchBroadcast('authorizationSuccessful'/'authorizationFailed')listeners stay; if a user closes the modal mid-flow, the underlying OAuth promise resolves into an unmounted component, which is the same race as before (no regression).Part B — eval/benchmark group from LOBE-9713 [3] heavy (declarative → imperative factory)
12 declarative
<Modal open … />sites converted tocreateXxxModal()factories. Each modal is split into:<ModalName>/index.tsx— exportscreateXxxModal(props): ModalInstance<ModalName>/Content.tsx— exports the body component usinguseModalContext().closeand its own footer (Cancel/OKButtons)Modals:
eval/features/CreateBenchmarkModaleval/features/BenchmarkEditModaleval/features/DatasetCreateModaleval/features/DatasetEditModaleval/features/DatasetImportModal(two-step wizard preserved; footer only renders on step 2)eval/features/TestCaseCreateModaleval/features/TestCaseEditModaleval/bench/[benchmarkId]/features/RunCreateModal(preservesSpace.Compact+Dropdownsplit button in footer)eval/bench/[benchmarkId]/features/RunEditModaleval/bench/[benchmarkId]/features/TestCasePreviewModal(new — extracted from inline<Modal>inTestCasesTab/index.tsx)eval/bench/[benchmarkId]/runs/[runId]/features/BatchResumeModalCallers updated to drop
useState/<Modal>and call the factory on click:eval/index.tsxeval/bench/[benchmarkId]/datasets/[datasetId]/index.tsxeval/bench/[benchmarkId]/features/BenchmarkHeader/index.tsxeval/bench/[benchmarkId]/features/DatasetsTab/index.tsxeval/bench/[benchmarkId]/features/RunsTab/index.tsxeval/bench/[benchmarkId]/features/TestCasesTab/index.tsxeval/bench/[benchmarkId]/runs/[runId]/index.tsxeval/bench/[benchmarkId]/runs/[runId]/features/RunHeader/index.tsxDead code:
eval/bench/[benchmarkId]/features/DatasetsTab/TestCasePreviewModal.tsxhad no imports anywhere — deleted.Net diff: ~2400 lines removed, ~2400 added; mostly relocation of form bodies into
Content.tsxplus footer extraction.🧪 How to Test
Smoke test in browser:
AddFilesToKnowledgeBaseandCreateNew(knowledge base flow) — title/content/close/maskClosable behave as before.createSkillStoreModal(); open a skill detail; verify all 4SkillDetailvariants (Builtin,BuiltinAgent,Klavis,Lobehub).authorizationRequiredbroadcast; verify the modal shows; click Sign in, observemaskClosable: falsewhile signing in./eval, create a benchmark; from a benchmark page, edit/delete dataset/test case/run; verify the two-stepDatasetImportModalflow; preview a test case via the eye icon; trigger aBatchResumeon a finished run.📝 Additional Information
BenchmarkHeaderhas a pre-existing unusedrunCountprop (eslint warning, unrelated to this PR).DatasetsTabkeeps an unusedonImportprop in its interface — its caller (bench/[benchmarkId]/index.tsx) passesonImport={() => {}}. Left as-is to keep the diff focused; can be removed in a follow-up.[4]antdModal.confirmcleanup batch from LOBE-9710 is not part of this PR.