- html viewer:.html/.htm 沙箱 iframe 预览(相对资源经 /sidebar/html 路径编码路由加载,CSP sandbox + 信任围栏 + cwd 边界),预览/编辑切换 - browser tab:+ 菜单多开(browser:<n>),地址栏后退/前进/刷新/在浏览器中打开(15px 图标),URL 与标题按会话持久化(patchTab + nextBrowser) - 安全:iframe 一律不透明源沙箱(无 allow-same-origin/top-navigation、no-referrer、权限策略全禁、禁 srcdoc);地址栏仅 http(s)、拒绝 localhost 等本机地址;界面实时沙箱状态(关闭红色警示)+ 临时解锁/恢复 - 设置:htmlViewerNoSandbox / htmlViewerDefaultUnsafe / browserNoSandbox / browserInterceptLinks 四个声明式二级设置(默认安全) - 外链拦截:聊天/界面 http(s) 外链默认在侧边栏打开(openTab seed.url),Ctrl/Cmd 点击放行 - 嵌入拒绝面板:browser.probe 路由探测 X-Frame-Options/frame-ancestors,被拒站点显示原因 + 在浏览器中打开/仍然加载 - 设置页可滚动;内置 7 tab + 9 viewer;新增设计文档;268 个测试全绿
61 lines
2.1 KiB
TypeScript
61 lines
2.1 KiB
TypeScript
/**
|
|
* The live sandbox status row of the two built-in web surfaces (HTML
|
|
* preview and the browser tab): a green "sandbox on" state with a one-tap
|
|
* TEMPORARY unlock, or a RED "sandbox off" state (global setting or the
|
|
* temporary unlock) with a restore action.
|
|
*
|
|
* The temporary unlock is component state only — it never writes the
|
|
* global side card setting (`htmlViewerNoSandbox` / `browserNoSandbox`);
|
|
* it lasts until the surface unmounts (tab switch / file switch) or the
|
|
* user restores the sandbox from the row. When the global setting already
|
|
* drops the sandbox, no unlock/restore action is offered (changing the
|
|
* global setting is the settings page's job) — the red warning stands.
|
|
*/
|
|
import clsx from 'clsx'
|
|
import { t } from './locales.ts'
|
|
import css from './sidebar.module.css'
|
|
|
|
export function SandboxStatusBar(props: {
|
|
/** The effective sandbox state (global pref OR the local temporary unlock). */
|
|
sandboxed: boolean
|
|
/** Whether the sandbox is off due to the LOCAL temporary unlock (shows the restore action). */
|
|
local: boolean
|
|
/** The red-state explanation (e.g. "the page runs with full GUI privileges"). */
|
|
dangerCopy: string
|
|
onUnlock: () => void
|
|
onRestore: () => void
|
|
}) {
|
|
const { sandboxed, local, dangerCopy, onUnlock, onRestore } = props
|
|
if (sandboxed) {
|
|
const copy = t('sandboxStatusOn')
|
|
return (
|
|
<div className={clsx(css.sandboxStatus, css.sandboxStatusOn)}>
|
|
<span className={css.sandboxDot} />
|
|
<span className={css.sandboxStatusText} title={copy}>{copy}</span>
|
|
<button
|
|
type="button"
|
|
className={css.sandboxAction}
|
|
onClick={onUnlock}
|
|
>
|
|
{t('sandboxUnlock')}
|
|
</button>
|
|
</div>
|
|
)
|
|
}
|
|
return (
|
|
<div className={clsx(css.sandboxStatus, css.sandboxStatusOff)}>
|
|
<span className={css.sandboxDot} />
|
|
<span className={css.sandboxStatusText} title={dangerCopy}>{dangerCopy}</span>
|
|
{local && (
|
|
<button
|
|
type="button"
|
|
className={css.sandboxAction}
|
|
onClick={onRestore}
|
|
>
|
|
{t('sandboxRestore')}
|
|
</button>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|