Files
232030f46c chore: release 1.0.2b4 (#1226)
* docs: refresh README roadmap, anchors, and product sync (#1211)

Align EN/CN README with shipped features and current install/channel
docs; fix GitHub heading anchors and drop obsolete extras examples.

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(dashboard): define missing --fn-bg-container theme variable (#1216)

Dark-theme surfaces that use var(--fn-bg-container, #fff) — most
visibly the knowledge-base Markdown preview body, toolbar and outline
panel — resolved to a white background while text stayed light, leaving
preview content invisible. Define the variable as #ffffff (light) and
#141414 (dark), and point the Admin/Users badge tints that borrowed the
name for a translucent fill at --fn-bg-tertiary so their look is
unchanged.

Fixes #1215

* feat(dashboard): let each account customize the sidebar (#1218)

Store group order, item placement, and hidden entries on the user so navigation can be rearranged without showing items the account cannot access.

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

* style(dashboard): apply Prettier to files that drifted from the formatter (#1222)

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: store role-template id on users.role and harden mobile composer (#1220)

Collapse the dual user_role_id / role model so users.role and invites.role
hold the template public id, block deleting roles still in use, and keep
the chat composer visible under mobile browser visualViewport.

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: use product names for sibling project links in the README (#1225)

Link labels now match the product names while the repository URLs stay unchanged.

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: release 1.0.2b4

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Dang Zitou <dengzitao888@163.com>
2026-09-27 22:05:48 +08:00

315 lines
9.6 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favico.svg" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, viewport-fit=cover, interactive-widget=resizes-content"
/>
<!-- PWA: theme color (light / dark) -->
<meta
name="theme-color"
content="#ffffff"
media="(prefers-color-scheme: light)"
/>
<meta
name="theme-color"
content="#1a1c28"
media="(prefers-color-scheme: dark)"
/>
<!-- PWA: standalone mode for iOS Safari -->
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta
name="apple-mobile-web-app-status-bar-style"
content="black-translucent"
/>
<meta name="apple-mobile-web-app-title" content="Octop" />
<!-- Web App Manifest -->
<link rel="manifest" href="/manifest.json" />
<title>Octop</title>
<!--
Resolve dashboard theme before first paint. Must stay a classic script:
the splash CSS keys off html[data-theme], not prefers-color-scheme,
so a stored "light" preference wins over an OS dark setting.
Storage shape matches appearanceStorage.ts (THEME_STORAGE_KEY = "theme").
-->
<script id="octop-boot-theme">
(function () {
var KEY = "theme";
var mode = "light";
function systemDark() {
try {
return window.matchMedia("(prefers-color-scheme: dark)").matches;
} catch (e) {
return false;
}
}
try {
var raw = localStorage.getItem(KEY);
var pref = "system";
if (raw === "light" || raw === "dark" || raw === "system") {
pref = raw;
} else if (raw) {
var parsed = JSON.parse(raw);
if (
parsed &&
(parsed.preference === "light" ||
parsed.preference === "dark" ||
parsed.preference === "system")
) {
pref = parsed.preference;
}
}
if (pref === "dark" || (pref === "system" && systemDark())) {
mode = "dark";
}
} catch (e) {
if (systemDark()) mode = "dark";
}
document.documentElement.setAttribute("data-theme", mode);
var themeColor = mode === "dark" ? "#1a1c28" : "#ffffff";
var metas = document.querySelectorAll('meta[name="theme-color"]');
for (var i = 0; i < metas.length; i++) {
metas[i].setAttribute("content", themeColor);
metas[i].removeAttribute("media");
}
})();
</script>
<!--
First-paint boot splash: visible before JS chunks parse. React.createRoot
replaces #root contents on mount, so this disappears automatically.
-->
<style>
html,
body {
height: 100%;
margin: 0;
background: #f7f8fa;
}
#root {
min-height: 100%;
min-height: 100lvh;
}
#octop-boot {
/* fixed + inset: always viewport-centered before app CSS sets #root height */
position: fixed;
inset: 0;
z-index: 2147483646;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 20px;
padding: max(24px, env(safe-area-inset-top, 0px))
max(24px, env(safe-area-inset-right, 0px))
max(24px, env(safe-area-inset-bottom, 0px))
max(24px, env(safe-area-inset-left, 0px));
margin: 0;
background: #f7f8fa;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI",
"Noto Sans SC", sans-serif;
color: #8b8fa3;
}
html[data-theme="dark"],
html[data-theme="dark"] body {
background: #12141c;
}
html[data-theme="dark"] #octop-boot {
background: #12141c;
color: rgba(255, 255, 255, 0.55);
}
#octop-boot-mark {
position: relative;
width: 88px;
height: 88px;
flex: none;
}
#octop-boot-ring {
box-sizing: border-box;
position: absolute;
inset: 0;
border-radius: 50%;
border: 3px solid rgba(232, 93, 117, 0.18);
border-top-color: #e85d75;
animation: octop-boot-spin 0.85s linear infinite;
}
html[data-theme="dark"] #octop-boot-ring {
border-color: rgba(240, 139, 154, 0.22);
border-top-color: #f08b9a;
}
.octop-boot-logo {
position: absolute;
inset: 14px;
width: 60px;
height: 60px;
border-radius: 14px;
object-fit: contain;
display: block;
animation: octop-boot-breathe 1.6s ease-in-out infinite;
}
.octop-boot-logo--dark {
display: none;
}
html[data-theme="dark"] .octop-boot-logo--light {
display: none;
}
html[data-theme="dark"] .octop-boot-logo--dark {
display: block;
}
#octop-boot-text {
font-size: 14px;
font-weight: 500;
letter-spacing: 0.04em;
user-select: none;
}
@keyframes octop-boot-spin {
to {
transform: rotate(360deg);
}
}
@keyframes octop-boot-breathe {
0%,
100% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(0.94);
opacity: 0.88;
}
}
@media (prefers-reduced-motion: reduce) {
#octop-boot-ring,
.octop-boot-logo {
animation: none;
}
}
</style>
<!--
Classic script (not type=module) so it runs even when hashed chunks 404.
Shares octop:chunk-reload with reloadOnStaleChunk.ts.
-->
<script>
(function () {
// Chromium 91 and earlier do not provide this ES2022 API, while some
// dashboard dependencies call it. Run before any ESM chunks evaluate.
if (typeof Object.hasOwn !== "function") {
Object.defineProperty(Object, "hasOwn", {
value: function hasOwn(object, property) {
return Object.prototype.hasOwnProperty.call(object, property);
},
configurable: true,
writable: true,
});
}
var KEY = "octop:chunk-reload";
// Firefox fires error on <link rel="modulepreload"> for /assets/*.js
// even when the file is fine; treating those as stale chunks caused
// infinite location.reload() loops (and aborted real dynamic imports).
var recovering = false;
function isAsset(url) {
return typeof url === "string" && url.indexOf("/assets/") !== -1;
}
function isStylesheetLink(el) {
if (!el || el.tagName !== "LINK") return false;
var rel = (el.rel || "").toLowerCase();
return rel.indexOf("stylesheet") !== -1;
}
function recover() {
if (recovering) return;
try {
if (sessionStorage.getItem(KEY) === "1") {
sessionStorage.removeItem(KEY);
return;
}
sessionStorage.setItem(KEY, "1");
} catch (e) {}
recovering = true;
var reload = function () {
location.reload();
};
var bust = Promise.resolve();
if ("serviceWorker" in navigator) {
bust = navigator.serviceWorker
.getRegistrations()
.then(function (regs) {
return Promise.all(
regs.map(function (r) {
return r.unregister();
}),
);
});
}
if ("caches" in window) {
bust = bust.then(function () {
return caches.keys().then(function (keys) {
return Promise.all(
keys.map(function (k) {
return caches.delete(k);
}),
);
});
});
}
bust.then(reload, reload);
}
window.addEventListener(
"error",
function (e) {
var t = e.target;
if (!t || !t.tagName) return;
if (t.tagName === "SCRIPT" && isAsset(t.src)) recover();
// Only stylesheets — never modulepreload / preload (Firefox noise).
if (isStylesheetLink(t) && isAsset(t.href)) recover();
},
true,
);
})();
</script>
</head>
<body>
<div id="root">
<div id="octop-boot" role="status" aria-live="polite">
<div id="octop-boot-mark" aria-hidden="true">
<div id="octop-boot-ring"></div>
<img
class="octop-boot-logo octop-boot-logo--light"
src="/logo_vertical_dark.svg"
alt=""
width="60"
height="60"
/>
<img
class="octop-boot-logo octop-boot-logo--dark"
src="/logo_vertical_white.svg"
alt=""
width="60"
height="60"
/>
</div>
<div id="octop-boot-text">Loading…</div>
</div>
</div>
<script>
(function () {
var el = document.getElementById("octop-boot-text");
if (!el) return;
try {
var lang = (
navigator.language ||
navigator.userLanguage ||
""
).toLowerCase();
if (lang.indexOf("zh") === 0) el.textContent = "加载中…";
} catch (e) {}
})();
</script>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>