@adecore/agents-react 0.30.0 → 0.31.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/AgentIcon.js +4 -3
- package/dist/agents/accounts.d.ts +2 -0
- package/dist/agents/accounts.js +6 -0
- package/dist/agents/agent-marks.d.ts +8 -0
- package/dist/agents/agent-marks.js +12 -0
- package/dist/agents/provider-paths.js +3 -2
- package/dist/host.d.ts +4 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/locales/en/agent-providers.d.ts +40 -0
- package/dist/locales/en/agent-providers.js +42 -2
- package/dist/locales/nl/agent-providers.d.ts +40 -0
- package/dist/locales/nl/agent-providers.js +42 -2
- package/dist/providers/AccountDetail.d.ts +4 -3
- package/dist/providers/AccountDetail.js +6 -5
- package/dist/providers/AddAccountForm.d.ts +3 -2
- package/dist/providers/AddAccountForm.js +2 -2
- package/dist/providers/CliDetail.d.ts +3 -3
- package/dist/providers/CliDetail.js +3 -3
- package/dist/providers/InstallPage.d.ts +9 -0
- package/dist/providers/InstallPage.js +74 -0
- package/dist/providers/ProvidersPane.d.ts +3 -1
- package/dist/providers/ProvidersPane.js +25 -14
- package/dist/providers/index.d.ts +1 -0
- package/dist/providers/install-steps.d.ts +14 -0
- package/dist/providers/install-steps.js +19 -0
- package/dist/providers/parts.js +2 -1
- package/dist/theme.css +3 -0
- package/dist/usage/format.js +6 -2
- package/dist/usage/limit-groups.js +2 -1
- package/package.json +8 -8
- package/src/agents/AgentIcon.tsx +12 -3
- package/src/agents/accounts.ts +13 -0
- package/src/agents/agent-marks.ts +26 -0
- package/src/agents/provider-paths.ts +3 -2
- package/src/host.ts +11 -1
- package/src/index.ts +1 -0
- package/src/locales/en/agent-providers.ts +42 -2
- package/src/locales/nl/agent-providers.ts +42 -2
- package/src/providers/AccountDetail.tsx +34 -26
- package/src/providers/AddAccountForm.tsx +8 -3
- package/src/providers/CliDetail.tsx +5 -5
- package/src/providers/InstallPage.tsx +221 -0
- package/src/providers/ProvidersPane.tsx +36 -19
- package/src/providers/index.ts +1 -0
- package/src/providers/install-steps.ts +35 -0
- package/src/providers/parts.tsx +2 -1
- package/src/theme.css +3 -0
- package/src/usage/format.ts +6 -2
- package/src/usage/limit-groups.ts +2 -1
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import type { ProviderInfo } from '@adecore/agent-contracts';
|
|
3
|
+
import type { ProviderInstallStep } from './install-steps.js';
|
|
3
4
|
interface ProvidersPaneProps {
|
|
4
5
|
target?: string | null;
|
|
5
6
|
detailOf?(provider: ProviderInfo): ReactNode | null;
|
|
6
7
|
defaults?: boolean;
|
|
8
|
+
installStepOf?(provider: ProviderInfo): ProviderInstallStep | null;
|
|
7
9
|
}
|
|
8
|
-
export declare function ProvidersPane({ target, detailOf, defaults }: ProvidersPaneProps): import("react").JSX.Element;
|
|
10
|
+
export declare function ProvidersPane({ target, detailOf, defaults, installStepOf }: ProvidersPaneProps): import("react").JSX.Element;
|
|
9
11
|
export {};
|
|
@@ -2,16 +2,17 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import { useMemo, useState } from 'react';
|
|
3
3
|
import clsx from 'clsx';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
|
-
import { Plus
|
|
5
|
+
import { Plus } from 'lucide-react';
|
|
6
6
|
import { AccountDot } from '../agents/AccountDot.js';
|
|
7
|
-
import { ACCOUNT_TONE_CLASSES, accountName, accountsOfKind, accountStatusLine } from '../agents/accounts.js';
|
|
8
|
-
import { ErrorBoundary,
|
|
7
|
+
import { ACCOUNT_TONE_CLASSES, accountLoginOf, accountName, accountsOfKind, accountStatusLine } from '../agents/accounts.js';
|
|
8
|
+
import { ErrorBoundary, IconButton, SectionLabel, Skeleton } from '@adecore/ui';
|
|
9
9
|
import { MasterDetail } from '@adecore/ui/settings';
|
|
10
10
|
import { useChatHost } from '../host.js';
|
|
11
11
|
import { useChatScope } from '../scope.js';
|
|
12
12
|
import { AccountDetail } from './AccountDetail.js';
|
|
13
13
|
import { AddAccountForm } from './AddAccountForm.js';
|
|
14
14
|
import { CliDetail } from './CliDetail.js';
|
|
15
|
+
import { InstallPage } from './InstallPage.js';
|
|
15
16
|
import { CliMark } from './parts.js';
|
|
16
17
|
import { useProviderAccounts } from '../state/provider-accounts.js';
|
|
17
18
|
import { useProviders } from '../state/providers.js';
|
|
@@ -20,7 +21,7 @@ function ListRow({ selected, onSelect, children, trailing, className }) {
|
|
|
20
21
|
return (_jsxs("div", { className: clsx('flex min-w-0 shrink-0 items-center gap-2 rounded-lg pr-2', selected ? 'bg-text/5' : 'hover:bg-surface-hover', className), children: [_jsx("button", { type: "button", "aria-current": selected ? 'true' : undefined, className: "flex min-w-0 grow items-center gap-3 self-stretch rounded-lg py-2 pl-3 text-left focus-visible:-outline-offset-2", onClick: onSelect, children: children }), trailing] }));
|
|
21
22
|
}
|
|
22
23
|
/* The agent CLIs of the host in scope, each with its accounts, beside the detail of what is picked. */
|
|
23
|
-
export function ProvidersPane({ target = null, detailOf, defaults = true }) {
|
|
24
|
+
export function ProvidersPane({ target = null, detailOf, defaults = true, installStepOf }) {
|
|
24
25
|
const { t } = useTranslation('agent-providers');
|
|
25
26
|
const { id: scopeId } = useChatScope();
|
|
26
27
|
const providers = useProviders((s) => s.providers);
|
|
@@ -34,13 +35,14 @@ export function ProvidersPane({ target = null, detailOf, defaults = true }) {
|
|
|
34
35
|
const installed = useMemo(() => providers.filter((provider) => provider.installed || !provider.capabilities.terminal), [providers]);
|
|
35
36
|
const missing = useMemo(() => providers.filter((provider) => !provider.installed && provider.capabilities.terminal), [providers]);
|
|
36
37
|
const entriesOf = (kind) => accountsOfKind(accounts, kind);
|
|
37
|
-
const
|
|
38
|
+
const loginOf = (kind) => accountLoginOf(accounts, kind);
|
|
38
39
|
// What was picked may be gone since (an account removed elsewhere); the first CLI stands in for it.
|
|
39
40
|
const pickedEntry = picked?.kind === 'account' ? (Object.hasOwn(accounts?.accounts ?? {}, picked.id) ? picked.id : null) : null;
|
|
40
41
|
const current = picked !== null && (picked.kind !== 'account' || pickedEntry !== null) ? picked : installed[0] ? { kind: 'cli', cli: installed[0].kind } : null;
|
|
41
42
|
const currentAccount = current?.kind === 'account' ? accounts?.accounts[current.id] : undefined;
|
|
42
43
|
const currentCli = current === null ? null : current.kind === 'account' ? currentAccount?.kind : current.cli;
|
|
43
|
-
const provider = installed.find((entry) => entry.kind === currentCli) ?? null;
|
|
44
|
+
const provider = (current?.kind === 'install' ? providers : installed).find((entry) => entry.kind === currentCli) ?? null;
|
|
45
|
+
const picksCli = (kind) => (current?.kind === 'cli' || current?.kind === 'install') && current.cli === kind;
|
|
44
46
|
// A search result lands on a row of a CLI's detail, so a picked account makes way for its CLI.
|
|
45
47
|
const [seenTarget, setSeenTarget] = useState(target);
|
|
46
48
|
if (target !== seenTarget) {
|
|
@@ -54,35 +56,44 @@ export function ProvidersPane({ target = null, detailOf, defaults = true }) {
|
|
|
54
56
|
}
|
|
55
57
|
const added = (id, name, made) => {
|
|
56
58
|
setPicked({ kind: 'account', id });
|
|
57
|
-
if (made && provider !== null && openLogin !== null && loginBlocked === null) {
|
|
59
|
+
if (made && provider !== null && loginOf(provider.kind) === 'terminal' && openLogin !== null && loginBlocked === null) {
|
|
58
60
|
void openLogin(scopeId, provider.kind, id, name);
|
|
59
61
|
}
|
|
60
62
|
};
|
|
61
63
|
const group = (cli) => {
|
|
62
64
|
const entries = cli.installed ? entriesOf(cli.kind) : [];
|
|
63
|
-
return (_jsxs("div", { className: "flex min-w-0 shrink-0 flex-col gap-0.5 pt-1", children: [_jsxs(ListRow, { selected:
|
|
65
|
+
return (_jsxs("div", { className: "flex min-w-0 shrink-0 flex-col gap-0.5 pt-1", children: [_jsxs(ListRow, { selected: picksCli(cli.kind), onSelect: () => setPicked({ kind: 'cli', cli: cli.kind }), className: "pr-1", trailing: loginOf(cli.kind) !== null && (_jsx(IconButton, { icon: Plus, size: "sm", label: t('list.addAccount', { provider: cli.name }), onClick: () => setPicked({ kind: 'add', cli: cli.kind }) })), children: [_jsx(CliMark, { kind: cli.kind }), _jsx("span", { className: "min-w-0 grow truncate text-xs font-medium text-text", children: cli.name }), cli.capabilities.terminal && cli.version && _jsx("span", { className: "shrink-0 font-mono text-code text-text-faint", children: cli.version })] }), entries.map((entry) => {
|
|
64
66
|
const name = accountName(entry, cli.name);
|
|
65
|
-
const status = accountStatusLine(entry.status,
|
|
67
|
+
const status = accountStatusLine(entry.status, loginOf(cli.kind) === 'terminal');
|
|
66
68
|
return (_jsxs(ListRow, { selected: current?.kind === 'account' && current.id === entry.id, onSelect: () => setPicked({ kind: 'account', id: entry.id }), children: [_jsx("span", { className: "grid w-3.5 shrink-0 place-items-center", children: _jsx(AccountDot, { color: entry.account.color, className: "size-2.25" }) }), _jsxs("span", { className: "flex min-w-0 grow flex-col", children: [_jsxs("span", { className: "flex min-w-0 items-center gap-2 text-sm text-text", children: [_jsx("span", { className: "truncate", children: name }), entry.isDefault && (_jsx("span", { className: "shrink-0 rounded-md bg-surface-active px-1.5 text-xs text-text-muted", children: t('list.default') }))] }), _jsx("span", { className: clsx('truncate text-xs', ACCOUNT_TONE_CLASSES[status.tone]), children: status.text })] })] }, entry.id));
|
|
67
69
|
})] }, cli.kind));
|
|
68
70
|
};
|
|
69
|
-
const list = !loaded ? (_jsxs("div", { className: "flex flex-col gap-3 p-3", children: [_jsx(Skeleton, { className: "w-32" }), _jsx(Skeleton, { className: "w-40" })] })) : (_jsxs(_Fragment, { children: [installed.map(group), installed.length === 0 && _jsx("p", { className: "px-3 py-2 text-xs text-text-muted", children: t('list.none') }), missing.length > 0 && (_jsxs("div", { className: "mt-1.5 flex min-w-0 shrink-0 flex-col border-t border-border pt-2.5 pb-2", children: [_jsx(SectionLabel, { className: "px-3 pt-1 pb-1.5", children: t('list.notInstalled') }), missing.map((cli) => (_jsxs(
|
|
71
|
+
const list = !loaded ? (_jsxs("div", { className: "flex flex-col gap-3 p-3", children: [_jsx(Skeleton, { className: "w-32" }), _jsx(Skeleton, { className: "w-40" })] })) : (_jsxs(_Fragment, { children: [installed.map(group), installed.length === 0 && _jsx("p", { className: "px-3 py-2 text-xs text-text-muted", children: t('list.none') }), missing.length > 0 && (_jsxs("div", { className: "mt-1.5 flex min-w-0 shrink-0 flex-col border-t border-border pt-2.5 pb-2", children: [_jsx(SectionLabel, { className: "px-3 pt-1 pb-1.5", children: t('list.notInstalled') }), missing.map((cli) => (_jsxs(ListRow, { selected: picksCli(cli.kind), onSelect: () => setPicked({ kind: 'install', cli: cli.kind }), children: [_jsx(CliMark, { kind: cli.kind, className: "opacity-60" }), _jsx("span", { className: "min-w-0 grow truncate text-xs font-medium text-text-muted", children: cli.name }), _jsx("span", { className: "shrink-0 text-xs text-text-faint", children: t('list.missing') })] }, cli.kind))), missing.every((cli) => cli.install === undefined) && _jsx("p", { className: "mt-1 px-3 text-xs text-text-faint", children: t('list.notInstalledNote') })] }))] }));
|
|
70
72
|
const detail = () => {
|
|
71
73
|
if (current === null || provider === null) {
|
|
72
74
|
return null;
|
|
73
75
|
}
|
|
74
|
-
|
|
76
|
+
if (current.kind === 'install') {
|
|
77
|
+
const extra = installStepOf?.(provider) ?? null;
|
|
78
|
+
const statuses = entriesOf(provider.kind).map((entry) => entry.status?.state);
|
|
79
|
+
// Without accounts to read, nothing says the CLI is signed out, so being there is all it takes.
|
|
80
|
+
const signedIn = statuses.length === 0 || statuses.includes('ready');
|
|
81
|
+
if (!provider.installed || !signedIn || (extra !== null && !extra.done)) {
|
|
82
|
+
return _jsx(InstallPage, { provider: provider, signedIn: signedIn, extra: extra }, provider.kind);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const own = current.kind === 'cli' || current.kind === 'install' ? (detailOf?.(provider) ?? null) : null;
|
|
75
86
|
if (own !== null) {
|
|
76
87
|
return own;
|
|
77
88
|
}
|
|
78
89
|
if (current.kind === 'add') {
|
|
79
|
-
return (_jsx(AddAccountForm, { provider: provider, entries: entriesOf(provider.kind), onCancel: () => setPicked({ kind: 'cli', cli: provider.kind }), onAdded: added }, provider.kind));
|
|
90
|
+
return (_jsx(AddAccountForm, { provider: provider, entries: entriesOf(provider.kind), login: loginOf(provider.kind), onCancel: () => setPicked({ kind: 'cli', cli: provider.kind }), onAdded: added }, provider.kind));
|
|
80
91
|
}
|
|
81
92
|
if (current.kind === 'account') {
|
|
82
93
|
const entry = entriesOf(provider.kind).find((candidate) => candidate.id === current.id);
|
|
83
|
-
return (entry && (_jsx(AccountDetail, { provider: provider, entry: entry,
|
|
94
|
+
return (entry && (_jsx(AccountDetail, { provider: provider, entry: entry, login: loginOf(provider.kind), canOpenLogin: openLogin !== null, loginBlocked: loginBlocked, secretsAvailable: accounts?.secretsAvailable === true, onRemoved: () => setPicked({ kind: 'cli', cli: provider.kind }) }, entry.id)));
|
|
84
95
|
}
|
|
85
|
-
return (_jsx(CliDetail, { provider: provider, entries: accounts === null ? null : entriesOf(provider.kind), defaults: defaults,
|
|
96
|
+
return (_jsx(CliDetail, { provider: provider, entries: accounts === null ? null : entriesOf(provider.kind), defaults: defaults, login: loginOf(provider.kind), onAddAccount: () => setPicked({ kind: 'add', cli: provider.kind }), onPickAccount: (id) => setPicked({ kind: 'account', id }) }));
|
|
86
97
|
};
|
|
87
98
|
return (_jsx(MasterDetail, { listWidth: 340, listLabel: t('list.label'), list: list, detail: _jsx(ErrorBoundary, { label: t('failed'), resetKeys: [scopeId, current?.kind, currentCli], children: _jsx("div", { className: "flex min-w-0 flex-col gap-7", children: detail() }) }) }));
|
|
88
99
|
}
|
|
@@ -7,4 +7,5 @@ export { modelName, useModelName, shortModelName } from '../agents/model-name.js
|
|
|
7
7
|
export { carryOptions } from '../agents/model-options.js';
|
|
8
8
|
export { type ModelRequirement } from '../agents/model-requirement.js';
|
|
9
9
|
export { ProvidersPane } from './ProvidersPane.js';
|
|
10
|
+
export type { ProviderInstallStep } from './install-steps.js';
|
|
10
11
|
export { CliMark, CliTile } from './parts.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { ProviderInfo, ProviderPlatform } from '@adecore/agent-contracts';
|
|
3
|
+
export interface ProviderInstallStep {
|
|
4
|
+
title: string;
|
|
5
|
+
done: boolean;
|
|
6
|
+
body: ReactNode;
|
|
7
|
+
}
|
|
8
|
+
export type InstallStepState = 'done' | 'current' | 'later';
|
|
9
|
+
export declare function installStepStates(done: readonly boolean[]): InstallStepState[];
|
|
10
|
+
export declare function pagePlatform(): ProviderPlatform;
|
|
11
|
+
export declare function installPlatforms(provider: ProviderInfo, machine: ProviderPlatform): {
|
|
12
|
+
offered: ProviderPlatform[];
|
|
13
|
+
initial: ProviderPlatform | null;
|
|
14
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { isApplePlatform } from '@adecore/ui';
|
|
2
|
+
const PLATFORMS = ['darwin', 'linux', 'win32'];
|
|
3
|
+
/* The first step not done is the current one. */
|
|
4
|
+
export function installStepStates(done) {
|
|
5
|
+
const first = done.indexOf(false);
|
|
6
|
+
return done.map((value, index) => (value ? 'done' : index === first ? 'current' : 'later'));
|
|
7
|
+
}
|
|
8
|
+
/* The system of the page, for a host that does not say which system its machine runs. */
|
|
9
|
+
export function pagePlatform() {
|
|
10
|
+
if (isApplePlatform()) {
|
|
11
|
+
return 'darwin';
|
|
12
|
+
}
|
|
13
|
+
return typeof navigator !== 'undefined' && /windows/i.test(navigator.userAgent) ? 'win32' : 'linux';
|
|
14
|
+
}
|
|
15
|
+
/* The systems a provider has a command for, and the one the page opens on: the machine's when it has one there. */
|
|
16
|
+
export function installPlatforms(provider, machine) {
|
|
17
|
+
const offered = PLATFORMS.filter((platform) => Boolean(provider.install?.commands[platform]));
|
|
18
|
+
return { offered, initial: offered.includes(machine) ? machine : (offered[0] ?? null) };
|
|
19
|
+
}
|
package/dist/providers/parts.js
CHANGED
|
@@ -7,7 +7,8 @@ const CLI_COLORS = {
|
|
|
7
7
|
claude: 'text-chart-claude',
|
|
8
8
|
codex: 'text-chart-codex',
|
|
9
9
|
gemini: 'text-chart-gemini',
|
|
10
|
-
copilot: 'text-chart-copilot'
|
|
10
|
+
copilot: 'text-chart-copilot',
|
|
11
|
+
nanogpt: 'text-chart-nanogpt'
|
|
11
12
|
};
|
|
12
13
|
export function CliMark({ kind, size = 14, className }) {
|
|
13
14
|
return _jsx(AgentIcon, { kind: kind, size: size, className: clsx('shrink-0', CLI_COLORS[kind] ?? 'text-text-muted', className) });
|
package/dist/theme.css
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
--chart-codex: #30b0c7;
|
|
14
14
|
--chart-gemini: #3b82f6;
|
|
15
15
|
--chart-copilot: #64748b;
|
|
16
|
+
--chart-nanogpt: #34c759;
|
|
16
17
|
/* What a chat's context holds, one color per part; the rest is the system's and stays gray. */
|
|
17
18
|
--chart-context-tools: #4d7ce6;
|
|
18
19
|
--chart-context-files: #9a63e0;
|
|
@@ -45,6 +46,7 @@
|
|
|
45
46
|
--chart-codex: #40c8e0;
|
|
46
47
|
--chart-gemini: #60a5fa;
|
|
47
48
|
--chart-copilot: #94a3b8;
|
|
49
|
+
--chart-nanogpt: #30d158;
|
|
48
50
|
--chart-context-tools: #8aa7f5;
|
|
49
51
|
--chart-context-files: #b98af3;
|
|
50
52
|
--chart-context-conversation: #6dd08b;
|
|
@@ -82,6 +84,7 @@
|
|
|
82
84
|
--color-chart-codex: var(--chart-codex);
|
|
83
85
|
--color-chart-gemini: var(--chart-gemini);
|
|
84
86
|
--color-chart-copilot: var(--chart-copilot);
|
|
87
|
+
--color-chart-nanogpt: var(--chart-nanogpt);
|
|
85
88
|
--color-chart-context-tools: var(--chart-context-tools);
|
|
86
89
|
--color-chart-context-files: var(--chart-context-files);
|
|
87
90
|
--color-chart-context-conversation: var(--chart-context-conversation);
|
package/dist/usage/format.js
CHANGED
|
@@ -20,8 +20,12 @@ export function moneyFormat(currency, rate) {
|
|
|
20
20
|
return formatMoney(value, code, value !== 0 && Math.abs(value) < 0.01 ? 4 : undefined);
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
|
-
export const PROVIDER_LABELS = { claude: 'Claude Code', codex: 'Codex' };
|
|
24
|
-
export const PROVIDER_COLORS = {
|
|
23
|
+
export const PROVIDER_LABELS = { claude: 'Claude Code', codex: 'Codex', nanogpt: 'NanoGPT' };
|
|
24
|
+
export const PROVIDER_COLORS = {
|
|
25
|
+
claude: 'var(--chart-claude)',
|
|
26
|
+
codex: 'var(--chart-codex)',
|
|
27
|
+
nanogpt: 'var(--chart-nanogpt)'
|
|
28
|
+
};
|
|
25
29
|
/*
|
|
26
30
|
* A slot is `2026-09-10` or `2026-09-10T14`, already in the viewer's own zone, so it is read as plain
|
|
27
31
|
* wall clock and never handed to a parser that would shift it back.
|
|
@@ -76,7 +76,8 @@ export function explain(provider) {
|
|
|
76
76
|
return i18next.t('agent-usage:limits.notInstalled');
|
|
77
77
|
}
|
|
78
78
|
if (provider.unavailable.reason === 'no-subscription') {
|
|
79
|
-
|
|
79
|
+
// A provider that knows why it has no plan says so; the rest are accounts on an API key.
|
|
80
|
+
return provider.unavailable.message ?? i18next.t('agent-usage:limits.noSubscription');
|
|
80
81
|
}
|
|
81
82
|
return provider.unavailable.message ?? i18next.t('agent-usage:limits.unreachable');
|
|
82
83
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adecore/agents-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.1",
|
|
4
4
|
"description": "React agent chat threads, composers, approvals, provider accounts and usage views.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -67,11 +67,11 @@
|
|
|
67
67
|
"test:visuals": "bun --config=integration.bunfig.toml --conditions=source test ./src/chat/visual-viewport.integration.test.ts"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@adecore/agent-contracts": "0.
|
|
71
|
-
"@adecore/editor": "0.
|
|
72
|
-
"@adecore/editor-react": "0.
|
|
73
|
-
"@adecore/intelligent-ui": "0.
|
|
74
|
-
"@adecore/merge": "0.
|
|
70
|
+
"@adecore/agent-contracts": "0.31.1",
|
|
71
|
+
"@adecore/editor": "0.31.1",
|
|
72
|
+
"@adecore/editor-react": "0.31.1",
|
|
73
|
+
"@adecore/intelligent-ui": "0.31.1",
|
|
74
|
+
"@adecore/merge": "0.31.1",
|
|
75
75
|
"@codemirror/commands": "^6.11.1",
|
|
76
76
|
"@codemirror/language": "^6.12.4",
|
|
77
77
|
"@codemirror/state": "^6.7.6",
|
|
@@ -94,14 +94,14 @@
|
|
|
94
94
|
"zustand": "^5.0.15"
|
|
95
95
|
},
|
|
96
96
|
"peerDependencies": {
|
|
97
|
-
"@adecore/ui": "^0.
|
|
97
|
+
"@adecore/ui": "^0.31.1",
|
|
98
98
|
"i18next": "^26.4.2",
|
|
99
99
|
"react": "^19.3.0",
|
|
100
100
|
"react-dom": "^19.3.0",
|
|
101
101
|
"react-i18next": "^17.0.15"
|
|
102
102
|
},
|
|
103
103
|
"devDependencies": {
|
|
104
|
-
"@adecore/ui": "0.
|
|
104
|
+
"@adecore/ui": "0.31.1",
|
|
105
105
|
"@types/bun": "^1.4.2",
|
|
106
106
|
"@types/react": "^19.3.0",
|
|
107
107
|
"@types/react-dom": "^19.3.0",
|
package/src/agents/AgentIcon.tsx
CHANGED
|
@@ -3,10 +3,11 @@ import clsx from 'clsx';
|
|
|
3
3
|
import { Bot } from 'lucide-react';
|
|
4
4
|
import { siClaude, siGithubcopilot, siGooglegemini } from 'simple-icons';
|
|
5
5
|
import { Icon } from '@adecore/ui';
|
|
6
|
+
import { type AgentMark, hostAgentMark } from './agent-marks';
|
|
6
7
|
import { PROVIDER_PATHS } from './provider-paths';
|
|
7
8
|
|
|
8
9
|
// simple-icons removed OpenAI's mark at its request, so Codex reuses the usage page's path.
|
|
9
|
-
const
|
|
10
|
+
const BUILT_IN_MARKS: Partial<Record<AgentKind, AgentMark>> = {
|
|
10
11
|
claude: siClaude,
|
|
11
12
|
codex: { title: 'Codex', path: PROVIDER_PATHS.codex },
|
|
12
13
|
gemini: siGooglegemini,
|
|
@@ -14,12 +15,20 @@ const MARKS: Partial<Record<AgentKind, { title: string; path: string }>> = {
|
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
export function AgentIcon({ kind, size = 14, className }: { kind: AgentKind; size?: number; className?: string }) {
|
|
17
|
-
const mark =
|
|
18
|
+
const mark = hostAgentMark(kind) ?? BUILT_IN_MARKS[kind];
|
|
18
19
|
if (!mark) {
|
|
19
20
|
return <Icon icon={Bot} size={size} className={className} />;
|
|
20
21
|
}
|
|
21
22
|
return (
|
|
22
|
-
<svg
|
|
23
|
+
<svg
|
|
24
|
+
role="img"
|
|
25
|
+
aria-label={mark.title}
|
|
26
|
+
viewBox={mark.viewBox ?? '0 0 24 24'}
|
|
27
|
+
width={size}
|
|
28
|
+
height={size}
|
|
29
|
+
fill="currentColor"
|
|
30
|
+
className={clsx('align-middle', className)}
|
|
31
|
+
>
|
|
23
32
|
<path d={mark.path} />
|
|
24
33
|
</svg>
|
|
25
34
|
);
|
package/src/agents/accounts.ts
CHANGED
|
@@ -41,6 +41,19 @@ export function accountName(entry: Pick<AccountEntry, 'id' | 'account' | 'isDefa
|
|
|
41
41
|
return entry.account.label ?? (entry.isDefault ? providerName : entry.id);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/*
|
|
45
|
+
* Where an account of a kind signs in: a terminal runs the CLI's login, or its first chat asks. Null for a
|
|
46
|
+
* CLI without a config folder, which has only its default account; also null until the accounts arrived.
|
|
47
|
+
*/
|
|
48
|
+
export type AccountLogin = 'terminal' | 'chat' | null;
|
|
49
|
+
|
|
50
|
+
export function accountLoginOf(accounts: ProviderAccounts | null | undefined, kind: AgentKind): AccountLogin {
|
|
51
|
+
if (accounts?.loginCommands?.[kind] !== undefined) {
|
|
52
|
+
return 'terminal';
|
|
53
|
+
}
|
|
54
|
+
return accounts?.chatLogins?.includes(kind) === true ? 'chat' : null;
|
|
55
|
+
}
|
|
56
|
+
|
|
44
57
|
export type AccountTone = 'muted' | 'needs-you' | 'error';
|
|
45
58
|
|
|
46
59
|
export const ACCOUNT_TONE_CLASSES: Record<AccountTone, string> = {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AgentKind } from '@adecore/agent-contracts';
|
|
2
|
+
|
|
3
|
+
/* A CLI's mark, drawn in the current text color. */
|
|
4
|
+
export interface AgentMark {
|
|
5
|
+
/* Read by a screen reader. */
|
|
6
|
+
title: string;
|
|
7
|
+
/* The `d` of one SVG path; several shapes go in one path. */
|
|
8
|
+
path: string;
|
|
9
|
+
/* `0 0 24 24` when absent. */
|
|
10
|
+
viewBox?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let hostMarks: Readonly<Partial<Record<AgentKind, AgentMark>>> = {};
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
* The marks of the kinds a host adds, and any it draws differently, set once before the first render. Every
|
|
17
|
+
* place the shared UI draws an agent's mark reads them; a kind with none keeps the built-in mark or the robot.
|
|
18
|
+
* A later call replaces the whole set.
|
|
19
|
+
*/
|
|
20
|
+
export function configureAgentMarks(marks: Readonly<Partial<Record<AgentKind, AgentMark>>>): void {
|
|
21
|
+
hostMarks = { ...marks };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function hostAgentMark(kind: AgentKind): AgentMark | undefined {
|
|
25
|
+
return hostMarks[kind];
|
|
26
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { UsageProvider } from '@adecore/agent-contracts';
|
|
2
2
|
|
|
3
3
|
/*
|
|
4
|
-
* The marks of Anthropic and OpenAI. Lucide has no brand marks, so these are the icon rule's
|
|
4
|
+
* The marks of Anthropic and OpenAI, and a monogram for a provider with no mark of its own. Lucide has no brand marks, so these are the icon rule's
|
|
5
5
|
* exception for provider marks. One path each, in `currentColor`, so a row decides how loud the logo is.
|
|
6
6
|
*/
|
|
7
7
|
export const PROVIDER_PATHS: Record<UsageProvider, string> = {
|
|
8
8
|
claude: 'M4.709 15.955l4.72-2.647.079-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.377-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.048.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.607.212-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.914-1.316-.012.01z',
|
|
9
|
-
codex: 'M21.55 10.004a5.416 5.416 0 00-.478-4.501c-1.217-2.09-3.662-3.166-6.05-2.66A5.59 5.59 0 0010.831 1C8.39.995 6.224 2.546 5.473 4.838A5.553 5.553 0 001.76 7.496a5.487 5.487 0 00.691 6.5 5.416 5.416 0 00.477 4.502c1.217 2.09 3.662 3.165 6.05 2.66A5.586 5.586 0 0013.168 23c2.443.006 4.61-1.546 5.361-3.84a5.554 5.554 0 003.715-2.66 5.488 5.488 0 00-.693-6.497v.001zm-8.381 11.558a4.199 4.199 0 01-2.675-.954c.034-.018.093-.05.132-.074l4.44-2.53a.71.71 0 00.364-.623v-6.176l1.877 1.069c.02.01.033.029.036.05v5.115c-.003 2.274-1.87 4.118-4.174 4.123zM4.192 17.78a4.059 4.059 0 01-.498-2.763c.032.019.09.055.131.078l4.44 2.53c.225.13.504.13.73 0l5.42-3.088v2.138a.068.068 0 01-.027.057L9.9 19.288c-1.999 1.136-4.552.46-5.707-1.51h-.001zM3.023 8.216A4.15 4.15 0 015.198 6.41l-.002.151v5.06a.711.711 0 00.364.624l5.42 3.087-1.876 1.07a.067.067 0 01-.063.005l-4.489-2.559c-1.995-1.14-2.679-3.658-1.53-5.63h.001zm15.417 3.54l-5.42-3.088L14.896 7.6a.067.067 0 01.063-.006l4.489 2.557c1.998 1.14 2.683 3.662 1.529 5.633a4.163 4.163 0 01-2.174 1.807V12.38a.71.71 0 00-.363-.623zm1.867-2.773a6.04 6.04 0 00-.132-.078l-4.44-2.53a.731.731 0 00-.729 0l-5.42 3.088V7.325a.068.068 0 01.027-.057L14.1 4.713c2-1.137 4.555-.46 5.707 1.513.487.833.664 1.809.499 2.757h.001zm-11.741 3.81l-1.877-1.068a.065.065 0 01-.036-.051V6.559c.001-2.277 1.873-4.122 4.181-4.12.976 0 1.92.338 2.671.954-.034.018-.092.05-.131.073l-4.44 2.53a.71.71 0 00-.365.623l-.003 6.173v.001zm1.02-2.168L12 9.25l2.414 1.375v2.75L12 14.75l-2.415-1.375v-2.75z'
|
|
9
|
+
codex: 'M21.55 10.004a5.416 5.416 0 00-.478-4.501c-1.217-2.09-3.662-3.166-6.05-2.66A5.59 5.59 0 0010.831 1C8.39.995 6.224 2.546 5.473 4.838A5.553 5.553 0 001.76 7.496a5.487 5.487 0 00.691 6.5 5.416 5.416 0 00.477 4.502c1.217 2.09 3.662 3.165 6.05 2.66A5.586 5.586 0 0013.168 23c2.443.006 4.61-1.546 5.361-3.84a5.554 5.554 0 003.715-2.66 5.488 5.488 0 00-.693-6.497v.001zm-8.381 11.558a4.199 4.199 0 01-2.675-.954c.034-.018.093-.05.132-.074l4.44-2.53a.71.71 0 00.364-.623v-6.176l1.877 1.069c.02.01.033.029.036.05v5.115c-.003 2.274-1.87 4.118-4.174 4.123zM4.192 17.78a4.059 4.059 0 01-.498-2.763c.032.019.09.055.131.078l4.44 2.53c.225.13.504.13.73 0l5.42-3.088v2.138a.068.068 0 01-.027.057L9.9 19.288c-1.999 1.136-4.552.46-5.707-1.51h-.001zM3.023 8.216A4.15 4.15 0 015.198 6.41l-.002.151v5.06a.711.711 0 00.364.624l5.42 3.087-1.876 1.07a.067.067 0 01-.063.005l-4.489-2.559c-1.995-1.14-2.679-3.658-1.53-5.63h.001zm15.417 3.54l-5.42-3.088L14.896 7.6a.067.067 0 01.063-.006l4.489 2.557c1.998 1.14 2.683 3.662 1.529 5.633a4.163 4.163 0 01-2.174 1.807V12.38a.71.71 0 00-.363-.623zm1.867-2.773a6.04 6.04 0 00-.132-.078l-4.44-2.53a.731.731 0 00-.729 0l-5.42 3.088V7.325a.068.068 0 01.027-.057L14.1 4.713c2-1.137 4.555-.46 5.707 1.513.487.833.664 1.809.499 2.757h.001zm-11.741 3.81l-1.877-1.068a.065.065 0 01-.036-.051V6.559c.001-2.277 1.873-4.122 4.181-4.12.976 0 1.92.338 2.671.954-.034.018-.092.05-.131.073l-4.44 2.53a.71.71 0 00-.365.623l-.003 6.173v.001zm1.02-2.168L12 9.25l2.414 1.375v2.75L12 14.75l-2.415-1.375v-2.75z',
|
|
10
|
+
nanogpt: 'M5 20V4h2.6l8.8 11.2V4H19v16h-2.6L7.6 8.8V20z'
|
|
10
11
|
};
|
package/src/host.ts
CHANGED
|
@@ -14,7 +14,8 @@ import type {
|
|
|
14
14
|
ChatUiQueryPayload,
|
|
15
15
|
ChatUiQueryReading,
|
|
16
16
|
ChatUiLinkPayload,
|
|
17
|
-
ChatUiLinkReading
|
|
17
|
+
ChatUiLinkReading,
|
|
18
|
+
ProviderPlatform
|
|
18
19
|
} from '@adecore/agent-contracts';
|
|
19
20
|
import { isApplePlatform } from '@adecore/ui';
|
|
20
21
|
import type { TimelineRow } from './chat/logic/timeline';
|
|
@@ -384,6 +385,15 @@ export interface ChatHost {
|
|
|
384
385
|
openLogin: ((scopeId: string, kind: AgentKind, accountId: string, name: string) => Promise<void>) | null;
|
|
385
386
|
/* Why a login cannot start from here right now, or null when it can. */
|
|
386
387
|
useLoginBlocked(scopeId: string): string | null;
|
|
388
|
+
/*
|
|
389
|
+
* Runs a command a provider's install page shows, its install or its sign-in, where a person sees it run,
|
|
390
|
+
* such as in a new terminal on the scope's machine; left out, the page offers to copy the command only.
|
|
391
|
+
*/
|
|
392
|
+
runProviderCommand?(scopeId: string, kind: AgentKind, command: string): void;
|
|
393
|
+
/* The system of the machine the scope's chats run on, whose install command the page shows first; left out, or null, the system of this page. */
|
|
394
|
+
useMachinePlatform?(scopeId: string): ProviderPlatform | null;
|
|
395
|
+
/* A sentence under a key the install page asks for in the environment, such as what the app does with it; left out, or null, none. */
|
|
396
|
+
providerKeyNote?(variable: string): string | null;
|
|
387
397
|
/* The name and the mark of a project of the host, for the usage per project; null draws the folder's own. */
|
|
388
398
|
useProjectLook(scopeId: string, projectId: string | null): { name: string; mark: ReactNode } | null;
|
|
389
399
|
/* What a person calls the machine the scope's chats run on, or null when it has no name worth saying. */
|
package/src/index.ts
CHANGED
|
@@ -37,6 +37,7 @@ export {
|
|
|
37
37
|
isConnectionError
|
|
38
38
|
} from './transport';
|
|
39
39
|
export { portTransport, type PortTransport } from './port-transport';
|
|
40
|
+
export { configureAgentMarks, type AgentMark } from './agents/agent-marks';
|
|
40
41
|
export { configureChatStorage, type ChatStorageRecord, type ChatStorageAdapter, type ChatStorageOptions } from './storage';
|
|
41
42
|
export { setLazyPrefetch, onLazyOpenError } from './lazy';
|
|
42
43
|
export { type MountedEntry, MountedRegistry } from './mounted-registry';
|
|
@@ -18,10 +18,47 @@ export default {
|
|
|
18
18
|
addAccount: 'Add a {{provider}} account',
|
|
19
19
|
notInstalled: 'Not installed',
|
|
20
20
|
missing: 'Missing',
|
|
21
|
-
notInstalledNote: '
|
|
21
|
+
notInstalledNote: 'Install one on this machine to use it here.',
|
|
22
22
|
none: 'No agent CLIs found on this machine.',
|
|
23
23
|
noAccounts: 'This machine keeps no accounts yet. Update Ruimte there to add one.'
|
|
24
24
|
},
|
|
25
|
+
install: {
|
|
26
|
+
notInstalled: 'Not installed',
|
|
27
|
+
steps: 'Steps to set up {{provider}}',
|
|
28
|
+
platform: 'System',
|
|
29
|
+
platforms: {
|
|
30
|
+
darwin: 'macOS',
|
|
31
|
+
linux: 'Linux',
|
|
32
|
+
win32: 'Windows'
|
|
33
|
+
},
|
|
34
|
+
copy: 'Copy',
|
|
35
|
+
copied: 'Copied',
|
|
36
|
+
run: 'Run in a terminal',
|
|
37
|
+
guide: 'Install guide',
|
|
38
|
+
install: {
|
|
39
|
+
title: 'Install the CLI',
|
|
40
|
+
none: 'There is no install command for this system.',
|
|
41
|
+
done: 'Installed, version {{version}}.',
|
|
42
|
+
doneNoVersion: 'Installed.'
|
|
43
|
+
},
|
|
44
|
+
signIn: {
|
|
45
|
+
title: 'Sign in',
|
|
46
|
+
command: 'Run this in a terminal and follow its steps:',
|
|
47
|
+
env: 'Set <code>{{variable}}</code> in the environment the CLI starts in.',
|
|
48
|
+
chat: 'The first chat asks you to sign in.',
|
|
49
|
+
or: 'Or',
|
|
50
|
+
unknown: 'The CLI asks you to sign in the first time it runs.'
|
|
51
|
+
},
|
|
52
|
+
start: {
|
|
53
|
+
title: 'Start working',
|
|
54
|
+
description: 'Pick {{provider}} for your next chat or terminal.'
|
|
55
|
+
},
|
|
56
|
+
footer: {
|
|
57
|
+
missing: 'Not found on this machine yet. It is checked again when you come back to this window.',
|
|
58
|
+
found: 'Found on this machine.'
|
|
59
|
+
},
|
|
60
|
+
fallback: 'Install {{provider}} on this machine to use it here.'
|
|
61
|
+
},
|
|
25
62
|
abilities: {
|
|
26
63
|
version: 'Version {{version}}.',
|
|
27
64
|
chatAndTerminal: 'Chat and terminal.',
|
|
@@ -65,7 +102,9 @@ export default {
|
|
|
65
102
|
signedOut: 'Not logged in',
|
|
66
103
|
signedOutDetail: "Opens a terminal that runs the CLI's login with this folder.",
|
|
67
104
|
logIn: 'Log in',
|
|
68
|
-
logInAgain: 'Log in again'
|
|
105
|
+
logInAgain: 'Log in again',
|
|
106
|
+
inChat: 'Logs in from a chat',
|
|
107
|
+
inChatDetail: 'The first chat on this account asks you to log in, and the CLI keeps that login in this folder.'
|
|
69
108
|
},
|
|
70
109
|
folder: {
|
|
71
110
|
label: 'Folder',
|
|
@@ -106,6 +145,7 @@ export default {
|
|
|
106
145
|
add: {
|
|
107
146
|
title: 'Add a {{provider}} account',
|
|
108
147
|
description: 'Ruimte makes a folder for it on the machine, then opens a terminal where you log in.',
|
|
148
|
+
chatDescription: 'A folder is made for it on the machine. The first chat on it asks you to log in.',
|
|
109
149
|
name: 'Name',
|
|
110
150
|
namePlaceholder: 'Work',
|
|
111
151
|
color: 'Color',
|
|
@@ -18,10 +18,47 @@ export default {
|
|
|
18
18
|
addAccount: 'Een {{provider}}-account toevoegen',
|
|
19
19
|
notInstalled: 'Niet geïnstalleerd',
|
|
20
20
|
missing: 'Ontbreekt',
|
|
21
|
-
notInstalledNote: 'Installeer er een
|
|
21
|
+
notInstalledNote: 'Installeer er een op deze machine om hem hier te gebruiken.',
|
|
22
22
|
none: "Geen agent-CLI's gevonden op deze machine.",
|
|
23
23
|
noAccounts: 'Deze machine houdt nog geen accounts bij. Werk Ruimte daar bij om er een toe te voegen.'
|
|
24
24
|
},
|
|
25
|
+
install: {
|
|
26
|
+
notInstalled: 'Niet geïnstalleerd',
|
|
27
|
+
steps: '{{provider}} instellen in stappen',
|
|
28
|
+
platform: 'Systeem',
|
|
29
|
+
platforms: {
|
|
30
|
+
darwin: 'macOS',
|
|
31
|
+
linux: 'Linux',
|
|
32
|
+
win32: 'Windows'
|
|
33
|
+
},
|
|
34
|
+
copy: 'Kopiëren',
|
|
35
|
+
copied: 'Gekopieerd',
|
|
36
|
+
run: 'In een terminal uitvoeren',
|
|
37
|
+
guide: 'Installatiegids',
|
|
38
|
+
install: {
|
|
39
|
+
title: 'Installeer de CLI',
|
|
40
|
+
none: 'Er is geen installatiecommando voor dit systeem.',
|
|
41
|
+
done: 'Geïnstalleerd, versie {{version}}.',
|
|
42
|
+
doneNoVersion: 'Geïnstalleerd.'
|
|
43
|
+
},
|
|
44
|
+
signIn: {
|
|
45
|
+
title: 'Log in',
|
|
46
|
+
command: 'Voer dit uit in een terminal en volg de stappen:',
|
|
47
|
+
env: 'Zet <code>{{variable}}</code> in de omgeving waarin de CLI start.',
|
|
48
|
+
chat: 'De eerste chat vraagt je om in te loggen.',
|
|
49
|
+
or: 'Of',
|
|
50
|
+
unknown: 'De CLI vraagt je om in te loggen als hij voor het eerst draait.'
|
|
51
|
+
},
|
|
52
|
+
start: {
|
|
53
|
+
title: 'Aan de slag',
|
|
54
|
+
description: 'Kies {{provider}} voor je volgende chat of terminal.'
|
|
55
|
+
},
|
|
56
|
+
footer: {
|
|
57
|
+
missing: 'Nog niet gevonden op deze machine. Er wordt opnieuw gekeken als je terugkomt in dit venster.',
|
|
58
|
+
found: 'Gevonden op deze machine.'
|
|
59
|
+
},
|
|
60
|
+
fallback: 'Installeer {{provider}} op deze machine om hem hier te gebruiken.'
|
|
61
|
+
},
|
|
25
62
|
abilities: {
|
|
26
63
|
version: 'Versie {{version}}.',
|
|
27
64
|
chatAndTerminal: 'Chat en terminal.',
|
|
@@ -65,7 +102,9 @@ export default {
|
|
|
65
102
|
signedOut: 'Niet ingelogd',
|
|
66
103
|
signedOutDetail: 'Opent een terminal die de login van de CLI met deze map draait.',
|
|
67
104
|
logIn: 'Inloggen',
|
|
68
|
-
logInAgain: 'Opnieuw inloggen'
|
|
105
|
+
logInAgain: 'Opnieuw inloggen',
|
|
106
|
+
inChat: 'Logt in vanuit een chat',
|
|
107
|
+
inChatDetail: 'De eerste chat op dit account vraagt je in te loggen, en de CLI bewaart die login in deze map.'
|
|
69
108
|
},
|
|
70
109
|
folder: {
|
|
71
110
|
label: 'Map',
|
|
@@ -106,6 +145,7 @@ export default {
|
|
|
106
145
|
add: {
|
|
107
146
|
title: 'Een {{provider}}-account toevoegen',
|
|
108
147
|
description: 'Ruimte maakt er een map voor op de machine en opent dan een terminal waarin je inlogt.',
|
|
148
|
+
chatDescription: 'Er komt een map voor op de machine. De eerste chat erop vraagt je in te loggen.',
|
|
109
149
|
name: 'Naam',
|
|
110
150
|
namePlaceholder: 'Werk',
|
|
111
151
|
color: 'Kleur',
|