mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-19 16:16:45 +00:00
feat(mcp): replace the authorization stepper with direct section tabs
This commit is contained in:
@@ -336,7 +336,7 @@ import ChangelogPanel from "@/components/settings/ChangelogPanel.vue";
|
||||
import SettingsTransferPanel from "@/components/settings/SettingsTransferPanel.vue";
|
||||
import McpResourceScopePicker from "@/components/settings/McpResourceScopePicker.vue";
|
||||
import McpDatabaseScopePicker from "@/components/settings/McpDatabaseScopePicker.vue";
|
||||
import McpAuthorizationStepper from "@/components/settings/McpAuthorizationStepper.vue";
|
||||
import McpAuthorizationTabs from "@/components/settings/McpAuthorizationTabs.vue";
|
||||
import ScheduledDatabaseBackupSettings from "@/components/backup/ScheduledDatabaseBackupSettings.vue";
|
||||
import SqlFormatterSettingsPanel from "./SqlFormatterSettingsPanel.vue";
|
||||
import { APP_CUSTOM_UI_COLOR_DEFS, APP_THEME_PALETTES, type AppCornerStyle, type AppCustomUiColors, type AppThemeAppearance, type AppThemeMode, type AppThemePalette } from "@/lib/app/appTheme";
|
||||
@@ -12254,7 +12254,7 @@ LIMIT 100;</pre>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="permissions" class="m-0 space-y-4">
|
||||
<McpAuthorizationStepper>
|
||||
<McpAuthorizationTabs>
|
||||
<template #connections>
|
||||
<div class="space-y-3">
|
||||
<p v-if="mcpPolicyLoadError" class="rounded-md border border-red-500/30 bg-red-500/5 px-3 py-2 text-xs text-red-600 dark:text-red-400">
|
||||
@@ -12422,7 +12422,7 @@ LIMIT 100;</pre>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
</McpAuthorizationStepper>
|
||||
</McpAuthorizationTabs>
|
||||
</TabsContent>
|
||||
<div v-if="mcpManagementTab === 'access'" class="space-y-5 border-t border-border/60 pt-5 sm:ml-1">
|
||||
<div class="space-y-3">
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
type StepId = "capabilities" | "connections" | "databases" | "overrides";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const steps = computed<Array<{ id: StepId; title: string; description: string }>>(() => [
|
||||
{ id: "overrides", title: t("settings.mcpAuthStepOverridesTitle"), description: t("settings.mcpAuthStepOverridesDescription") },
|
||||
{ id: "connections", title: t("settings.mcpAuthStepConnectionsTitle"), description: t("settings.mcpAuthStepConnectionsDescription") },
|
||||
{ id: "databases", title: t("settings.mcpAuthStepDatabasesTitle"), description: t("settings.mcpAuthStepDatabasesDescription") },
|
||||
{ id: "capabilities", title: t("settings.mcpAuthStepCapabilitiesTitle"), description: t("settings.mcpAuthStepCapabilitiesDescription") },
|
||||
]);
|
||||
|
||||
const currentStep = ref<StepId>("overrides");
|
||||
const currentIndex = computed(() => steps.value.findIndex((step) => step.id === currentStep.value));
|
||||
// Every step is directly reachable from the nav above and all policy edits stage into the
|
||||
// dialog draft, so the last step has nothing to advance to and nothing to confirm here —
|
||||
// persistence is the settings footer Apply button's job, not this stepper's.
|
||||
const isLastStep = computed(() => currentIndex.value === steps.value.length - 1);
|
||||
|
||||
function scrollableAncestor(target: EventTarget | null): HTMLElement | null {
|
||||
let element = target instanceof HTMLElement ? target.parentElement : null;
|
||||
while (element) {
|
||||
const overflowY = window.getComputedStyle(element).overflowY;
|
||||
if (overflowY === "auto" || overflowY === "scroll") return element;
|
||||
element = element.parentElement;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function selectStep(step: StepId, event?: Event) {
|
||||
if (step === currentStep.value) return;
|
||||
const scroller = scrollableAncestor(event?.currentTarget ?? null);
|
||||
const scrollTop = scroller?.scrollTop;
|
||||
currentStep.value = step;
|
||||
if (!scroller || scrollTop === undefined) return;
|
||||
await nextTick();
|
||||
requestAnimationFrame(() => {
|
||||
if (scroller.isConnected) scroller.scrollTop = scrollTop;
|
||||
});
|
||||
}
|
||||
|
||||
function previousStep(event: Event) {
|
||||
const step = steps.value[currentIndex.value - 1];
|
||||
if (step) void selectStep(step.id, event);
|
||||
}
|
||||
|
||||
function nextStep(event: Event) {
|
||||
const step = steps.value[currentIndex.value + 1];
|
||||
if (step) void selectStep(step.id, event);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="space-y-4 rounded-md border bg-background p-3 sm:p-4">
|
||||
<header class="space-y-1">
|
||||
<p class="text-sm font-semibold">{{ t("settings.mcpAuthTitle") }}</p>
|
||||
<p class="text-xs text-muted-foreground">{{ t("settings.mcpAuthDescription") }}</p>
|
||||
</header>
|
||||
|
||||
<nav class="flex items-stretch gap-1 overflow-x-auto rounded-md border bg-muted/50 p-1 sm:grid sm:grid-cols-4" :aria-label="t('settings.mcpAuthStepsLabel')">
|
||||
<button
|
||||
v-for="(step, index) in steps"
|
||||
:key="step.id"
|
||||
type="button"
|
||||
class="group flex min-h-16 min-w-48 flex-none items-start gap-2 rounded px-2.5 py-2 text-left transition-colors focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 sm:min-w-0"
|
||||
:class="currentStep === step.id ? 'bg-background shadow-sm' : 'text-muted-foreground hover:bg-background/70'"
|
||||
:aria-current="currentStep === step.id ? 'step' : undefined"
|
||||
@click="selectStep(step.id, $event)"
|
||||
>
|
||||
<span
|
||||
class="flex h-5 w-5 shrink-0 items-center justify-center rounded-full border text-[11px] font-semibold"
|
||||
:class="currentStep === step.id ? 'border-primary bg-primary text-primary-foreground' : index < currentIndex ? 'border-primary/40 bg-primary/10 text-primary' : 'bg-background text-muted-foreground'"
|
||||
>{{ index + 1 }}</span
|
||||
>
|
||||
<span class="min-w-0 flex-1">
|
||||
<span class="block whitespace-normal text-xs font-medium leading-5">{{ step.title }}</span>
|
||||
<span class="mt-0.5 block whitespace-normal text-[10px] leading-4 text-muted-foreground">{{ step.description }}</span>
|
||||
</span>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<div :class="currentStep === 'overrides' ? 'block' : 'hidden'" :aria-hidden="currentStep !== 'overrides'" role="region" :aria-label="t('settings.mcpAuthStepRegionLabel', { step: 1, title: t('settings.mcpAuthStepOverridesTitle') })"><slot name="overrides" /></div>
|
||||
<div :class="currentStep === 'connections' ? 'block' : 'hidden'" :aria-hidden="currentStep !== 'connections'" role="region" :aria-label="t('settings.mcpAuthStepRegionLabel', { step: 2, title: t('settings.mcpAuthStepConnectionsTitle') })"><slot name="connections" /></div>
|
||||
<div :class="currentStep === 'databases' ? 'block' : 'hidden'" :aria-hidden="currentStep !== 'databases'" role="region" :aria-label="t('settings.mcpAuthStepRegionLabel', { step: 3, title: t('settings.mcpAuthStepDatabasesTitle') })"><slot name="databases" /></div>
|
||||
<div :class="currentStep === 'capabilities' ? 'block' : 'hidden'" :aria-hidden="currentStep !== 'capabilities'" role="region" :aria-label="t('settings.mcpAuthStepRegionLabel', { step: 4, title: t('settings.mcpAuthStepCapabilitiesTitle') })"><slot name="capabilities" /></div>
|
||||
|
||||
<footer class="flex items-center justify-between gap-3 border-t pt-3">
|
||||
<button type="button" class="rounded-md border bg-background px-3 py-1.5 text-xs font-medium disabled:cursor-not-allowed disabled:opacity-50" :disabled="currentIndex === 0" @click="previousStep($event)">{{ t("settings.mcpAuthPreviousStep") }}</button>
|
||||
<p class="text-center text-xs text-muted-foreground">{{ t("settings.mcpAuthStepProgress", { current: currentIndex + 1, total: steps.length }) }}</p>
|
||||
<!-- Kept in the layout (visibility: hidden) so the progress label holds the same offset in
|
||||
every step: a fixed-width spacer could not match a label whose width varies per locale.
|
||||
`invisible` also drops the button from the tab order and the accessibility tree. -->
|
||||
<button type="button" class="rounded-md border bg-background px-3 py-1.5 text-xs font-medium disabled:cursor-not-allowed disabled:opacity-50" :class="{ invisible: isLastStep }" :disabled="isLastStep" @click="nextStep($event)">
|
||||
{{ t("settings.mcpAuthNextStep") }}
|
||||
</button>
|
||||
</footer>
|
||||
</section>
|
||||
</template>
|
||||
@@ -0,0 +1,39 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
|
||||
type SectionId = "overrides" | "connections" | "databases" | "capabilities";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const sections = computed<Array<{ id: SectionId; title: string }>>(() => [
|
||||
{ id: "overrides", title: t("settings.mcpAuthTabOverridesTitle") },
|
||||
{ id: "connections", title: t("settings.mcpAuthTabConnectionsTitle") },
|
||||
{ id: "databases", title: t("settings.mcpAuthTabDatabasesTitle") },
|
||||
{ id: "capabilities", title: t("settings.mcpAuthTabCapabilitiesTitle") },
|
||||
]);
|
||||
|
||||
const currentSection = ref<SectionId>("overrides");
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="space-y-3">
|
||||
<header class="space-y-1">
|
||||
<p class="text-sm font-semibold">{{ t("settings.mcpAuthTitle") }}</p>
|
||||
<p class="text-xs text-muted-foreground">{{ t("settings.mcpAuthDescription") }}</p>
|
||||
</header>
|
||||
|
||||
<!-- The sections are independent views over one staged policy draft (persisted by the settings
|
||||
footer Apply button), not sequential steps, so every one is a directly reachable tab. Hidden
|
||||
panels stay mounted (unmount-on-hide=false) so the pickers keep their search/expand state across tab switches. -->
|
||||
<Tabs v-model="currentSection" :unmount-on-hide="false" class="gap-4">
|
||||
<TabsList variant="line" class="h-9 w-full justify-start gap-1 overflow-x-auto border-b border-border/60 bg-transparent p-0" :aria-label="t('settings.mcpAuthTitle')">
|
||||
<TabsTrigger v-for="section in sections" :key="section.id" :value="section.id" class="flex-none px-3">{{ section.title }}</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent v-for="section in sections" :key="section.id" :value="section.id" class="m-0">
|
||||
<slot :name="section.id" />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</section>
|
||||
</template>
|
||||
-164
@@ -1,164 +0,0 @@
|
||||
// @vitest-environment happy-dom
|
||||
// MCP 授权步骤条回归测试(#9309):
|
||||
// 末步(第 4 步「汇总与确认」)没有下一页可去,右侧按钮不再显示可点的“下一步”。
|
||||
// 1) 第 1-3 步:右侧“下一步”可见可点,点击前进一格;左侧“上一步”第 1 步禁用、其后可点。
|
||||
// 2) 第 4 步:右侧按钮带 invisible 且 disabled,但仍留在 footer 中作同尺寸占位。
|
||||
// 3) 第 4 步点“上一步”回到第 3 步后,右侧按钮恢复可见。
|
||||
// 4) 顶部步骤条四个步骤都能直达,末步右侧按钮同样隐藏。
|
||||
// 5) 末步占位按钮不可聚焦。
|
||||
// 6) 正向对照:非末步的“下一步”按钮可以聚焦——证明这个环境能表达焦点,第 5 条才不是恒真。
|
||||
//
|
||||
// 哪些用例真正守住 #9309:第 2、4 条(断言 invisible 与 footer 计数)。把组件还原到修复前,
|
||||
// 恰好是这两条失败。第 5 条在修复前也通过——末步按钮本来就带 disabled,所以它是防止后人
|
||||
// 删掉 disabled 的前瞻守卫,而不是本次缺陷的回归守卫,勿误读。
|
||||
//
|
||||
// 说明:happy-dom 无排版引擎、不加载 Tailwind 样式表,因此“进度文案是否真的居中/不位移”
|
||||
// 这类几何断言在单测里无法表达,只以“占位仍留在 DOM 中”作为可证伪的替代断言。
|
||||
|
||||
import { createApp, nextTick, type App } from "vue";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("vue-i18n", () => ({
|
||||
useI18n: () => ({
|
||||
t: (key: string, params?: Record<string, unknown>) => (params ? `${key}:${params.current}/${params.total}` : key),
|
||||
}),
|
||||
}));
|
||||
|
||||
import McpAuthorizationStepper from "@/components/settings/McpAuthorizationStepper.vue";
|
||||
|
||||
let harness: { app: App<Element>; root: HTMLElement } | null = null;
|
||||
|
||||
function mountStepper() {
|
||||
const root = document.createElement("div");
|
||||
document.body.appendChild(root);
|
||||
const app = createApp(McpAuthorizationStepper);
|
||||
app.mount(root);
|
||||
harness = { app, root };
|
||||
return root;
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
harness?.app?.unmount();
|
||||
harness = null;
|
||||
document.body.innerHTML = "";
|
||||
});
|
||||
|
||||
function buttonByText(root: HTMLElement, key: string): HTMLButtonElement {
|
||||
const button = Array.from(root.querySelectorAll("button")).find((el) => el.textContent?.trim() === key);
|
||||
if (!button) throw new Error(`button ${key} not found`);
|
||||
return button as HTMLButtonElement;
|
||||
}
|
||||
|
||||
function nextButton(root: HTMLElement) {
|
||||
return buttonByText(root, "settings.mcpAuthNextStep");
|
||||
}
|
||||
|
||||
function prevButton(root: HTMLElement) {
|
||||
return buttonByText(root, "settings.mcpAuthPreviousStep");
|
||||
}
|
||||
|
||||
function navStep(root: HTMLElement, index: number) {
|
||||
const nav = root.querySelector("nav");
|
||||
if (!nav) throw new Error("step nav not found");
|
||||
const button = nav.querySelectorAll("button")[index];
|
||||
if (!button) throw new Error(`nav step ${index} not found`);
|
||||
return button as HTMLButtonElement;
|
||||
}
|
||||
|
||||
function progress(root: HTMLElement) {
|
||||
return root.textContent?.match(/settings\.mcpAuthStepProgress:\d+\/\d+/)?.[0];
|
||||
}
|
||||
|
||||
async function goToLastStep(root: HTMLElement) {
|
||||
for (let i = 0; i < 3; i += 1) {
|
||||
nextButton(root).click();
|
||||
await nextTick();
|
||||
}
|
||||
}
|
||||
|
||||
describe("McpAuthorizationStepper", () => {
|
||||
it("第 1 步:右侧为可见可点的“下一步”,左侧“上一步”禁用", () => {
|
||||
const root = mountStepper();
|
||||
|
||||
expect(progress(root)).toBe("settings.mcpAuthStepProgress:1/4");
|
||||
expect(nextButton(root).classList.contains("invisible")).toBe(false);
|
||||
expect(nextButton(root).disabled).toBe(false);
|
||||
expect(prevButton(root).disabled).toBe(true);
|
||||
});
|
||||
|
||||
it("逐级前进:第 2、3 步右侧仍是可见可点的“下一步”,左侧“上一步”可点", async () => {
|
||||
const root = mountStepper();
|
||||
|
||||
nextButton(root).click();
|
||||
await nextTick();
|
||||
expect(progress(root)).toBe("settings.mcpAuthStepProgress:2/4");
|
||||
expect(nextButton(root).classList.contains("invisible")).toBe(false);
|
||||
expect(nextButton(root).disabled).toBe(false);
|
||||
expect(prevButton(root).disabled).toBe(false);
|
||||
|
||||
nextButton(root).click();
|
||||
await nextTick();
|
||||
expect(progress(root)).toBe("settings.mcpAuthStepProgress:3/4");
|
||||
expect(nextButton(root).classList.contains("invisible")).toBe(false);
|
||||
expect(nextButton(root).disabled).toBe(false);
|
||||
expect(prevButton(root).disabled).toBe(false);
|
||||
});
|
||||
|
||||
it("第 4 步:右侧按钮隐藏且禁用,但仍留在 footer 中撑住布局", async () => {
|
||||
const root = mountStepper();
|
||||
await goToLastStep(root);
|
||||
|
||||
expect(progress(root)).toBe("settings.mcpAuthStepProgress:4/4");
|
||||
// footer 仍是 [上一步][进度][占位] 三个子元素,即占位按钮没有被 v-if 摘掉——摘掉的话这里只剩 1 个。
|
||||
expect(root.querySelectorAll("footer button").length).toBe(2);
|
||||
|
||||
const next = nextButton(root);
|
||||
expect(next.classList.contains("invisible")).toBe(true);
|
||||
expect(next.disabled).toBe(true);
|
||||
expect(prevButton(root).disabled).toBe(false);
|
||||
});
|
||||
|
||||
it("第 4 步点“上一步”回到第 3 步后,右侧按钮恢复可见可点", async () => {
|
||||
const root = mountStepper();
|
||||
await goToLastStep(root);
|
||||
|
||||
prevButton(root).click();
|
||||
await nextTick();
|
||||
|
||||
expect(progress(root)).toBe("settings.mcpAuthStepProgress:3/4");
|
||||
expect(nextButton(root).classList.contains("invisible")).toBe(false);
|
||||
expect(nextButton(root).disabled).toBe(false);
|
||||
});
|
||||
|
||||
it("顶部步骤条四个步骤均可直达,末步的右侧按钮同样隐藏", async () => {
|
||||
const root = mountStepper();
|
||||
|
||||
for (let index = 0; index < 4; index += 1) {
|
||||
navStep(root, index).click();
|
||||
await nextTick();
|
||||
expect(progress(root)).toBe(`settings.mcpAuthStepProgress:${index + 1}/4`);
|
||||
expect(nextButton(root).classList.contains("invisible")).toBe(index === 3);
|
||||
}
|
||||
expect(nextButton(root).disabled).toBe(true);
|
||||
});
|
||||
|
||||
// 正向对照:证明这个环境里 focus() 确实能生效,下面那条否定断言才不是恒真。
|
||||
it("正向对照:非末步的“下一步”按钮可以聚焦", () => {
|
||||
const root = mountStepper();
|
||||
|
||||
nextButton(root).focus();
|
||||
expect(document.activeElement).toBe(nextButton(root));
|
||||
});
|
||||
|
||||
// 每个用例独立挂载、起始焦点都不在任何按钮上——避免“元素已在焦点上、随后才被禁用”这种
|
||||
// 与断言无关的状态混进来(原生化不会因为元素被禁用就把已有焦点移走)。
|
||||
it("末步占位按钮不可聚焦", async () => {
|
||||
const root = mountStepper();
|
||||
await goToLastStep(root);
|
||||
|
||||
const next = nextButton(root);
|
||||
next.focus();
|
||||
expect(document.activeElement).not.toBe(next);
|
||||
expect(next.disabled).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,104 @@
|
||||
// @vitest-environment happy-dom
|
||||
// MCP 授权设置页签:四个区块是同一份暂存策略的独立视图,不是线性流程,
|
||||
// 因此不再有“上一步/下一步/第 N 步”,而是可直达的页签。
|
||||
// 1) 四个页签可直达,默认选中“默认权限”。
|
||||
// 2) 切换页签只改变可见面板;所有面板保持挂载(子组件的搜索/展开状态不丢)。
|
||||
// 3) 不再渲染流程式的上一步/下一步/进度控件。
|
||||
|
||||
import { createApp, nextTick, type App } from "vue";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("vue-i18n", () => ({
|
||||
useI18n: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
import McpAuthorizationTabs from "@/components/settings/McpAuthorizationTabs.vue";
|
||||
|
||||
const SECTIONS = ["overrides", "connections", "databases", "capabilities"] as const;
|
||||
|
||||
let harness: { app: App<Element>; root: HTMLElement } | null = null;
|
||||
|
||||
function mountTabs() {
|
||||
const root = document.createElement("div");
|
||||
document.body.appendChild(root);
|
||||
const app = createApp({
|
||||
components: { McpAuthorizationTabs },
|
||||
template: `
|
||||
<McpAuthorizationTabs>
|
||||
<template #overrides><p data-slot-content="overrides">overrides</p></template>
|
||||
<template #connections><p data-slot-content="connections">connections</p></template>
|
||||
<template #databases><p data-slot-content="databases">databases</p></template>
|
||||
<template #capabilities><p data-slot-content="capabilities">capabilities</p></template>
|
||||
</McpAuthorizationTabs>
|
||||
`,
|
||||
});
|
||||
app.mount(root);
|
||||
harness = { app, root };
|
||||
return root;
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
harness?.app?.unmount();
|
||||
harness = null;
|
||||
document.body.innerHTML = "";
|
||||
});
|
||||
|
||||
function triggers(root: HTMLElement) {
|
||||
return Array.from(root.querySelectorAll<HTMLElement>('[role="tab"]'));
|
||||
}
|
||||
|
||||
function panelOf(root: HTMLElement, section: string) {
|
||||
const content = root.querySelector(`[data-slot-content="${section}"]`);
|
||||
const panel = content?.closest<HTMLElement>('[role="tabpanel"]');
|
||||
if (!panel) throw new Error(`panel ${section} not found`);
|
||||
return panel;
|
||||
}
|
||||
|
||||
async function selectTab(tab: HTMLElement) {
|
||||
// reka-ui 的触发器在 mousedown / focus 时激活,而不是 click。
|
||||
tab.dispatchEvent(new MouseEvent("mousedown", { bubbles: true, button: 0, ctrlKey: false }));
|
||||
await nextTick();
|
||||
}
|
||||
|
||||
describe("McpAuthorizationTabs", () => {
|
||||
it("渲染四个页签,默认选中第一个", () => {
|
||||
const root = mountTabs();
|
||||
|
||||
const tabs = triggers(root);
|
||||
expect(tabs.map((tab) => tab.textContent?.trim())).toEqual(["settings.mcpAuthTabOverridesTitle", "settings.mcpAuthTabConnectionsTitle", "settings.mcpAuthTabDatabasesTitle", "settings.mcpAuthTabCapabilitiesTitle"]);
|
||||
expect(tabs[0].getAttribute("aria-selected")).toBe("true");
|
||||
expect(panelOf(root, "overrides").hidden).toBe(false);
|
||||
expect(panelOf(root, "databases").hidden).toBe(true);
|
||||
});
|
||||
|
||||
it("每个页签都能直达,且只显示被选中的面板", async () => {
|
||||
const root = mountTabs();
|
||||
|
||||
for (const [index, section] of SECTIONS.entries()) {
|
||||
await selectTab(triggers(root)[index]);
|
||||
expect(triggers(root)[index].getAttribute("aria-selected")).toBe("true");
|
||||
for (const other of SECTIONS) {
|
||||
// data-state 由选中态直接驱动;hidden 经由 Presence 判定,happy-dom 下没有动画事件,故不以它为准。
|
||||
expect(panelOf(root, other).getAttribute("data-state"), `${section} selected, ${other}`).toBe(other === section ? "active" : "inactive");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("所有面板始终保持挂载", async () => {
|
||||
const root = mountTabs();
|
||||
await selectTab(triggers(root)[2]);
|
||||
|
||||
for (const section of SECTIONS) {
|
||||
expect(root.querySelector(`[data-slot-content="${section}"]`), section).not.toBeNull();
|
||||
}
|
||||
});
|
||||
|
||||
it("不再渲染上一步/下一步/进度等流程控件", () => {
|
||||
const root = mountTabs();
|
||||
|
||||
expect(root.querySelector("footer")).toBeNull();
|
||||
expect(root.textContent).not.toMatch(/PreviousStep|NextStep|StepProgress/);
|
||||
});
|
||||
});
|
||||
@@ -8438,20 +8438,11 @@ export default withEnglishFallback({
|
||||
mcpRefresh: "Yenidən yoxla",
|
||||
mcpGuide: "MCP təlimatı",
|
||||
mcpAuthTitle: "MCP səlahiyyətləndirməsi",
|
||||
mcpAuthDescription: "Səlahiyyətləndirməni ardıcıllıqla tamamlayın: qlobal standartı təyin edin, qrupları və əlaqələri seçin, verilənlər bazalarını məhdudlaşdırın, sonra yekun siyasəti nəzərdən keçirin.",
|
||||
mcpAuthStepsLabel: "MCP səlahiyyətləndirmə addımları",
|
||||
mcpAuthStepConnectionsTitle: "Qruplar və əlaqələr",
|
||||
mcpAuthStepConnectionsDescription: "MCP-nin nəyi görə biləcəyini seçin və əlaqə icazələrini təyin edin",
|
||||
mcpAuthStepDatabasesTitle: "Verilənlər bazası əhatəsi",
|
||||
mcpAuthStepDatabasesDescription: "Hər əlaqənin verilənlər bazalarını məhdudlaşdırın və verilənlər bazası icazələrini təyin edin",
|
||||
mcpAuthStepOverridesTitle: "Standart icazələr",
|
||||
mcpAuthStepOverridesDescription: "Bütün sonrakı əhatə dairələri üçün qlobal standartı təyin edin",
|
||||
mcpAuthStepCapabilitiesTitle: "Xülasə və baxış",
|
||||
mcpAuthStepCapabilitiesDescription: "Qüvvədə olan icazələri nəzərdən keçirin, alətləri seçin və təsdiqləyin",
|
||||
mcpAuthStepRegionLabel: "Addım {step}: {title}",
|
||||
mcpAuthPreviousStep: "Əvvəlki",
|
||||
mcpAuthNextStep: "Növbəti",
|
||||
mcpAuthStepProgress: "Addım {current}/{total}",
|
||||
mcpAuthDescription: "İcazələr pilləli şəkildə üstələnir: verilənlər bazası › əlaqə › qrup › qlobal standart. Əhatə nə qədər dar olarsa, prioritet bir o qədər yüksəkdir.",
|
||||
mcpAuthTabConnectionsTitle: "Qruplar və əlaqələr",
|
||||
mcpAuthTabDatabasesTitle: "Verilənlər bazası əhatəsi",
|
||||
mcpAuthTabOverridesTitle: "Standart icazələr",
|
||||
mcpAuthTabCapabilitiesTitle: "Qüvvədə olan giriş və alətlər",
|
||||
mcpPolicyExceptionsTitle: "Saxlanılmış icazə istisnaları",
|
||||
mcpPolicyExceptionsDescription: "Bağlantı əhatəsinin dəyişdirilməsi icazə istisnalarını saxlayır. Əhatə daxilindəki istisnalar qüvvədə qalır; əhatədən kənardakı istisnalar bağlantı əhatəyə qayıtdıqda tətbiq olunur. Burada icra icazələrini dəyişə və ya irsi icazələri bərpa edə bilərsiniz.",
|
||||
mcpPolicyExceptionInScope: "Cari əhatə daxilində",
|
||||
|
||||
@@ -9956,20 +9956,11 @@ export default {
|
||||
mcpRefresh: "Check again",
|
||||
mcpGuide: "MCP guide",
|
||||
mcpAuthTitle: "MCP authorization",
|
||||
mcpAuthDescription: "Complete authorization in order: set the global default, choose groups and connections, limit databases, then review the final policy.",
|
||||
mcpAuthStepsLabel: "MCP authorization steps",
|
||||
mcpAuthStepConnectionsTitle: "Groups and connections",
|
||||
mcpAuthStepConnectionsDescription: "Choose what MCP can see and set connection permissions",
|
||||
mcpAuthStepDatabasesTitle: "Database scope",
|
||||
mcpAuthStepDatabasesDescription: "Limit each connection's databases and set database permissions",
|
||||
mcpAuthStepOverridesTitle: "Default permissions",
|
||||
mcpAuthStepOverridesDescription: "Set the global default for all later scopes",
|
||||
mcpAuthStepCapabilitiesTitle: "Summary and review",
|
||||
mcpAuthStepCapabilitiesDescription: "Review effective permissions, choose tools, and confirm",
|
||||
mcpAuthStepRegionLabel: "Step {step}: {title}",
|
||||
mcpAuthPreviousStep: "Previous",
|
||||
mcpAuthNextStep: "Next",
|
||||
mcpAuthStepProgress: "Step {current} of {total}",
|
||||
mcpAuthDescription: "Permissions are overridden level by level: database › connection › group › global default. The narrower the scope, the higher its priority.",
|
||||
mcpAuthTabConnectionsTitle: "Groups and connections",
|
||||
mcpAuthTabDatabasesTitle: "Database scope",
|
||||
mcpAuthTabOverridesTitle: "Default permissions",
|
||||
mcpAuthTabCapabilitiesTitle: "Effective access and tools",
|
||||
mcpPolicyExceptionsTitle: "Saved permission exceptions",
|
||||
mcpPolicyExceptionsDescription: "Changing the connection scope preserves permission exceptions. Exceptions inside the scope remain effective; those outside apply when the connection returns to the scope. You can edit execution permissions or restore inherited permissions here.",
|
||||
mcpPolicyExceptionInScope: "Inside current scope",
|
||||
|
||||
@@ -8999,20 +8999,11 @@ export default withEnglishFallback({
|
||||
mcpRefresh: "Comprobar de nuevo",
|
||||
mcpGuide: "Guía MCP",
|
||||
mcpAuthTitle: "Autorización de MCP",
|
||||
mcpAuthDescription: "Completa la autorización en orden: define el valor global, elige grupos y conexiones, limita las bases de datos y revisa la política final.",
|
||||
mcpAuthStepsLabel: "Pasos de autorización de MCP",
|
||||
mcpAuthStepConnectionsTitle: "Grupos y conexiones",
|
||||
mcpAuthStepConnectionsDescription: "Elige lo que MCP puede ver y configura los permisos",
|
||||
mcpAuthStepDatabasesTitle: "Alcance de bases de datos",
|
||||
mcpAuthStepDatabasesDescription: "Limita las bases de datos y configura permisos por base",
|
||||
mcpAuthStepOverridesTitle: "Permisos predeterminados",
|
||||
mcpAuthStepOverridesDescription: "Define el permiso global para los ámbitos posteriores",
|
||||
mcpAuthStepCapabilitiesTitle: "Resumen y confirmación",
|
||||
mcpAuthStepCapabilitiesDescription: "Revisa los permisos efectivos, elige herramientas y confirma",
|
||||
mcpAuthStepRegionLabel: "Paso {step}: {title}",
|
||||
mcpAuthPreviousStep: "Anterior",
|
||||
mcpAuthNextStep: "Siguiente",
|
||||
mcpAuthStepProgress: "Paso {current} de {total}",
|
||||
mcpAuthDescription: "Los permisos se sobrescriben por niveles: base de datos › conexión › grupo › valor global. Cuanto más específico es el alcance, mayor es su prioridad.",
|
||||
mcpAuthTabConnectionsTitle: "Grupos y conexiones",
|
||||
mcpAuthTabDatabasesTitle: "Alcance de bases de datos",
|
||||
mcpAuthTabOverridesTitle: "Permisos predeterminados",
|
||||
mcpAuthTabCapabilitiesTitle: "Acceso efectivo y herramientas",
|
||||
mcpPolicyExceptionsTitle: "Excepciones de permisos guardadas",
|
||||
mcpPolicyExceptionsDescription: "Cambiar el ámbito de conexiones conserva las excepciones de permisos. Las excepciones dentro del ámbito siguen vigentes; las demás se aplican cuando la conexión vuelve al ámbito. Aquí puede editar los permisos de ejecución o restaurar los permisos heredados.",
|
||||
mcpPolicyExceptionInScope: "Dentro del ámbito actual",
|
||||
|
||||
@@ -9254,20 +9254,11 @@ export default withEnglishFallback({
|
||||
mcpRefresh: "Periksa lagi",
|
||||
mcpGuide: "Panduan MCP",
|
||||
mcpAuthTitle: "Otorisasi MCP",
|
||||
mcpAuthDescription: "Selesaikan otorisasi secara berurutan: atur default global, pilih grup dan koneksi, batasi database, lalu tinjau kebijakan akhir.",
|
||||
mcpAuthStepsLabel: "Langkah otorisasi MCP",
|
||||
mcpAuthStepConnectionsTitle: "Grup dan koneksi",
|
||||
mcpAuthStepConnectionsDescription: "Pilih apa yang dapat dilihat MCP dan atur izin koneksi",
|
||||
mcpAuthStepDatabasesTitle: "Cakupan database",
|
||||
mcpAuthStepDatabasesDescription: "Batasi database setiap koneksi dan atur izin database",
|
||||
mcpAuthStepOverridesTitle: "Izin default",
|
||||
mcpAuthStepOverridesDescription: "Atur default global untuk semua cakupan berikutnya",
|
||||
mcpAuthStepCapabilitiesTitle: "Ringkasan dan tinjauan",
|
||||
mcpAuthStepCapabilitiesDescription: "Tinjau izin efektif, pilih alat, dan konfirmasi",
|
||||
mcpAuthStepRegionLabel: "Langkah {step}: {title}",
|
||||
mcpAuthPreviousStep: "Sebelumnya",
|
||||
mcpAuthNextStep: "Berikutnya",
|
||||
mcpAuthStepProgress: "Langkah {current} dari {total}",
|
||||
mcpAuthDescription: "Izin ditimpa secara bertingkat: database › koneksi › grup › default global. Semakin sempit cakupannya, semakin tinggi prioritasnya.",
|
||||
mcpAuthTabConnectionsTitle: "Grup dan koneksi",
|
||||
mcpAuthTabDatabasesTitle: "Cakupan database",
|
||||
mcpAuthTabOverridesTitle: "Izin default",
|
||||
mcpAuthTabCapabilitiesTitle: "Akses efektif dan alat",
|
||||
mcpPolicyExceptionsTitle: "Pengecualian izin tersimpan",
|
||||
mcpPolicyExceptionsDescription: "Mengubah cakupan koneksi mempertahankan pengecualian izin. Pengecualian dalam cakupan tetap berlaku; pengecualian di luar cakupan berlaku saat koneksi kembali ke cakupan. Anda dapat mengedit izin eksekusi atau memulihkan izin yang diwarisi di sini.",
|
||||
mcpPolicyExceptionInScope: "Dalam cakupan saat ini",
|
||||
|
||||
@@ -8749,20 +8749,11 @@ export default withEnglishFallback({
|
||||
mcpRefresh: "Verifica di nuovo",
|
||||
mcpGuide: "Guida MCP",
|
||||
mcpAuthTitle: "Autorizzazione MCP",
|
||||
mcpAuthDescription: "Completa l'autorizzazione in ordine: imposta il valore globale, scegli gruppi e connessioni, limita i database e rivedi la policy finale.",
|
||||
mcpAuthStepsLabel: "Passaggi di autorizzazione MCP",
|
||||
mcpAuthStepConnectionsTitle: "Gruppi e connessioni",
|
||||
mcpAuthStepConnectionsDescription: "Determina quali connessioni MCP può vedere",
|
||||
mcpAuthStepDatabasesTitle: "Ambito database",
|
||||
mcpAuthStepDatabasesDescription: "Limita i database accessibili per ogni connessione",
|
||||
mcpAuthStepOverridesTitle: "Permessi predefiniti",
|
||||
mcpAuthStepOverridesDescription: "Imposta il permesso globale per gli ambiti successivi",
|
||||
mcpAuthStepCapabilitiesTitle: "Riepilogo e conferma",
|
||||
mcpAuthStepCapabilitiesDescription: "Rivedi i permessi effettivi, scegli gli strumenti e conferma",
|
||||
mcpAuthStepRegionLabel: "Passaggio {step}: {title}",
|
||||
mcpAuthPreviousStep: "Indietro",
|
||||
mcpAuthNextStep: "Avanti",
|
||||
mcpAuthStepProgress: "Passaggio {current} di {total}",
|
||||
mcpAuthDescription: "I permessi vengono sovrascritti per livelli: database › connessione › gruppo › valore globale. Più è ristretto l'ambito, più alta è la priorità.",
|
||||
mcpAuthTabConnectionsTitle: "Gruppi e connessioni",
|
||||
mcpAuthTabDatabasesTitle: "Ambito database",
|
||||
mcpAuthTabOverridesTitle: "Permessi predefiniti",
|
||||
mcpAuthTabCapabilitiesTitle: "Accesso effettivo e strumenti",
|
||||
mcpPolicyExceptionsTitle: "Eccezioni ai permessi salvate",
|
||||
mcpPolicyExceptionsDescription: "La modifica dell’ambito delle connessioni conserva le eccezioni ai permessi. Le eccezioni nell’ambito restano attive; le altre si applicano quando la connessione rientra nell’ambito. Qui puoi modificare i permessi di esecuzione o ripristinare quelli ereditati.",
|
||||
mcpPolicyExceptionInScope: "Nell’ambito attuale",
|
||||
|
||||
@@ -8736,20 +8736,11 @@ export default withEnglishFallback({
|
||||
mcpRefresh: "再確認",
|
||||
mcpGuide: "MCPガイド",
|
||||
mcpAuthTitle: "MCP 権限設定",
|
||||
mcpAuthDescription: "順番に設定します:グローバル既定値、グループと接続、データベース範囲を設定し、最後に最終ポリシーを確認します。",
|
||||
mcpAuthStepsLabel: "MCP 権限設定の手順",
|
||||
mcpAuthStepConnectionsTitle: "グループと接続",
|
||||
mcpAuthStepConnectionsDescription: "MCP に公開する接続を決定します",
|
||||
mcpAuthStepDatabasesTitle: "データベース範囲",
|
||||
mcpAuthStepDatabasesDescription: "接続ごとにアクセス可能なデータベースを制限します",
|
||||
mcpAuthStepOverridesTitle: "既定の権限",
|
||||
mcpAuthStepOverridesDescription: "後続の範囲に適用するグローバル既定値を設定します",
|
||||
mcpAuthStepCapabilitiesTitle: "概要と確認",
|
||||
mcpAuthStepCapabilitiesDescription: "有効な権限を確認し、ツールを選択して確定します",
|
||||
mcpAuthStepRegionLabel: "ステップ {step}:{title}",
|
||||
mcpAuthPreviousStep: "前へ",
|
||||
mcpAuthNextStep: "次へ",
|
||||
mcpAuthStepProgress: "ステップ {current} / {total}",
|
||||
mcpAuthDescription: "権限は範囲が狭いものが優先して上書きされます:データベース › 接続 › グループ › グローバル既定値。",
|
||||
mcpAuthTabConnectionsTitle: "グループと接続",
|
||||
mcpAuthTabDatabasesTitle: "データベース範囲",
|
||||
mcpAuthTabOverridesTitle: "既定の権限",
|
||||
mcpAuthTabCapabilitiesTitle: "有効な権限とツール",
|
||||
mcpPolicyExceptionsTitle: "保存された権限の例外",
|
||||
mcpPolicyExceptionsDescription: "接続範囲を変更しても権限の例外は保持されます。範囲内の例外は引き続き有効で、範囲外の例外は接続が範囲に戻ると適用されます。ここで実行権限を変更したり、継承する権限に戻したりできます。",
|
||||
mcpPolicyExceptionInScope: "現在の範囲内",
|
||||
|
||||
@@ -8642,20 +8642,11 @@ export default withEnglishFallback({
|
||||
mcpRefresh: "다시 확인",
|
||||
mcpGuide: "MCP 가이드",
|
||||
mcpAuthTitle: "MCP 권한 설정",
|
||||
mcpAuthDescription: "순서대로 권한을 설정하세요: 전역 기본값을 정하고 그룹과 연결, 데이터베이스 범위를 설정한 뒤 최종 정책을 확인합니다.",
|
||||
mcpAuthStepsLabel: "MCP 권한 설정 단계",
|
||||
mcpAuthStepConnectionsTitle: "그룹 및 연결",
|
||||
mcpAuthStepConnectionsDescription: "MCP에 노출할 연결을 결정합니다",
|
||||
mcpAuthStepDatabasesTitle: "데이터베이스 범위",
|
||||
mcpAuthStepDatabasesDescription: "연결별로 접근 가능한 데이터베이스를 제한합니다",
|
||||
mcpAuthStepOverridesTitle: "기본 권한",
|
||||
mcpAuthStepOverridesDescription: "이후 범위에 적용할 전역 기본 권한을 설정합니다",
|
||||
mcpAuthStepCapabilitiesTitle: "요약 및 확인",
|
||||
mcpAuthStepCapabilitiesDescription: "최종 권한을 검토하고 도구를 선택한 뒤 확인합니다",
|
||||
mcpAuthStepRegionLabel: "단계 {step}: {title}",
|
||||
mcpAuthPreviousStep: "이전",
|
||||
mcpAuthNextStep: "다음",
|
||||
mcpAuthStepProgress: "{total}단계 중 {current}단계",
|
||||
mcpAuthDescription: "권한은 단계별로 덮어씁니다: 데이터베이스 › 연결 › 그룹 › 전역 기본값. 범위가 좁을수록 우선순위가 높습니다.",
|
||||
mcpAuthTabConnectionsTitle: "그룹 및 연결",
|
||||
mcpAuthTabDatabasesTitle: "데이터베이스 범위",
|
||||
mcpAuthTabOverridesTitle: "기본 권한",
|
||||
mcpAuthTabCapabilitiesTitle: "적용 권한 및 도구",
|
||||
mcpPolicyExceptionsTitle: "저장된 권한 예외",
|
||||
mcpPolicyExceptionsDescription: "연결 범위를 변경해도 권한 예외는 유지됩니다. 범위 안의 예외는 계속 유효하며, 범위 밖의 예외는 연결이 범위에 다시 포함되면 적용됩니다. 여기서 실행 권한을 수정하거나 상속된 권한으로 되돌릴 수 있습니다.",
|
||||
mcpPolicyExceptionInScope: "현재 범위 안",
|
||||
|
||||
@@ -8747,20 +8747,11 @@ export default withEnglishFallback({
|
||||
mcpRefresh: "Verificar novamente",
|
||||
mcpGuide: "Guia do MCP",
|
||||
mcpAuthTitle: "Autorização do MCP",
|
||||
mcpAuthDescription: "Conclua a autorização em ordem: defina o padrão global, escolha grupos e conexões, limite os bancos e revise a política final.",
|
||||
mcpAuthStepsLabel: "Etapas de autorização do MCP",
|
||||
mcpAuthStepConnectionsTitle: "Grupos e conexões",
|
||||
mcpAuthStepConnectionsDescription: "Define quais conexões o MCP pode ver",
|
||||
mcpAuthStepDatabasesTitle: "Escopo de bancos de dados",
|
||||
mcpAuthStepDatabasesDescription: "Limita os bancos acessíveis por conexão",
|
||||
mcpAuthStepOverridesTitle: "Permissões padrão",
|
||||
mcpAuthStepOverridesDescription: "Defina a permissão global para os escopos seguintes",
|
||||
mcpAuthStepCapabilitiesTitle: "Resumo e confirmação",
|
||||
mcpAuthStepCapabilitiesDescription: "Revise as permissões efetivas, escolha as ferramentas e confirme",
|
||||
mcpAuthStepRegionLabel: "Etapa {step}: {title}",
|
||||
mcpAuthPreviousStep: "Voltar",
|
||||
mcpAuthNextStep: "Avançar",
|
||||
mcpAuthStepProgress: "Etapa {current} de {total}",
|
||||
mcpAuthDescription: "As permissões são sobrescritas por níveis: banco › conexão › grupo › padrão global. Quanto mais específico o escopo, maior a prioridade.",
|
||||
mcpAuthTabConnectionsTitle: "Grupos e conexões",
|
||||
mcpAuthTabDatabasesTitle: "Escopo de bancos de dados",
|
||||
mcpAuthTabOverridesTitle: "Permissões padrão",
|
||||
mcpAuthTabCapabilitiesTitle: "Acesso efetivo e ferramentas",
|
||||
mcpPolicyExceptionsTitle: "Exceções de permissões salvas",
|
||||
mcpPolicyExceptionsDescription: "Alterar o escopo das conexões preserva as exceções de permissões. As exceções dentro do escopo continuam ativas; as demais se aplicam quando a conexão retorna ao escopo. Aqui você pode editar as permissões de execução ou restaurar as permissões herdadas.",
|
||||
mcpPolicyExceptionInScope: "Dentro do escopo atual",
|
||||
|
||||
@@ -10141,20 +10141,11 @@ export default withEnglishFallback({
|
||||
mcpRefresh: "Проверить снова",
|
||||
mcpGuide: "Руководство MCP",
|
||||
mcpAuthTitle: "Авторизация MCP",
|
||||
mcpAuthDescription: "Выполните авторизацию по порядку: задайте глобальное значение по умолчанию, выберите группы и подключения, ограничьте базы данных, затем проверьте итоговую политику.",
|
||||
mcpAuthStepsLabel: "Шаги авторизации MCP",
|
||||
mcpAuthStepConnectionsTitle: "Группы и подключения",
|
||||
mcpAuthStepConnectionsDescription: "Выберите, что видит MCP, и задайте разрешения для подключений",
|
||||
mcpAuthStepDatabasesTitle: "Область баз данных",
|
||||
mcpAuthStepDatabasesDescription: "Ограничьте базы данных каждого подключения и задайте разрешения для баз данных",
|
||||
mcpAuthStepOverridesTitle: "Разрешения по умолчанию",
|
||||
mcpAuthStepOverridesDescription: "Задайте глобальное значение по умолчанию для всех последующих областей",
|
||||
mcpAuthStepCapabilitiesTitle: "Сводка и проверка",
|
||||
mcpAuthStepCapabilitiesDescription: "Проверьте действующие разрешения, выберите инструменты и подтвердите",
|
||||
mcpAuthStepRegionLabel: "Шаг {step}: {title}",
|
||||
mcpAuthPreviousStep: "Назад",
|
||||
mcpAuthNextStep: "Далее",
|
||||
mcpAuthStepProgress: "Шаг {current} из {total}",
|
||||
mcpAuthDescription: "Права переопределяются по уровням: база данных › подключение › группа › глобальное значение по умолчанию. Чем уже область, тем выше приоритет.",
|
||||
mcpAuthTabConnectionsTitle: "Группы и подключения",
|
||||
mcpAuthTabDatabasesTitle: "Область баз данных",
|
||||
mcpAuthTabOverridesTitle: "Разрешения по умолчанию",
|
||||
mcpAuthTabCapabilitiesTitle: "Действующие права и инструменты",
|
||||
mcpPolicyExceptionsTitle: "Сохранённые исключения разрешений",
|
||||
mcpPolicyExceptionsDescription: "При изменении области подключений исключения разрешений сохраняются. Внутри области они остаются действующими; вне области они применяются при возвращении подключения. Здесь можно изменить разрешения на выполнение или восстановить наследуемые разрешения.",
|
||||
mcpPolicyExceptionInScope: "В текущей области",
|
||||
|
||||
@@ -10581,20 +10581,11 @@ export default withEnglishFallback({
|
||||
mcpRefresh: "ตรวจสอบอีกครั้ง",
|
||||
mcpGuide: "คู่มือ MCP",
|
||||
mcpAuthTitle: "การอนุญาต MCP",
|
||||
mcpAuthDescription: "ทำการอนุญาตให้ครบตามลำดับ: ตั้งค่าเริ่มต้นรวม เลือกกลุ่มและการเชื่อมต่อ จำกัดฐานข้อมูล แล้วตรวจสอบนโยบายสุดท้าย",
|
||||
mcpAuthStepsLabel: "ขั้นตอนการอนุญาต MCP",
|
||||
mcpAuthStepConnectionsTitle: "กลุ่มและการเชื่อมต่อ",
|
||||
mcpAuthStepConnectionsDescription: "เลือกสิ่งที่ MCP มองเห็นและตั้งค่าสิทธิ์การเชื่อมต่อ",
|
||||
mcpAuthStepDatabasesTitle: "ขอบเขตฐานข้อมูล",
|
||||
mcpAuthStepDatabasesDescription: "จำกัดฐานข้อมูลของแต่ละการเชื่อมต่อและตั้งค่าสิทธิ์ฐานข้อมูล",
|
||||
mcpAuthStepOverridesTitle: "สิทธิ์เริ่มต้น",
|
||||
mcpAuthStepOverridesDescription: "ตั้งค่าเริ่มต้นรวมสำหรับขอบเขตถัดไปทั้งหมด",
|
||||
mcpAuthStepCapabilitiesTitle: "สรุปและตรวจสอบ",
|
||||
mcpAuthStepCapabilitiesDescription: "ตรวจสอบสิทธิ์ที่มีผล เลือกเครื่องมือ และยืนยัน",
|
||||
mcpAuthStepRegionLabel: "ขั้นตอน {step}: {title}",
|
||||
mcpAuthPreviousStep: "ก่อนหน้า",
|
||||
mcpAuthNextStep: "ถัดไป",
|
||||
mcpAuthStepProgress: "ขั้นตอน {current} จาก {total}",
|
||||
mcpAuthDescription: "สิทธิ์จะถูกแทนที่ตามลำดับขั้น: ฐานข้อมูล › การเชื่อมต่อ › กลุ่ม › ค่าเริ่มต้นรวม ขอบเขตยิ่งแคบ ลำดับความสำคัญยิ่งสูง",
|
||||
mcpAuthTabConnectionsTitle: "กลุ่มและการเชื่อมต่อ",
|
||||
mcpAuthTabDatabasesTitle: "ขอบเขตฐานข้อมูล",
|
||||
mcpAuthTabOverridesTitle: "สิทธิ์เริ่มต้น",
|
||||
mcpAuthTabCapabilitiesTitle: "สิทธิ์ที่มีผลและเครื่องมือ",
|
||||
mcpPolicyExceptionsTitle: "ข้อยกเว้นสิทธิ์ที่บันทึกไว้",
|
||||
mcpPolicyExceptionsDescription: "การเปลี่ยนขอบเขตการเชื่อมต่อจะคงข้อยกเว้นสิทธิ์ไว้ ข้อยกเว้นภายในขอบเขตยังมีผล ส่วนภายนอกจะมีผลเมื่อการเชื่อมต่อกลับเข้าสู่ขอบเขต คุณแก้ไขสิทธิ์การเรียกใช้หรือคืนค่าสิทธิ์ที่สืบทอดได้ที่นี่",
|
||||
mcpPolicyExceptionInScope: "ภายในขอบเขตปัจจุบัน",
|
||||
|
||||
@@ -8340,20 +8340,11 @@ export default withEnglishFallback({
|
||||
mcpRefresh: "Yeniden denetle",
|
||||
mcpGuide: "MCP kılavuzu",
|
||||
mcpAuthTitle: "MCP yetkilendirmesi",
|
||||
mcpAuthDescription: "Yetkilendirmeyi sırayla tamamlayın: genel varsayılanı belirleyin, grup ve bağlantıları seçin, veritabanlarını sınırlayın ve son politikayı inceleyin.",
|
||||
mcpAuthStepsLabel: "MCP yetkilendirme adımları",
|
||||
mcpAuthStepConnectionsTitle: "Gruplar ve bağlantılar",
|
||||
mcpAuthStepConnectionsDescription: "MCP'nin hangi bağlantıları görebileceğini belirler",
|
||||
mcpAuthStepDatabasesTitle: "Veritabanı kapsamı",
|
||||
mcpAuthStepDatabasesDescription: "Her bağlantının erişebileceği veritabanlarını sınırlayın",
|
||||
mcpAuthStepOverridesTitle: "Varsayılan izinler",
|
||||
mcpAuthStepOverridesDescription: "Sonraki kapsamlar için genel varsayılan izni belirleyin",
|
||||
mcpAuthStepCapabilitiesTitle: "Özet ve onay",
|
||||
mcpAuthStepCapabilitiesDescription: "Etkin izinleri inceleyin, araçları seçin ve onaylayın",
|
||||
mcpAuthStepRegionLabel: "Adım {step}: {title}",
|
||||
mcpAuthPreviousStep: "Geri",
|
||||
mcpAuthNextStep: "İleri",
|
||||
mcpAuthStepProgress: "{total} adımdan {current}. adım",
|
||||
mcpAuthDescription: "İzinler kademeli olarak geçersiz kılınır: veritabanı › bağlantı › grup › genel varsayılan. Kapsam ne kadar dar olursa öncelik o kadar yüksektir.",
|
||||
mcpAuthTabConnectionsTitle: "Gruplar ve bağlantılar",
|
||||
mcpAuthTabDatabasesTitle: "Veritabanı kapsamı",
|
||||
mcpAuthTabOverridesTitle: "Varsayılan izinler",
|
||||
mcpAuthTabCapabilitiesTitle: "Geçerli erişim ve araçlar",
|
||||
mcpPolicyExceptionsTitle: "Kaydedilen izin istisnaları",
|
||||
mcpPolicyExceptionsDescription: "Bağlantı kapsamını değiştirmek izin istisnalarını korur. Kapsamdaki istisnalar geçerli kalır; kapsam dışındakiler bağlantı kapsama döndüğünde uygulanır. Burada yürütme izinlerini düzenleyebilir veya devralınan izinleri geri yükleyebilirsiniz.",
|
||||
mcpPolicyExceptionInScope: "Geçerli kapsam içinde",
|
||||
|
||||
@@ -9938,20 +9938,11 @@ export default withEnglishFallback({
|
||||
mcpRefresh: "重新检查",
|
||||
mcpGuide: "MCP 指南",
|
||||
mcpAuthTitle: "MCP 授权设置",
|
||||
mcpAuthDescription: "按顺序完成授权:先设置全局默认权限,再选择分组与连接、限定数据库,最后确认最终策略。",
|
||||
mcpAuthStepsLabel: "MCP 授权步骤",
|
||||
mcpAuthStepConnectionsTitle: "分组与连接",
|
||||
mcpAuthStepConnectionsDescription: "决定哪些分组和连接可被 MCP 看见,并设置连接权限",
|
||||
mcpAuthStepDatabasesTitle: "数据库范围",
|
||||
mcpAuthStepDatabasesDescription: "为每个连接限定可访问的库,并设置单库权限",
|
||||
mcpAuthStepOverridesTitle: "默认权限",
|
||||
mcpAuthStepOverridesDescription: "设置全局默认权限,后续范围可按需覆盖",
|
||||
mcpAuthStepCapabilitiesTitle: "汇总与确认",
|
||||
mcpAuthStepCapabilitiesDescription: "查看最终生效权限、选择 MCP 工具并确认策略",
|
||||
mcpAuthStepRegionLabel: "第 {step} 步:{title}",
|
||||
mcpAuthPreviousStep: "上一步",
|
||||
mcpAuthNextStep: "下一步",
|
||||
mcpAuthStepProgress: "第 {current} 步,共 {total} 步",
|
||||
mcpAuthDescription: "权限逐级覆盖:数据库 › 连接 › 分组 › 全局默认,范围越小优先级越高。",
|
||||
mcpAuthTabConnectionsTitle: "分组与连接",
|
||||
mcpAuthTabDatabasesTitle: "数据库范围",
|
||||
mcpAuthTabOverridesTitle: "默认权限",
|
||||
mcpAuthTabCapabilitiesTitle: "生效权限与工具",
|
||||
mcpPolicyExceptionsTitle: "已保存的权限例外",
|
||||
mcpPolicyExceptionsDescription: "切换连接范围会保留权限例外。范围内的例外仍然生效;范围外的例外会在连接重新进入范围后生效。可直接修改执行权限,或恢复继承上级权限。",
|
||||
mcpPolicyExceptionInScope: "当前范围内",
|
||||
|
||||
@@ -8124,20 +8124,11 @@ export default withEnglishFallback({
|
||||
mcpRefresh: "重新檢查",
|
||||
mcpGuide: "MCP 指南",
|
||||
mcpAuthTitle: "MCP 授權設定",
|
||||
mcpAuthDescription: "按順序完成授權:先設定全域預設權限,再選擇群組與連線、限定資料庫,最後確認最終策略。",
|
||||
mcpAuthStepsLabel: "MCP 授權步驟",
|
||||
mcpAuthStepConnectionsTitle: "群組與連線",
|
||||
mcpAuthStepConnectionsDescription: "決定哪些連線可被 MCP 看見",
|
||||
mcpAuthStepDatabasesTitle: "資料庫範圍",
|
||||
mcpAuthStepDatabasesDescription: "為每個連線限定可存取的庫",
|
||||
mcpAuthStepOverridesTitle: "預設權限",
|
||||
mcpAuthStepOverridesDescription: "設定套用至後續範圍的全域預設權限",
|
||||
mcpAuthStepCapabilitiesTitle: "摘要與確認",
|
||||
mcpAuthStepCapabilitiesDescription: "檢視最終權限、選擇工具並確認策略",
|
||||
mcpAuthStepRegionLabel: "第 {step} 步:{title}",
|
||||
mcpAuthPreviousStep: "上一步",
|
||||
mcpAuthNextStep: "下一步",
|
||||
mcpAuthStepProgress: "第 {current} 步,共 {total} 步",
|
||||
mcpAuthDescription: "權限逐級覆蓋:資料庫 › 連線 › 群組 › 全域預設,範圍越小優先級越高。",
|
||||
mcpAuthTabConnectionsTitle: "群組與連線",
|
||||
mcpAuthTabDatabasesTitle: "資料庫範圍",
|
||||
mcpAuthTabOverridesTitle: "預設權限",
|
||||
mcpAuthTabCapabilitiesTitle: "生效權限與工具",
|
||||
mcpPolicyExceptionsTitle: "已儲存的權限例外",
|
||||
mcpPolicyExceptionsDescription: "切換連線範圍會保留權限例外。範圍內的例外仍然生效;範圍外的例外會在連線重新進入範圍後生效。可直接修改執行權限,或恢復繼承上層權限。",
|
||||
mcpPolicyExceptionInScope: "目前範圍內",
|
||||
|
||||
Reference in New Issue
Block a user