Files
dbx/plugins/examples/hello-workbench/ui/index.html

328 lines
13 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>DBX Hello Workbench</title>
<style>
:root {
color-scheme: light dark;
font-family: Inter, ui-sans-serif, system-ui, sans-serif;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
min-height: 100vh;
background: #0b1020;
color: #e8ecf8;
}
main {
min-height: 100vh;
display: grid;
place-items: center;
padding: 32px;
background: radial-gradient(circle at 20% 10%, rgba(77, 124, 255, 0.28), transparent 34%), radial-gradient(circle at 90% 80%, rgba(25, 214, 166, 0.2), transparent 32%);
}
.card {
width: min(720px, 100%);
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 20px;
padding: 28px;
background: rgba(15, 22, 42, 0.86);
box-shadow: 0 24px 80px rgba(0, 0, 0, 0.35);
}
.eyebrow {
color: #7ee7cb;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.14em;
text-transform: uppercase;
}
h1 {
margin: 10px 0 8px;
font-size: clamp(28px, 5vw, 46px);
line-height: 1.05;
}
p {
color: #aeb8d0;
line-height: 1.65;
}
.controls {
display: flex;
gap: 10px;
margin-top: 22px;
}
.secondary {
color: #dce5f8;
background: rgba(255, 255, 255, 0.08);
}
input,
button {
border-radius: 10px;
border: 1px solid rgba(255, 255, 255, 0.14);
font: inherit;
}
input {
min-width: 0;
flex: 1;
padding: 11px 13px;
color: inherit;
background: rgba(255, 255, 255, 0.06);
outline: none;
}
button {
padding: 11px 16px;
color: #06101c;
background: #7ee7cb;
font-weight: 750;
cursor: pointer;
}
button:disabled {
opacity: 0.55;
cursor: wait;
}
.result {
margin-top: 18px;
min-height: 74px;
padding: 14px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.05);
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 13px;
white-space: pre-wrap;
}
.meta {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
gap: 10px;
margin-top: 18px;
}
.meta div {
padding: 12px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.04);
}
.meta span {
display: block;
color: #8290ad;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.meta strong {
display: block;
margin-top: 5px;
overflow-wrap: anywhere;
}
</style>
</head>
<body>
<main>
<section class="card">
<div id="eyebrow" class="eyebrow">Sandboxed DBX plugin</div>
<h1 id="title-copy">Sidecar + UI, end to end.</h1>
<p id="description">This page cannot access Tauri or the filesystem directly. It talks only to the DBX Host Bridge, which binds every request to this plugin.</p>
<div class="controls">
<input id="name" value="DBX" aria-label="Name" />
<button id="greet">Invoke sidecar</button>
<button id="files" class="secondary">Open host files</button>
</div>
<div id="result" class="result">Waiting for the plugin host…</div>
<div class="meta">
<div><span id="plugin-label">Plugin</span><strong id="plugin">-</strong></div>
<div><span id="workbench-label">Workbench</span><strong id="workbench">-</strong></div>
<div><span id="connection-label">Connection</span><strong id="connection">-</strong></div>
<div><span id="event-label">Last event</span><strong id="event">-</strong></div>
</div>
</section>
</main>
<script>
const translations = {
en: {
eyebrow: "Sandboxed DBX plugin",
title: "Sidecar + UI, end to end.",
description: "This page cannot access Tauri or the filesystem directly. It talks only to the DBX Host Bridge, which binds every request to this plugin.",
name: "Name",
invoke: "Invoke sidecar",
files: "Open host files",
waiting: "Waiting for the plugin host…",
plugin: "Plugin",
workbench: "Workbench",
connection: "Connection",
lastEvent: "Last event",
noConnection: "No saved connection",
ready: "Ready. Click the button to invoke hello/greet.",
openSaved: "Open this workbench from a saved Hello connection.",
calling: "Calling native sidecar…",
error: "Error",
},
"zh-CN": {
eyebrow: "受沙箱保护的 DBX 插件",
title: "Sidecar 与界面完整联动。",
description: "此页面不能直接访问 Tauri 或文件系统,只能通过 DBX Host Bridge 通信;宿主会把每个请求绑定到当前插件。",
name: "名称",
invoke: "调用 Sidecar",
files: "打开宿主文件浏览器",
waiting: "正在等待插件宿主…",
plugin: "插件",
workbench: "工作台",
connection: "连接",
lastEvent: "最近事件",
noConnection: "未保存连接",
ready: "已就绪。点击按钮调用 hello/greet。",
openSaved: "请从已保存的 Hello 连接打开此工作台。",
calling: "正在调用原生 Sidecar…",
error: "错误",
},
"zh-TW": {
eyebrow: "受沙箱保護的 DBX 外掛",
title: "Sidecar 與介面完整串接。",
description: "此頁面無法直接存取 Tauri 或檔案系統,只能透過 DBX Host Bridge 通訊;主程式會將每個請求綁定到目前外掛。",
name: "名稱",
invoke: "呼叫 Sidecar",
files: "開啟主程式檔案瀏覽器",
waiting: "正在等待外掛主程式…",
plugin: "外掛",
workbench: "工作台",
connection: "連線",
lastEvent: "最近事件",
noConnection: "尚未儲存連線",
ready: "已就緒。按一下按鈕呼叫 hello/greet。",
openSaved: "請從已儲存的 Hello 連線開啟此工作台。",
calling: "正在呼叫原生 Sidecar…",
error: "錯誤",
},
ja: {
eyebrow: "サンドボックス化された DBX プラグイン",
title: "Sidecar と UI をエンドツーエンドで連携。",
description: "このページは Tauri やファイルシステムへ直接アクセスできません。各リクエストを現在のプラグインに関連付ける DBX Host Bridge のみを使用します。",
name: "名前",
invoke: "Sidecar を呼び出す",
files: "ホストのファイルを開く",
waiting: "プラグインホストを待機中…",
plugin: "プラグイン",
workbench: "ワークベンチ",
connection: "接続",
lastEvent: "最新イベント",
noConnection: "保存済み接続なし",
ready: "準備完了。ボタンをクリックして hello/greet を呼び出します。",
openSaved: "保存済みの Hello 接続からこのワークベンチを開いてください。",
calling: "ネイティブ Sidecar を呼び出し中…",
error: "エラー",
},
es: {
eyebrow: "Plugin de DBX aislado",
title: "Sidecar e interfaz, de extremo a extremo.",
description: "Esta página no puede acceder directamente a Tauri ni al sistema de archivos. Solo se comunica con DBX Host Bridge, que vincula cada solicitud a este plugin.",
name: "Nombre",
invoke: "Invocar sidecar",
files: "Abrir archivos del host",
waiting: "Esperando al host del plugin…",
plugin: "Plugin",
workbench: "Área de trabajo",
connection: "Conexión",
lastEvent: "Último evento",
noConnection: "Sin conexión guardada",
ready: "Listo. Haz clic en el botón para invocar hello/greet.",
openSaved: "Abre esta área de trabajo desde una conexión Hello guardada.",
calling: "Invocando el sidecar nativo…",
error: "Error",
},
it: {
eyebrow: "Plugin DBX in sandbox",
title: "Sidecar e interfaccia, end-to-end.",
description: "Questa pagina non può accedere direttamente a Tauri o al file system. Comunica solo con DBX Host Bridge, che associa ogni richiesta a questo plugin.",
name: "Nome",
invoke: "Invoca sidecar",
files: "Apri file dell'host",
waiting: "In attesa dell'host del plugin…",
plugin: "Plugin",
workbench: "Area di lavoro",
connection: "Connessione",
lastEvent: "Ultimo evento",
noConnection: "Nessuna connessione salvata",
ready: "Pronto. Fai clic sul pulsante per invocare hello/greet.",
openSaved: "Apri quest'area di lavoro da una connessione Hello salvata.",
calling: "Chiamata del sidecar nativo…",
error: "Errore",
},
"pt-BR": {
eyebrow: "Plugin DBX em sandbox",
title: "Sidecar e interface, de ponta a ponta.",
description: "Esta página não pode acessar diretamente o Tauri nem o sistema de arquivos. Ela se comunica apenas com o DBX Host Bridge, que vincula cada solicitação a este plugin.",
name: "Nome",
invoke: "Invocar sidecar",
files: "Abrir arquivos do host",
waiting: "Aguardando o host do plugin…",
plugin: "Plugin",
workbench: "Área de trabalho",
connection: "Conexão",
lastEvent: "Último evento",
noConnection: "Nenhuma conexão salva",
ready: "Pronto. Clique no botão para invocar hello/greet.",
openSaved: "Abra esta área de trabalho a partir de uma conexão Hello salva.",
calling: "Invocando o sidecar nativo…",
error: "Erro",
},
};
const result = document.querySelector("#result");
const button = document.querySelector("#greet");
const input = document.querySelector("#name");
const files = document.querySelector("#files");
let connectionId = "";
let copy = translations.en;
function applyLocale(locale) {
copy = translations[locale] || translations.en;
document.documentElement.lang = locale || "en";
document.querySelector("#eyebrow").textContent = copy.eyebrow;
document.querySelector("#title-copy").textContent = copy.title;
document.querySelector("#description").textContent = copy.description;
input.setAttribute("aria-label", copy.name);
button.textContent = copy.invoke;
files.textContent = copy.files;
document.querySelector("#plugin-label").textContent = copy.plugin;
document.querySelector("#workbench-label").textContent = copy.workbench;
document.querySelector("#connection-label").textContent = copy.connection;
document.querySelector("#event-label").textContent = copy.lastEvent;
}
window.dbxPlugin.onEvent((event) => {
document.querySelector("#event").textContent = `${event.method}: ${JSON.stringify(event.params)}`;
});
window.dbxPlugin.ready.then((context) => {
applyLocale(window.dbxPlugin.locale);
document.querySelector("#plugin").textContent = "dbx.example.hello";
document.querySelector("#workbench").textContent = "dbx.example.hello.main";
connectionId = context?.connectionId || "";
document.querySelector("#connection").textContent = connectionId || copy.noConnection;
result.textContent = connectionId ? copy.ready : copy.openSaved;
});
button.addEventListener("click", async () => {
button.disabled = true;
result.textContent = copy.calling;
try {
const response = await window.dbxPlugin.invoke("hello/greet", { name: input.value, connectionId }, { timeoutMs: 5000 });
result.textContent = JSON.stringify(response, null, 2);
} catch (error) {
result.textContent = `${copy.error}: ${error.message}`;
} finally {
button.disabled = false;
}
});
files.addEventListener("click", async () => {
files.disabled = true;
try {
await window.dbxPlugin.openFilesystem("dbx.example.hello.files", { connectionId });
} catch (error) {
result.textContent = `${copy.error}: ${error.message}`;
} finally {
files.disabled = false;
}
});
</script>
</body>
</html>