Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
8e4a41f
fix: align desktop renderer with Blora design
Detritalw Sep 7, 2026
2a13693
fix: 重构桌面端首次启动引导流程
Detritalw Sep 7, 2026
41658c9
fix: 保持首次启动从欢迎页开始
Detritalw Sep 7, 2026
48b98f1
fix: 通过官方站直接打开服务器入口
Detritalw Sep 7, 2026
32eafe5
fix: 通过官方站直接打开服务器入口
Detritalw Sep 7, 2026
bcf0999
fix: 修正 OOBE 步骤进度显示
Detritalw Sep 7, 2026
63c81c5
fix: 通过官方站选择目标 CrewRouter 登录
Detritalw Sep 7, 2026
aa6ee98
fix: 官方站先展示目标 CrewRouter 选择
Detritalw Sep 7, 2026
2c5c396
fix: helper 登录不再强制跳转 Passport
Detritalw Sep 7, 2026
9fd78a8
fix: 允许官方站入口重复打开
Detritalw Sep 7, 2026
8e9bd42
fix: 修正 Desktop OAuth 客户端名称与回调
Detritalw Sep 7, 2026
4ac9aa0
fix: 放行官方站选择目标的 DNS 代理解析
Detritalw Sep 7, 2026
ffb7473
fix: 授权后复用浏览器会话打开目标
Detritalw Sep 7, 2026
e0c6fa3
fix: 显示官方站授权完成状态
Detritalw Sep 7, 2026
3d47083
fix: 区分官方站打开与授权完成状态
Detritalw Sep 7, 2026
d1b403d
feat: 授权后在 Desktop 内打开目标实例
Detritalw Sep 7, 2026
8113016
fix: 注入授权后的 Desktop Web Session Cookie
Detritalw Sep 7, 2026
17be288
feat: 在 CrewRouter 设置中增加 Desktop 设置入口
Detritalw Sep 7, 2026
0178e3f
fix: 正确替换授权后的 HttpOnly Session Cookie
Detritalw Sep 7, 2026
d78d050
fix: 确保授权后自动登录 Desktop 会话
Detritalw Sep 7, 2026
ef2ce2c
fix: 绑定 Desktop 设置卡片点击事件
Detritalw Sep 7, 2026
52ad72d
refactor: 将 Desktop 设置嵌入 CrewRouter 设置页
Detritalw Sep 7, 2026
ce34fc7
fix: 允许内嵌设置读取 Desktop 配置
Detritalw Sep 7, 2026
bc80e14
feat: 扩展嵌入式 Desktop 实例管理设置
Detritalw Sep 8, 2026
f1dea1c
fix: 完善内嵌 Desktop Profile 切换权限
Detritalw Sep 8, 2026
0955708
fix: 重构 Desktop 设置页内嵌视觉
Detritalw Sep 8, 2026
b301d23
fix: 启动时自动恢复远程 CrewRouter
Detritalw Sep 8, 2026
43eee34
feat: 增加 Desktop 重启和关闭操作
Detritalw Sep 8, 2026
1a2c19f
fix: 修复控制台弹窗适配器初始化异常
Detritalw Sep 8, 2026
c85d29b
fix: 为 Desktop 设置操作增加确认
Detritalw Sep 8, 2026
522932e
fix: 修复 Blora 弹窗内容显示异常
Detritalw Sep 8, 2026
e8612b4
fix: 使用页内确认避免设置操作误执行
Detritalw Sep 8, 2026
f1b0cf5
fix: 修复设置按钮点击事件确认流程
Detritalw Sep 8, 2026
47d6f58
fix: 使用 Blora 对话框确认 Desktop 操作
Detritalw Sep 8, 2026
3c3323d
feat: 将模型库迁移到 Blora Design
Detritalw Sep 8, 2026
4513a06
feat: 迁移模型库按钮卡片与菜单
Detritalw Sep 8, 2026
49de725
fix: 修正飞书 OAuth 反向代理回调地址
Detritalw Sep 8, 2026
03c3ab0
feat: 支持从设置添加新的 CrewRouter 连接
Detritalw Sep 8, 2026
66876ad
fix: 让飞书登录入口兼容无脚本浏览器
Detritalw Sep 8, 2026
1a67aac
fix: 优化模型库卡片与操作布局
Detritalw Sep 8, 2026
e209bad
fix: 为普通登录按钮增加表单回退
Detritalw Sep 8, 2026
4f2520e
fix: 统一模型库按钮比例与圆角
Detritalw Sep 8, 2026
f8f0b01
fix: 移除本地模式悬浮 Desktop 设置按钮
Detritalw Sep 8, 2026
cc56bd2
fix: 淡化模型库卡片边框
Detritalw Sep 8, 2026
9675541
fix: 修复文档页模型列表接口错误
Detritalw Sep 8, 2026
8750b53
feat: 将我的上游页面迁移到 Blora Design
Detritalw Sep 8, 2026
f82ae84
fix: 修复设置页切换官方站连接
Detritalw Sep 8, 2026
5c0478b
fix: 本地连接不再自动打开 Desktop 设置窗口
Detritalw Sep 8, 2026
f534235
fix: 修复我的上游动态按钮迁移
Detritalw Sep 8, 2026
5f6d0fc
feat: 将 API Key 页面迁移到 Blora Design
Detritalw Sep 8, 2026
8f8fc37
feat: 将统计信息页面迁移到 Blora Design
Detritalw Sep 8, 2026
4074c4b
feat: 将项目工作页面迁移到 Blora Design
Detritalw Sep 8, 2026
4da34e4
fix: 避免本地 Profile 重复并增加切换加载状态
Detritalw Sep 8, 2026
08e0e99
fix: 优化 API Key 客户端配置对话框
Detritalw Sep 8, 2026
e6c91a3
feat: 将排行榜页面迁移到 Blora Design
Detritalw Sep 8, 2026
a7e0601
feat: 将接口文档页面迁移到 Blora Design
Detritalw Sep 8, 2026
ea502ca
feat: 将积分页面迁移到 Blora Design
Detritalw Sep 8, 2026
1042cd8
feat: 将操作日志页面迁移到 Blora Design
Detritalw Sep 8, 2026
60fc74d
feat: 将用户设置页面迁移到 Blora Design
Detritalw Sep 8, 2026
5cd5033
feat: 将会话页面迁移到 Blora Design
Detritalw Sep 8, 2026
f62cd7f
feat: 将提示词页面迁移到 Blora Design
Detritalw Sep 8, 2026
dee2512
feat: 将管理后台统计页面迁移到 Blora Design
Detritalw Sep 8, 2026
891a249
feat: 将管理后台用户页面迁移到 Blora Design
Detritalw Sep 8, 2026
9ee7363
feat: 将管理后台 Team 页面迁移到 Blora Design
Detritalw Sep 9, 2026
35ebf17
feat: 将管理后台用户组页面迁移到 Blora Design
Detritalw Sep 9, 2026
38b2a12
feat: 将管理后台模型页面迁移到 Blora Design
Detritalw Sep 9, 2026
de95fef
feat: 将管理后台供应商页面迁移到 Blora Design
Detritalw Sep 9, 2026
4c5a8be
feat: 将管理后台调用错误页面迁移到 Blora Design
Detritalw Sep 9, 2026
12844e2
feat: 将管理后台操作日志迁移到 Blora Design
Detritalw Sep 9, 2026
4f4b4a3
feat: 将管理后台提示词页面迁移到 Blora Design
Detritalw Sep 9, 2026
03aa262
feat: 将管理后台系统设置迁移到 Blora Design
Detritalw Sep 9, 2026
67c670e
feat: 将管理后台插件页面迁移到 Blora Design
Detritalw Sep 9, 2026
d74c557
fix: 统一管理后台模型卡片按钮
Detritalw Sep 9, 2026
bd7f7d6
fix: 统一管理后台模型筛选操作按钮
Detritalw Sep 9, 2026
0fe1465
fix: 统一管理后台对话框视觉
Detritalw Sep 9, 2026
be26774
fix: 兼容本地 Personal 版通知状态
Detritalw Sep 9, 2026
67fbb28
feat: 迁移控制台与后台全部下拉框
Detritalw Sep 9, 2026
b9e86ce
feat: 迁移控制台与后台全部按钮
Detritalw Sep 9, 2026
0712feb
feat: 统一控制台与后台标签徽章
Detritalw Sep 9, 2026
3b74638
feat: 统一控制台与后台进度条
Detritalw Sep 9, 2026
1437f44
feat: 统一控制台与后台滑块
Detritalw Sep 9, 2026
9ccad55
feat: 统一控制台与后台开关
Detritalw Sep 9, 2026
18099ea
feat: add personal edition workspace
Detritalw Sep 9, 2026
7aa2f2a
fix: show desktop settings in browser console
Detritalw Sep 10, 2026
2d32fa4
fix: hide desktop settings in browser
Detritalw Sep 10, 2026
25ccf3e
fix: align model library actions with Blora design
Detritalw Sep 10, 2026
9d59d05
fix: polish console navigation and model controls
Detritalw Sep 10, 2026
a2d4dd1
fix: refine API key card controls
Detritalw Sep 10, 2026
2078136
fix: align stats filters and cards with Blora
Detritalw Sep 10, 2026
cc1dfa2
fix: remove task aggregation panel from stats
Detritalw Sep 10, 2026
dee0803
fix: constrain console language selector width
Detritalw Sep 10, 2026
e973b91
fix: align console surfaces with Blora Design
Detritalw Sep 10, 2026
58da955
fix: constrain CC Switch dialog icons
Detritalw Sep 11, 2026
dbd978f
feat: expand model test error details
Detritalw Sep 11, 2026
0c8f5aa
fix: refine model library controls
Detritalw Sep 11, 2026
5ca7f83
fix: align admin dashboard with Blora Design
Detritalw Sep 11, 2026
42ff5dc
fix: keep desktop settings hidden outside desktop app
Detritalw Sep 11, 2026
17eadd9
feat: move API key usage details to a page
Detritalw Sep 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CrewRouter-Desktop/scripts/prepare-blora-assets.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ function copyCssAssets(sourceRoot, relative = '') {
for (const entry of fs.readdirSync(path.join(sourceRoot, relative), { withFileTypes: true })) {
const child = path.join(relative, entry.name);
if (entry.isDirectory()) copyCssAssets(sourceRoot, child);
else if (entry.isFile() && entry.name.endsWith('.css')) {
else if (entry.isFile() && (entry.name.endsWith('.css') || entry.name.endsWith('.js'))) {
const source = path.join(sourceRoot, child);
const target = path.join(targetRoot, child);
fs.mkdirSync(path.dirname(target), { recursive: true });
Expand Down
4 changes: 2 additions & 2 deletions CrewRouter-Desktop/src/connection-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ class ConnectionManager {
return { ...parseInstanceResponse(body, { allowLocalRuntime: options.allowLocalhost }), url: result.url.toString() };
}

async connect({ id = crypto.randomUUID(), name = 'CrewRouter', displayName = null, localIdentityId = null, url, mode = 'remote', allowLocalhost = false } = {}) {
const instance = await this.inspect(url, { allowLocalhost });
async connect({ id = crypto.randomUUID(), name = 'CrewRouter', displayName = null, localIdentityId = null, url, mode = 'remote', allowLocalhost = false, resolveDns = undefined } = {}) {
const instance = await this.inspect(url, { allowLocalhost, ...(resolveDns === undefined ? {} : { resolveDns }) });
const profile = { id, name, ...(displayName ? { displayName } : {}), ...(localIdentityId ? { localIdentityId } : {}), url: instance.url, mode, runtime: instance.runtime, edition: instance.edition, auth: instance.auth, capabilities: instance.capabilities, protocolVersion: instance.protocolVersion, lastConnectedAt: new Date(this.now()).toISOString() };
this.store.upsert(profile); this.store.setActive(id);
return profile;
Expand Down
129 changes: 102 additions & 27 deletions CrewRouter-Desktop/src/main.js

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions CrewRouter-Desktop/src/preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,24 @@ const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('crewrouterDesktop', Object.freeze({
getStatus: () => ipcRenderer.invoke('desktop:get-status'),
openOobe: () => ipcRenderer.invoke('desktop:open-oobe'),
chooseMode: (mode) => ipcRenderer.invoke('desktop:choose-mode', mode),
setupLocalProfile: (displayName) => ipcRenderer.invoke('desktop:setup-local-profile', displayName),
connectRemote: (url) => ipcRenderer.invoke('desktop:connect-remote', url),
openOfficialDemo: () => ipcRenderer.invoke('desktop:open-official-demo'),
connectCustomRemote: (url) => ipcRenderer.invoke('desktop:connect-custom-remote', url),
openExternal: (url) => ipcRenderer.invoke('desktop:open-external', url),
listProfiles: () => ipcRenderer.invoke('desktop:list-profiles'),
switchProfile: (id) => ipcRenderer.invoke('desktop:switch-profile', id),
quit: () => ipcRenderer.invoke('desktop:quit'),
restartApp: () => ipcRenderer.invoke('desktop:restart-app'),
openSettings: () => ipcRenderer.invoke('desktop:open-settings'),
getDesktopSettings: () => ipcRenderer.invoke('desktop:get-settings'),
saveDesktopSettings: (settings) => ipcRenderer.invoke('desktop:save-settings', settings),
renameProfile: (id, name) => ipcRenderer.invoke('desktop:rename-profile', id, name),
deleteProfile: (id) => ipcRenderer.invoke('desktop:delete-profile', id),
stopLocal: () => ipcRenderer.invoke('desktop:stop-local'),
restartLocal: () => ipcRenderer.invoke('desktop:restart-local'),
getDiagnostics: () => ipcRenderer.invoke('desktop:get-diagnostics'),
onStatus: (callback) => {
const listener = (_event, status) => callback(status);
Expand Down
138 changes: 65 additions & 73 deletions CrewRouter-Desktop/src/renderer/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,108 +10,100 @@
<link rel="stylesheet" href="vendor/blora-design/components/card/card.css">
<link rel="stylesheet" href="vendor/blora-design/components/badge/badge.css">
<link rel="stylesheet" href="vendor/blora-design/components/input/input.css">
<link rel="stylesheet" href="vendor/blora-design/components/field/field.css">
<link rel="stylesheet" href="vendor/blora-design/components/button/button.css">
<link rel="stylesheet" href="vendor/blora-design/components/hero/hero.css">
<link rel="stylesheet" href="vendor/blora-design/components/steps/steps.css">
<link rel="stylesheet" href="vendor/blora-design/components/result/result.css">
<link rel="stylesheet" href="styles.css">
</head>
<body class="blora-page">
<body class="blora-page blora-scope">
<noscript><p class="blora-fallback-message">CrewRouter Desktop 需要启用 JavaScript。</p></noscript>
<main class="blora-shell" aria-labelledby="page-title">
<header class="blora-header"><button id="settings" class="blora-text-button" type="button">⚙ 设置</button>
<main class="oobe-shell" aria-labelledby="page-title">
<header class="oobe-header">
<button id="settings" class="blora-button" data-variant="ghost" data-size="sm" type="button">
<span class="button-icon" data-icon="settings" aria-hidden="true"></span><span>设置</span>
</button>
<a class="blora-brand" href="#page-title" aria-label="CrewRouter Desktop 首页">
<span class="blora-brand-mark" aria-hidden="true"><span></span><span></span><span></span></span>
<span><strong>CrewRouter</strong><small>DESKTOP</small></span>
</a>
<span class="blora-version">安全连接</span>
<span class="blora-badge" data-variant="soft">安全连接</span>
</header>

<section class="connection-hero" aria-describedby="intro">
<p class="blora-eyebrow">首次启动</p>
<h1 id="page-title">开始使用 CrewRouter</h1>
<p id="intro">选择本地使用,或连接已有服务器。</p>
<section class="blora-hero oobe-hero" data-size="compact" aria-describedby="intro">
<div class="blora-hero__content">
<p class="blora-eyebrow">首次启动</p>
<h1 id="page-title">开始使用 CrewRouter</h1>
<p id="intro">选择本地使用,或连接已有服务器。</p>
</div>
</section>

<section id="mode-step" class="mode-step" aria-labelledby="mode-heading">
<h2 id="mode-heading" class="step-heading">选择一种方式</h2>
<div class="mode-options">
<button id="local" class="blora-card mode-option" type="button">
<span class="mode-option__icon" aria-hidden="true">⌂</span>
<span class="mode-option__copy"><strong>本地使用</strong><small>一键启动本地服务,无需登录</small></span>
<span class="mode-option__arrow" aria-hidden="true">→</span>
<nav class="oobe-progress" aria-label="设置进度">
<blora-steps id="oobe-steps" current="0" clickable="false">
<blora-step title="选择方式" description="选择本地或远程连接"></blora-step>
<blora-step title="完成设置" description="填写必要信息"></blora-step>
<blora-step title="开始使用" description="进入 CrewRouter"></blora-step>
</blora-steps>
</nav>

<section id="welcome-panel" class="oobe-panel" aria-labelledby="welcome-heading">
<div class="oobe-panel__heading">
<div><p class="blora-eyebrow">第一步</p><h2 id="welcome-heading">选择一种方式</h2></div>
</div>
<p class="oobe-description">本地使用无需登录;连接服务器时,登录由目标服务器负责。</p>
<div class="oobe-choice-grid">
<button id="local" class="blora-card oobe-choice" data-variant="hover" type="button">
<span class="oobe-choice__icon" data-icon="home" aria-hidden="true"></span>
<span class="oobe-choice__copy"><strong>本地使用</strong><small>启动本地服务,无需登录</small></span>
<span class="oobe-choice__arrow" data-icon="chevron-right" aria-hidden="true"></span>
</button>
<button id="remote-choice" class="blora-card mode-option" type="button" aria-controls="remote-choice-step">
<span class="mode-option__icon" aria-hidden="true"></span>
<span class="mode-option__copy"><strong>连接服务器</strong><small>选择官方站或自定义服务器地址</small></span>
<span class="mode-option__arrow" aria-hidden="true"></span>
<button id="remote-choice" class="blora-card oobe-choice" data-variant="hover" type="button">
<span class="oobe-choice__icon" data-icon="globe" aria-hidden="true"></span>
<span class="oobe-choice__copy"><strong>连接服务器</strong><small>连接官方站或自定义服务器</small></span>
<span class="oobe-choice__arrow" data-icon="chevron-right" aria-hidden="true"></span>
</button>
</div>
</section>

<section id="local-profile-step" class="remote-step" aria-labelledby="local-profile-heading" hidden>
<div class="step-heading-row"><div><p class="blora-eyebrow">本地设置</p><h2 id="local-profile-heading">设置你的用户名</h2></div></div>
<p class="remote-description">只需设置一次,用于显示在本地控制台和资料中。无需密码或登录。</p>
<form id="local-profile-form" novalidate>
<label class="blora-label" for="local-username">用户名</label>
<input id="local-username" class="blora-input" type="text" maxlength="64" autocomplete="nickname" aria-describedby="local-username-hint local-username-error" aria-invalid="false">
<p id="local-username-hint" class="blora-input-hint">可使用普通文字、数字或短横线,最多 64 个字符。</p>
<p id="local-username-error" class="blora-field-error" role="alert"></p>
<button id="local-continue" class="blora-button" data-variant="primary" data-block type="submit"><span>继续进入本地控制台</span><span class="blora-button-arrow" aria-hidden="true">→</span></button>
<section id="local-profile-panel" class="oobe-panel blora-card" data-variant="content" aria-labelledby="local-profile-heading" hidden>
<div class="oobe-panel__heading"><div><p class="blora-eyebrow">第二步</p><h2 id="local-profile-heading">设置你的用户名</h2></div><button id="local-back" class="blora-button" data-variant="text" data-size="sm" type="button"><span class="button-icon" data-icon="chevron-left" aria-hidden="true"></span>返回</button></div>
<p class="oobe-description">只需设置一次,用于显示在本地控制台和资料中。</p>
<form id="local-profile-form" class="oobe-form" novalidate>
<blora-field id="local-username-field" label="用户名" name="local-username" hint="可使用普通文字、数字或短横线,最多 64 个字符。" maxlength="64" autocomplete="nickname" required></blora-field>
<p id="local-username-error" class="oobe-field-error" role="alert"></p>
<div class="oobe-actions"><button id="local-continue" class="blora-button" data-variant="primary" data-size="lg" type="submit">继续进入本地控制台 <span class="button-icon" data-icon="arrow-right" aria-hidden="true"></span></button></div>
</form>
</section>

<section id="remote-choice-step" class="remote-step" aria-labelledby="remote-choice-heading" hidden>
<div class="step-heading-row">
<div><p class="blora-eyebrow">第二步</p><h2 id="remote-choice-heading">连接服务器</h2></div>
<button id="choice-back" class="blora-text-button" type="button">返回</button>
</div>
<p class="remote-description">Personal / Team 由目标服务器自动识别,登录由目标服务器负责。</p>
<div class="mode-options remote-options">
<button id="official-remote" class="blora-card mode-option" type="button" aria-controls="official-remote-step">
<span class="mode-option__icon" aria-hidden="true">↗</span>
<span class="mode-option__copy"><strong>通过官方站连接</strong><small>先验证目标服务器,再打开 crewrouter.bloret.net 负责转向</small></span>
<span class="mode-option__arrow" aria-hidden="true">→</span>
</button>
<button id="custom-remote" class="blora-card mode-option" type="button" aria-controls="remote-step">
<span class="mode-option__icon" aria-hidden="true">⌕</span>
<span class="mode-option__copy"><strong>输入服务器地址</strong><small>自己填写服务器地址,直接打开,不经过官方 Demo</small></span>
<span class="mode-option__arrow" aria-hidden="true">→</span>
</button>
<section id="remote-method-panel" class="oobe-panel blora-card" data-variant="content" aria-labelledby="remote-method-heading" hidden>
<div class="oobe-panel__heading"><div><p class="blora-eyebrow">第二步</p><h2 id="remote-method-heading">连接服务器</h2></div><button id="choice-back" class="blora-button" data-variant="text" data-size="sm" type="button"><span class="button-icon" data-icon="chevron-left" aria-hidden="true"></span>返回</button></div>
<p class="oobe-description">选择连接方式。Personal / Team 由目标服务器自动识别。</p>
<div class="oobe-choice-list">
<button id="official-remote" class="blora-card oobe-choice" data-variant="hover" type="button"><span class="oobe-choice__icon" data-icon="external-link" aria-hidden="true"></span><span class="oobe-choice__copy"><strong>通过官方站连接</strong><small>打开官方站,在官方页面选择或输入服务器</small></span><span class="oobe-choice__arrow" data-icon="chevron-right" aria-hidden="true"></span></button>
<button id="custom-remote" class="blora-card oobe-choice" data-variant="hover" type="button"><span class="oobe-choice__icon" data-icon="search" aria-hidden="true"></span><span class="oobe-choice__copy"><strong>输入服务器地址</strong><small>直接打开你填写的服务器,不经过官方站</small></span><span class="oobe-choice__arrow" data-icon="chevron-right" aria-hidden="true"></span></button>
</div>
</section>

<section id="official-remote-step" class="remote-step" aria-labelledby="official-remote-heading" hidden>
<div class="step-heading-row">
<div><p class="blora-eyebrow">第三步</p><h2 id="official-remote-heading">输入目标服务器</h2></div>
<button id="official-back" class="blora-text-button" type="button">返回</button>
</div>
<p class="remote-description">Desktop 只验证公开实例信息并把目标 origin 绑定到一次性 state,不接触登录凭据。</p>
<form id="official-remote-form" novalidate>
<label class="blora-label" for="official-remote-url">目标服务器地址</label>
<input id="official-remote-url" class="blora-input" type="url" inputmode="url" placeholder="https://server.example.com" autocomplete="url" aria-describedby="official-url-hint official-url-error" aria-invalid="false">
<p id="official-url-hint" class="blora-input-hint">验证通过后将在系统浏览器打开官方 Demo 转向入口。</p>
<p id="official-url-error" class="blora-field-error" role="alert"></p>
<button id="official-continue" class="blora-button" data-variant="primary" data-block type="submit"><span>打开官方站</span><span class="blora-button-arrow" aria-hidden="true">↗</span></button>
<section id="custom-target-panel" class="oobe-panel blora-card" data-variant="content" aria-labelledby="custom-target-heading" hidden>
<div class="oobe-panel__heading"><div><p class="blora-eyebrow">第三步</p><h2 id="custom-target-heading">输入服务器地址</h2></div><button id="custom-back" class="blora-button" data-variant="text" data-size="sm" type="button"><span class="button-icon" data-icon="chevron-left" aria-hidden="true"></span>返回</button></div>
<p class="oobe-description">登录由目标服务器负责,Desktop 不保存凭据。</p>
<form id="connection-form" class="oobe-form" novalidate>
<blora-field id="custom-url-field" label="服务器地址" name="remote-url" type="url" placeholder="https://server.example.com" hint="仅支持 http:// 或 https:// 地址。" autocomplete="url" inputmode="url" required></blora-field>
<p id="url-error" class="oobe-field-error" role="alert"></p>
<div class="oobe-actions"><button id="remote" class="blora-button" data-variant="primary" data-size="lg" type="submit">直接连接服务器 <span class="button-icon" data-icon="external-link" aria-hidden="true"></span></button></div>
</form>
</section>

<section id="remote-step" class="remote-step" aria-labelledby="remote-heading" hidden>
<div class="step-heading-row">
<div><p class="blora-eyebrow">第三步</p><h2 id="remote-heading">输入服务器地址</h2></div>
<button id="back" class="blora-text-button" type="button">返回</button>
</div>
<form id="connection-form" novalidate>
<label class="blora-label" for="remote-url">服务器地址</label>
<input id="remote-url" class="blora-input" type="url" inputmode="url" placeholder="https://server.example.com" autocomplete="url" aria-describedby="url-hint url-error" aria-invalid="false">
<p id="url-hint" class="blora-input-hint">Personal 或 Team 由目标服务器自动识别,登录由目标服务器负责。</p>
<p id="url-error" class="blora-field-error" role="alert"></p>
<button id="remote" class="blora-button" data-variant="primary" data-block type="submit"><span>直接连接服务器</span><span class="blora-button-arrow" aria-hidden="true">↗</span></button>
</form>
<section id="status-panel" class="oobe-status" aria-live="polite" aria-atomic="true" hidden>
<blora-result id="status-result" variant="info" title="正在处理" description=""></blora-result>
<p id="runtime-error" class="oobe-field-error" role="alert"></p>
<p id="status" class="oobe-status__text" role="status"></p>
</section>

<div class="blora-feedback" data-state="idle" aria-live="polite" aria-atomic="true">
<span id="runtime-error" class="blora-runtime-error" role="alert"></span><span class="blora-status-dot" aria-hidden="true"></span><p id="status" role="status">请选择一个连接方式</p>
</div>
<footer class="blora-footer"><small>远程页面负责登录;Desktop 不接管凭据。</small><button id="quit" class="blora-quit" type="button">退出应用 <span aria-hidden="true">⌘Q</span></button></footer>
<footer class="oobe-footer"><small>远程页面负责登录;Desktop 不接管凭据。</small><button id="quit" class="blora-button" data-variant="text" data-size="sm" type="button">退出应用 <kbd>⌘Q</kbd></button></footer>
</main>
<script type="module" src="vendor/blora-design/auto.js"></script>
<script src="renderer.js"></script>
</body>
</html>
Loading
Loading