connectors: quieter chips, 3-line descriptions, single access-grant surface
Nightly Build / build (push) Successful in 7m2s
Nightly Build / build (push) Successful in 7m2s
Row descriptions clamp at three lines instead of one. Metadata chips
(scope/type/auth) lose the loud accents — only status chips keep colour —
and the auth chip speaks human ("Requires an API key") instead of the
raw enum, via a shared authLabel().
Drop the access-grant section from the connector detail page: the Users
page modal already grants both global and per-user connectors, so
"who has what" now has a single surface.
This commit is contained in:
@@ -395,7 +395,7 @@ The role editor (`roles-page.js`) sets the default group + an allowed-groups che
|
|||||||
| `system-agents.js` | `<system-agents-page>` | `#system-agents` — the caller's own run history for the background system agents (TIC): agent, start, status, duration, counters; row → the run's session |
|
| `system-agents.js` | `<system-agents-page>` | `#system-agents` — the caller's own run history for the background system agents (TIC): agent, start, status, duration, counters; row → the run's session |
|
||||||
| `shared-folders.js` | `<shared-folders-page>` | `#shared-folders` — admin-only CRUD for on-disk shared folders (§6): create/describe/delete + per-member read-only/read-write grants; description feeds the assistant's `__SHARED_FOLDERS__` context |
|
| `shared-folders.js` | `<shared-folders-page>` | `#shared-folders` — admin-only CRUD for on-disk shared folders (§6): create/describe/delete + per-member read-only/read-write grants; description feeds the assistant's `__SHARED_FOLDERS__` context |
|
||||||
| `projects/` | `<projects-page>` | `#projects` — host + list + board; the board is tabbed (**Files** explorer with live watcher + write actions, **Sharing** members), deep-linked `#projects/{id}[/sharing]`. See the Projects section |
|
| `projects/` | `<projects-page>` | `#projects` — host + list + board; the board is tabbed (**Files** explorer with live watcher + write actions, **Sharing** members), deep-linked `#projects/{id}[/sharing]`. See the Projects section |
|
||||||
| `connector-detail.js` | `<connector-detail-page>` | A connector's own page (`#connector?name=X`): env/secret form + Test, the **OAuth login panel** (sign in → paste code → complete, §15), global enable + per-user access grants |
|
| `connector-detail.js` | `<connector-detail-page>` | A connector's own page (`#connector?name=X`): env/secret form + Test, the **OAuth login panel** (sign in → paste code → complete, §15), global enable. Access grants live **only** on the Users page (`users-page.js` connectors modal), so "who has what" has a single surface |
|
||||||
| `shared/connector-common.js` | (helpers) | Shared Connectors vocabulary: `statusOf` (incl. `needs_login` for a pending OAuth row), `STATUS_LABEL`, schema normalization, `jf` fetch |
|
| `shared/connector-common.js` | (helpers) | Shared Connectors vocabulary: `statusOf` (incl. `needs_login` for a pending OAuth row), `STATUS_LABEL`, schema normalization, `jf` fetch |
|
||||||
| `llm-providers.js` | `<llm-providers-page>` | LLM provider management |
|
| `llm-providers.js` | `<llm-providers-page>` | LLM provider management |
|
||||||
| `models-hub.js` | `<models-hub-page>` | Models hub landing (LLM / Transcription / Image) |
|
| `models-hub.js` | `<models-hub-page>` | Models hub landing (LLM / Transcription / Image) |
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { html, nothing } from 'lit';
|
|||||||
import { LightElement } from '../lib/base.js';
|
import { LightElement } from '../lib/base.js';
|
||||||
import { t } from '../lib/i18n.js';
|
import { t } from '../lib/i18n.js';
|
||||||
import {
|
import {
|
||||||
announceChange, connectorIconUrl, jf, normalizeSchema, parseJson, seedEnv, statusOf,
|
announceChange, authLabel, connectorIconUrl, jf, normalizeSchema, parseJson, seedEnv, statusOf,
|
||||||
} from './shared/connector-common.js';
|
} from './shared/connector-common.js';
|
||||||
|
|
||||||
// One connector's own page — `#connector?name=<catalog name>`.
|
// One connector's own page — `#connector?name=<catalog name>`.
|
||||||
@@ -12,9 +12,9 @@ import {
|
|||||||
// for a dozen fields, and a fixed-size modal simply could not hold them — it grew
|
// for a dozen fields, and a fixed-size modal simply could not hold them — it grew
|
||||||
// taller than the viewport and the buttons went off-screen. A page scrolls.
|
// taller than the viewport and the buttons went off-screen. A page scrolls.
|
||||||
//
|
//
|
||||||
// It is also the natural home for everything else that is per-connector and was
|
// It is also the natural home for the other per-connector actions: the Test button
|
||||||
// scattered before: the Test button, the global enable, and the per-user access
|
// and the global enable. Access grants are **not** here on purpose: they live only
|
||||||
// grants — which used to be a second modal reached from a third place.
|
// on the Users page, so "who has what" has a single surface.
|
||||||
//
|
//
|
||||||
// Deliberately not a `name` field: the list is one row per connector (§7 template),
|
// Deliberately not a `name` field: the list is one row per connector (§7 template),
|
||||||
// so the runtime name is the catalog name. The backend still defends against
|
// so the runtime name is the catalog name. The backend still defends against
|
||||||
@@ -44,8 +44,6 @@ export class ConnectorDetailPage extends LightElement {
|
|||||||
_test: { state: true }, // null | 'running' | report
|
_test: { state: true }, // null | 'running' | report
|
||||||
_busy: { state: true },
|
_busy: { state: true },
|
||||||
_error: { state: true },
|
_error: { state: true },
|
||||||
_users: { state: true }, // admin: for the access panel
|
|
||||||
_access: { state: true }, // admin: Set of granted user ids
|
|
||||||
_noIcon: { state: true },
|
_noIcon: { state: true },
|
||||||
_oauth: { state: true }, // in-flight OAuth login: { state, auth_url, code }
|
_oauth: { state: true }, // in-flight OAuth login: { state, auth_url, code }
|
||||||
_qr: { state: true }, // in-flight QR/device login: { state, qr, message }
|
_qr: { state: true }, // in-flight QR/device login: { state, qr, message }
|
||||||
@@ -70,8 +68,6 @@ export class ConnectorDetailPage extends LightElement {
|
|||||||
this._test = null;
|
this._test = null;
|
||||||
this._busy = false;
|
this._busy = false;
|
||||||
this._error = null;
|
this._error = null;
|
||||||
this._users = null;
|
|
||||||
this._access = null;
|
|
||||||
this._oauth = null;
|
this._oauth = null;
|
||||||
this._qr = null;
|
this._qr = null;
|
||||||
this._qrServerId = null;
|
this._qrServerId = null;
|
||||||
@@ -142,23 +138,11 @@ export class ConnectorDetailPage extends LightElement {
|
|||||||
this._schema = schema;
|
this._schema = schema;
|
||||||
// Keep whatever the user has already typed across a reload triggered by a save.
|
// Keep whatever the user has already typed across a reload triggered by a save.
|
||||||
this._form = { api_key: this._form.api_key || '', env: { ...seedEnv(schema), ...this._form.env } };
|
this._form = { api_key: this._form.api_key || '', env: { ...seedEnv(schema), ...this._form.env } };
|
||||||
|
|
||||||
if (this._isAdmin && this._isGlobal) await this._loadAccess();
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._error = e.message;
|
this._error = e.message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _loadAccess() {
|
|
||||||
try {
|
|
||||||
this._users = await jf('/api/users');
|
|
||||||
if (this._glob) {
|
|
||||||
const granted = await jf(`/api/mcp/global/${this._glob.id}/access`);
|
|
||||||
this._access = new Set(granted || []);
|
|
||||||
}
|
|
||||||
} catch (e) { this._error = e.message; }
|
|
||||||
}
|
|
||||||
|
|
||||||
_back() {
|
_back() {
|
||||||
// Prefer real history so the browser's own Back stays consistent; fall back to
|
// Prefer real history so the browser's own Back stays consistent; fall back to
|
||||||
// the list when this page was opened straight from a pasted URL.
|
// the list when this page was opened straight from a pasted URL.
|
||||||
@@ -384,26 +368,6 @@ export class ConnectorDetailPage extends LightElement {
|
|||||||
finally { this._busy = false; }
|
finally { this._busy = false; }
|
||||||
}
|
}
|
||||||
|
|
||||||
_toggleAccess(userId) {
|
|
||||||
const next = new Set(this._access);
|
|
||||||
next.has(userId) ? next.delete(userId) : next.add(userId);
|
|
||||||
this._access = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
async _saveAccess() {
|
|
||||||
this._busy = true;
|
|
||||||
try {
|
|
||||||
await jf(`/api/mcp/global/${this._glob.id}/access`, {
|
|
||||||
method: 'PUT',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ user_ids: [...this._access] }),
|
|
||||||
});
|
|
||||||
announceChange();
|
|
||||||
await this._load();
|
|
||||||
} catch (e) { this._error = e.message; }
|
|
||||||
finally { this._busy = false; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Render ─────────────────────────────────────────────────────────────────
|
// ── Render ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -428,7 +392,6 @@ export class ConnectorDetailPage extends LightElement {
|
|||||||
<div class="alert alert-danger py-2 mb-3" style="font-size:.85rem">${this._error}</div>` : nothing}
|
<div class="alert alert-danger py-2 mb-3" style="font-size:.85rem">${this._error}</div>` : nothing}
|
||||||
${this._renderSummary()}
|
${this._renderSummary()}
|
||||||
${this._renderConfig()}
|
${this._renderConfig()}
|
||||||
${this._renderAccess()}
|
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
@@ -471,7 +434,7 @@ export class ConnectorDetailPage extends LightElement {
|
|||||||
</div>
|
</div>
|
||||||
${desc ? html`<div class="connector-card-desc" style="-webkit-line-clamp:initial">${desc}</div>` : nothing}
|
${desc ? html`<div class="connector-card-desc" style="-webkit-line-clamp:initial">${desc}</div>` : nothing}
|
||||||
<div class="connector-chips">
|
<div class="connector-chips">
|
||||||
<span class="connector-chip connector-chip--scope">
|
<span class="connector-chip">
|
||||||
<i class="bi ${this._isGlobal ? 'bi-globe' : 'bi-person'}"></i>${this._isGlobal ? t('connectors.detail.detail_scope_global') : t('connectors.chip.per_user')}
|
<i class="bi ${this._isGlobal ? 'bi-globe' : 'bi-person'}"></i>${this._isGlobal ? t('connectors.detail.detail_scope_global') : t('connectors.chip.per_user')}
|
||||||
</span>
|
</span>
|
||||||
${isScript ? html`
|
${isScript ? html`
|
||||||
@@ -479,7 +442,7 @@ export class ConnectorDetailPage extends LightElement {
|
|||||||
<i class="bi bi-file-earmark-code"></i>${t('connectors.detail.scope_local')}
|
<i class="bi bi-file-earmark-code"></i>${t('connectors.detail.scope_local')}
|
||||||
</span>` : nothing}
|
</span>` : nothing}
|
||||||
${e?.auth_kind && e.auth_kind !== 'none' ? html`
|
${e?.auth_kind && e.auth_kind !== 'none' ? html`
|
||||||
<span class="connector-chip"><i class="bi bi-key"></i>${e.auth_kind}</span>` : nothing}
|
<span class="connector-chip"><i class="bi bi-key"></i>${authLabel(e.auth_kind)}</span>` : nothing}
|
||||||
${status === 'active' ? html`
|
${status === 'active' ? html`
|
||||||
<span class="connector-chip connector-chip--ok"><i class="bi bi-check-circle"></i>${t('connectors.detail.status.active')}</span>` : nothing}
|
<span class="connector-chip connector-chip--ok"><i class="bi bi-check-circle"></i>${t('connectors.detail.status.active')}</span>` : nothing}
|
||||||
${status === 'pending' ? html`
|
${status === 'pending' ? html`
|
||||||
@@ -736,37 +699,4 @@ export class ConnectorDetailPage extends LightElement {
|
|||||||
style="font-size:.7rem;white-space:pre-wrap">${JSON.stringify(t.details, null, 2)}</pre>` : nothing}
|
style="font-size:.7rem;white-space:pre-wrap">${JSON.stringify(t.details, null, 2)}</pre>` : nothing}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Who may use this global connector. Only meaningful once it is enabled — there
|
|
||||||
/// is no instance to grant access to before that.
|
|
||||||
_renderAccess() {
|
|
||||||
if (!this._isGlobal || !this._isAdmin || !this._glob) return nothing;
|
|
||||||
const users = this._users ?? [];
|
|
||||||
return html`
|
|
||||||
<div style="margin-top:1.75rem">
|
|
||||||
<div class="um-header" style="padding:0 0 .5rem">
|
|
||||||
<h3 class="um-title" style="font-size:1rem"><i class="bi bi-people me-2"></i>${t('connectors.detail.access.title')}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="text-muted mb-2" style="font-size:.78rem">${t('connectors.detail.access.desc')}</div>
|
|
||||||
${users.length === 0
|
|
||||||
? html`<div class="um-empty" style="padding:1rem"><i class="bi bi-people"></i><p>${t('connectors.detail.access.empty')}</p></div>`
|
|
||||||
: html`
|
|
||||||
<div class="connector-card">
|
|
||||||
${users.map(u => html`
|
|
||||||
<div class="form-check">
|
|
||||||
<input class="form-check-input" type="checkbox" id=${'acc-' + u.id}
|
|
||||||
.checked=${this._access?.has(u.id) ?? false}
|
|
||||||
@change=${() => this._toggleAccess(u.id)} />
|
|
||||||
<label class="form-check-label" for=${'acc-' + u.id}>
|
|
||||||
${u.display_name || u.username}
|
|
||||||
<code class="text-muted" style="font-size:.7rem">${u.id}</code>
|
|
||||||
</label>
|
|
||||||
</div>`)}
|
|
||||||
</div>`}
|
|
||||||
<button class="btn btn-sm btn-primary mt-2" ?disabled=${this._busy || !this._access}
|
|
||||||
@click=${() => this._saveAccess()}>
|
|
||||||
<i class="bi bi-check-lg me-1"></i>${t('connectors.detail.access.save')}
|
|
||||||
</button>
|
|
||||||
</div>`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { html, nothing } from 'lit';
|
|||||||
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
||||||
import { LightElement } from '../lib/base.js';
|
import { LightElement } from '../lib/base.js';
|
||||||
import { t } from '../lib/i18n.js';
|
import { t } from '../lib/i18n.js';
|
||||||
import { connectorIconUrl, statusOf, STATUS_LABEL, statusText } from './shared/connector-common.js';
|
import { authLabel, connectorIconUrl, statusOf, STATUS_LABEL, statusText } from './shared/connector-common.js';
|
||||||
|
|
||||||
// Connectors (MCP) — blueprint §7/§14/§15.
|
// Connectors (MCP) — blueprint §7/§14/§15.
|
||||||
//
|
//
|
||||||
@@ -445,7 +445,7 @@ export class ConnectorsPage extends LightElement {
|
|||||||
${r.description ? html`<div class="connector-row-desc">${r.description}</div>` : nothing}
|
${r.description ? html`<div class="connector-row-desc">${r.description}</div>` : nothing}
|
||||||
</div>
|
</div>
|
||||||
<div class="connector-row-chips">
|
<div class="connector-row-chips">
|
||||||
<span class="connector-chip connector-chip--scope">
|
<span class="connector-chip">
|
||||||
<i class="bi ${isGlobal ? 'bi-globe' : 'bi-person'}"></i>${isGlobal ? t('connectors.chip.global') : t('connectors.chip.per_user')}
|
<i class="bi ${isGlobal ? 'bi-globe' : 'bi-person'}"></i>${isGlobal ? t('connectors.chip.global') : t('connectors.chip.per_user')}
|
||||||
</span>
|
</span>
|
||||||
${isScript ? html`
|
${isScript ? html`
|
||||||
@@ -453,7 +453,7 @@ export class ConnectorsPage extends LightElement {
|
|||||||
<i class="bi bi-file-earmark-code"></i>${t('connectors.chip.local_script')}
|
<i class="bi bi-file-earmark-code"></i>${t('connectors.chip.local_script')}
|
||||||
</span>` : nothing}
|
</span>` : nothing}
|
||||||
${r.auth_kind && r.auth_kind !== 'none' ? html`
|
${r.auth_kind && r.auth_kind !== 'none' ? html`
|
||||||
<span class="connector-chip"><i class="bi bi-key"></i>${r.auth_kind}</span>` : nothing}
|
<span class="connector-chip"><i class="bi bi-key"></i>${authLabel(r.auth_kind)}</span>` : nothing}
|
||||||
</div>
|
</div>
|
||||||
<span class=${`connector-chip${STATUS_LABEL[status].tone ? ` connector-chip--${STATUS_LABEL[status].tone}` : ''}`}>
|
<span class=${`connector-chip${STATUS_LABEL[status].tone ? ` connector-chip--${STATUS_LABEL[status].tone}` : ''}`}>
|
||||||
${statusText(status)}
|
${statusText(status)}
|
||||||
|
|||||||
@@ -34,6 +34,17 @@ export function statusText(status) {
|
|||||||
}[status] ?? status;
|
}[status] ?? status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The auth requirement as a human phrase ("Requires an API key"), never the raw
|
||||||
|
/// enum — `api_key` on a chip told the reader nothing.
|
||||||
|
export function authLabel(kind) {
|
||||||
|
return {
|
||||||
|
api_key: t('connectors.chip.auth.api_key'),
|
||||||
|
oauth: t('connectors.chip.auth.oauth'),
|
||||||
|
qr: t('connectors.chip.auth.qr'),
|
||||||
|
ssh_key: t('connectors.chip.auth.ssh_key'),
|
||||||
|
}[kind] ?? kind;
|
||||||
|
}
|
||||||
|
|
||||||
/// The one place that decides what a connector's state *is*, from whichever runtime
|
/// The one place that decides what a connector's state *is*, from whichever runtime
|
||||||
/// rows exist for it.
|
/// rows exist for it.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -79,10 +79,14 @@
|
|||||||
|
|
||||||
.connector-row-desc {
|
.connector-row-desc {
|
||||||
font-size: 0.74rem;
|
font-size: 0.74rem;
|
||||||
|
line-height: 1.35;
|
||||||
color: var(--placeholder-color);
|
color: var(--placeholder-color);
|
||||||
|
/* Up to three lines: long descriptions stay readable without blowing the row
|
||||||
|
up — the clamp, not the text, decides the height. */
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.connector-row-chips {
|
.connector-row-chips {
|
||||||
|
|||||||
+4
-4
@@ -776,6 +776,10 @@ export default {
|
|||||||
'connectors.chip.global': 'global',
|
'connectors.chip.global': 'global',
|
||||||
'connectors.chip.per_user': 'per-user',
|
'connectors.chip.per_user': 'per-user',
|
||||||
'connectors.chip.local_script': 'local script',
|
'connectors.chip.local_script': 'local script',
|
||||||
|
'connectors.chip.auth.api_key': 'Requires an API key',
|
||||||
|
'connectors.chip.auth.oauth': 'Requires sign-in (OAuth)',
|
||||||
|
'connectors.chip.auth.qr': 'Requires QR pairing',
|
||||||
|
'connectors.chip.auth.ssh_key': 'Requires an SSH key',
|
||||||
|
|
||||||
'connectors.status.active': 'active',
|
'connectors.status.active': 'active',
|
||||||
'connectors.status.needs_fix': 'needs fix',
|
'connectors.status.needs_fix': 'needs fix',
|
||||||
@@ -866,10 +870,6 @@ export default {
|
|||||||
'connectors.detail.test.error_saved': 'Saved, but the credentials did not check out — fix them and test again.',
|
'connectors.detail.test.error_saved': 'Saved, but the credentials did not check out — fix them and test again.',
|
||||||
'connectors.detail.test.error_verify': 'Verification failed — the connector stays disabled until the credentials are fixed.',
|
'connectors.detail.test.error_verify': 'Verification failed — the connector stays disabled until the credentials are fixed.',
|
||||||
|
|
||||||
'connectors.detail.access.title': 'Who can use it',
|
|
||||||
'connectors.detail.access.desc': 'Ticking a box grants this connector\'s tools to that person\'s agent. Saving replaces the whole list.',
|
|
||||||
'connectors.detail.access.empty': 'No users.',
|
|
||||||
'connectors.detail.access.save': 'Save access',
|
|
||||||
|
|
||||||
'connectors.error.no_connector': 'No connector named "{name}" is available to you.',
|
'connectors.error.no_connector': 'No connector named "{name}" is available to you.',
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -776,6 +776,10 @@ export default {
|
|||||||
'connectors.chip.global': 'global',
|
'connectors.chip.global': 'global',
|
||||||
'connectors.chip.per_user': 'par utilisateur',
|
'connectors.chip.per_user': 'par utilisateur',
|
||||||
'connectors.chip.local_script': 'script local',
|
'connectors.chip.local_script': 'script local',
|
||||||
|
'connectors.chip.auth.api_key': 'Nécessite une clé API',
|
||||||
|
'connectors.chip.auth.oauth': 'Connexion requise (OAuth)',
|
||||||
|
'connectors.chip.auth.qr': 'Appairage QR requis',
|
||||||
|
'connectors.chip.auth.ssh_key': 'Nécessite une clé SSH',
|
||||||
|
|
||||||
'connectors.status.active': 'actif',
|
'connectors.status.active': 'actif',
|
||||||
'connectors.status.needs_fix': 'nécessite une correction',
|
'connectors.status.needs_fix': 'nécessite une correction',
|
||||||
@@ -856,10 +860,6 @@ export default {
|
|||||||
'connectors.detail.test.error_saved': 'Enregistré, mais les informations n\'ont pas été vérifiées — corrigez-les et testez à nouveau.',
|
'connectors.detail.test.error_saved': 'Enregistré, mais les informations n\'ont pas été vérifiées — corrigez-les et testez à nouveau.',
|
||||||
'connectors.detail.test.error_verify': 'La vérification a échoué — le connecteur reste désactivé jusqu\'à ce que les informations soient corrigées.',
|
'connectors.detail.test.error_verify': 'La vérification a échoué — le connecteur reste désactivé jusqu\'à ce que les informations soient corrigées.',
|
||||||
|
|
||||||
'connectors.detail.access.title': 'Qui peut l\'utiliser',
|
|
||||||
'connectors.detail.access.desc': 'Cocher une case accorde les outils de ce connecteur à l\'agent de cette personne. L\'enregistrement remplace toute la liste.',
|
|
||||||
'connectors.detail.access.empty': 'Aucun utilisateur.',
|
|
||||||
'connectors.detail.access.save': 'Enregistrer les accès',
|
|
||||||
|
|
||||||
'connectors.error.no_connector': 'Aucun connecteur nommé "{name}" ne vous est disponible.',
|
'connectors.error.no_connector': 'Aucun connecteur nommé "{name}" ne vous est disponible.',
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -776,6 +776,10 @@ export default {
|
|||||||
'connectors.chip.global': 'globale',
|
'connectors.chip.global': 'globale',
|
||||||
'connectors.chip.per_user': 'per utente',
|
'connectors.chip.per_user': 'per utente',
|
||||||
'connectors.chip.local_script': 'script locale',
|
'connectors.chip.local_script': 'script locale',
|
||||||
|
'connectors.chip.auth.api_key': 'Richiede una API key',
|
||||||
|
'connectors.chip.auth.oauth': 'Richiede accesso (OAuth)',
|
||||||
|
'connectors.chip.auth.qr': 'Richiede abbinamento QR',
|
||||||
|
'connectors.chip.auth.ssh_key': 'Richiede una chiave SSH',
|
||||||
|
|
||||||
'connectors.status.active': 'attivo',
|
'connectors.status.active': 'attivo',
|
||||||
'connectors.status.needs_fix': 'da sistemare',
|
'connectors.status.needs_fix': 'da sistemare',
|
||||||
@@ -856,10 +860,6 @@ export default {
|
|||||||
'connectors.detail.test.error_saved': 'Salvato, ma le credenziali non hanno superato la verifica — correggile e prova di nuovo.',
|
'connectors.detail.test.error_saved': 'Salvato, ma le credenziali non hanno superato la verifica — correggile e prova di nuovo.',
|
||||||
'connectors.detail.test.error_verify': 'Verifica fallita — il connettore rimane disabilitato finché le credenziali non vengono corrette.',
|
'connectors.detail.test.error_verify': 'Verifica fallita — il connettore rimane disabilitato finché le credenziali non vengono corrette.',
|
||||||
|
|
||||||
'connectors.detail.access.title': 'Chi può usarlo',
|
|
||||||
'connectors.detail.access.desc': 'Selezionando un utente gli vengono concessi gli strumenti di questo connettore. Salvando si sostituisce l\'intera lista.',
|
|
||||||
'connectors.detail.access.empty': 'Nessun utente.',
|
|
||||||
'connectors.detail.access.save': 'Salva accesso',
|
|
||||||
|
|
||||||
'connectors.error.no_connector': 'Nessun connettore chiamato "{name}" è disponibile per te.',
|
'connectors.error.no_connector': 'Nessun connettore chiamato "{name}" è disponibile per te.',
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user