feat(codex-link): persist direct subscription and AITURK connection modes
This commit is contained in:
1 parent
049c4d1338
commit
8f53b090f7
11 files changed
+188
-17
No files matched your search
@@ -225,12 +225,17 @@ describe('preference file', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('writes the contract shape and keeps unknown fields', () => {
|
it('writes the contract shape and keeps unknown fields', () => {
|
||||||
expect(JSON.parse(serializeCodexLinkSettings({}, false))).toEqual({ enabled: false, kapsam: 'ide' })
|
expect(JSON.parse(serializeCodexLinkSettings({}, false))).toEqual({
|
||||||
|
enabled: false,
|
||||||
|
mod: 'dogrudan',
|
||||||
|
kapsam: 'ide'
|
||||||
|
})
|
||||||
|
|
||||||
const { stored } = parseCodexLinkSettings('{"enabled": false, "kapsam": "hepsi", "gelecek": {"a": 1}}')
|
const { stored } = parseCodexLinkSettings('{"enabled": false, "kapsam": "hepsi", "gelecek": {"a": 1}}')
|
||||||
|
|
||||||
expect(JSON.parse(serializeCodexLinkSettings(stored, true))).toEqual({
|
expect(JSON.parse(serializeCodexLinkSettings(stored, true))).toEqual({
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
mod: 'dogrudan',
|
||||||
gelecek: { a: 1 },
|
gelecek: { a: 1 },
|
||||||
kapsam: 'hepsi'
|
kapsam: 'hepsi'
|
||||||
})
|
})
|
||||||
@@ -308,3 +313,17 @@ describe('bridge configuration and command', () => {
|
|||||||
expect(codexAutostartPlan(`${deep}\\pythonw.exe`, launch, `${deep}\\baslat.pyw`)).toBeNull()
|
expect(codexAutostartPlan(`${deep}\\pythonw.exe`, launch, `${deep}\\baslat.pyw`)).toBeNull()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('mode preference', () => {
|
||||||
|
it('uses direct for missing, invalid and malformed modes', () => {
|
||||||
|
for (const raw of [null, '{', '{}', '{"mod":"bad"}', '{"mod":false}']) {
|
||||||
|
expect(parseCodexLinkSettings(raw).mod).toBe('dogrudan')
|
||||||
|
}
|
||||||
|
expect(parseCodexLinkSettings('{"mod":"aiturk"}').mod).toBe('aiturk')
|
||||||
|
})
|
||||||
|
it('preserves other fields when changing mode', () => {
|
||||||
|
const stored = { enabled: false, mod: 'aiturk', kapsam: 'hepsi', future: { a: 1 } }
|
||||||
|
expect(JSON.parse(serializeCodexLinkSettings(stored, false, 'dogrudan'))).toEqual({ ...stored, mod: 'dogrudan' })
|
||||||
|
expect(JSON.parse(serializeCodexLinkSettings(stored, true)).mod).toBe('aiturk')
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -300,6 +300,7 @@ export function removeCodexLinkLine(source: string): CodexConfigEdit {
|
|||||||
|
|
||||||
export interface CodexLinkSettings {
|
export interface CodexLinkSettings {
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
|
mod: 'dogrudan' | 'aiturk'
|
||||||
/** Every field of the stored object, so a rewrite keeps what it did not set. */
|
/** Every field of the stored object, so a rewrite keeps what it did not set. */
|
||||||
stored: Record<string, unknown>
|
stored: Record<string, unknown>
|
||||||
}
|
}
|
||||||
@@ -307,26 +308,34 @@ export interface CodexLinkSettings {
|
|||||||
/** Missing, unreadable or malformed means ON: the feature is on by default. */
|
/** Missing, unreadable or malformed means ON: the feature is on by default. */
|
||||||
export function parseCodexLinkSettings(raw: null | string | undefined): CodexLinkSettings {
|
export function parseCodexLinkSettings(raw: null | string | undefined): CodexLinkSettings {
|
||||||
if (typeof raw !== 'string') {
|
if (typeof raw !== 'string') {
|
||||||
return { enabled: true, stored: {} }
|
return { enabled: true, mod: 'dogrudan', stored: {} }
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const parsed = JSON.parse(raw)
|
const parsed = JSON.parse(raw)
|
||||||
|
|
||||||
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
||||||
return { enabled: true, stored: {} }
|
return { enabled: true, mod: 'dogrudan', stored: {} }
|
||||||
}
|
}
|
||||||
|
|
||||||
return { enabled: parsed.enabled !== false, stored: parsed as Record<string, unknown> }
|
return {
|
||||||
|
enabled: parsed.enabled !== false,
|
||||||
|
mod: parsed.mod === 'aiturk' ? 'aiturk' : 'dogrudan',
|
||||||
|
stored: parsed as Record<string, unknown>
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
return { enabled: true, stored: {} }
|
return { enabled: true, mod: 'dogrudan', stored: {} }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function serializeCodexLinkSettings(stored: Record<string, unknown>, enabled: boolean): string {
|
export function serializeCodexLinkSettings(
|
||||||
|
stored: Record<string, unknown>,
|
||||||
|
enabled: boolean,
|
||||||
|
mod: 'dogrudan' | 'aiturk' = stored.mod === 'aiturk' ? 'aiturk' : 'dogrudan'
|
||||||
|
): string {
|
||||||
const kapsam = typeof stored.kapsam === 'string' && stored.kapsam ? stored.kapsam : 'ide'
|
const kapsam = typeof stored.kapsam === 'string' && stored.kapsam ? stored.kapsam : 'ide'
|
||||||
|
|
||||||
return `${JSON.stringify({ ...stored, enabled, kapsam }, null, 2)}\n`
|
return `${JSON.stringify({ ...stored, enabled, mod, kapsam }, null, 2)}\n`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Bridge configuration ────────────────────────────────────────────────────
|
// ── Bridge configuration ────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
* code into its program. See codex-link.ts for what the fields mean in action.
|
* code into its program. See codex-link.ts for what the fields mean in action.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export type CodexLinkMode = 'dogrudan' | 'aiturk'
|
||||||
|
|
||||||
export type CodexLinkIdeState = 'anahtar-yok' | 'kapali' | 'tamam'
|
export type CodexLinkIdeState = 'anahtar-yok' | 'kapali' | 'tamam'
|
||||||
|
|
||||||
/** Stable codes; the renderer owns the wording. Detail goes to the desktop log. */
|
/** Stable codes; the renderer owns the wording. Detail goes to the desktop log. */
|
||||||
@@ -18,6 +20,7 @@ export type CodexLinkError =
|
|||||||
|
|
||||||
export interface CodexLinkStatus {
|
export interface CodexLinkStatus {
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
|
mod: CodexLinkMode
|
||||||
/** Codex's `config.toml` exists on this machine. */
|
/** Codex's `config.toml` exists on this machine. */
|
||||||
codexFound: boolean
|
codexFound: boolean
|
||||||
/** The IDE's own Python environment exists (first-run setup finished). */
|
/** The IDE's own Python environment exists (first-run setup finished). */
|
||||||
@@ -34,6 +37,7 @@ export interface CodexLinkStatus {
|
|||||||
|
|
||||||
export interface CodexLinkPatch {
|
export interface CodexLinkPatch {
|
||||||
enabled?: boolean
|
enabled?: boolean
|
||||||
|
mod?: CodexLinkMode
|
||||||
/** "Sync now": re-run the apply without changing the preference. */
|
/** "Sync now": re-run the apply without changing the preference. */
|
||||||
sync?: boolean
|
sync?: boolean
|
||||||
}
|
}
|
||||||
@@ -52,6 +52,7 @@ function createWorld(patch: Partial<World> & { files?: Map<string, string> } = {
|
|||||||
events: [],
|
events: [],
|
||||||
files: new Map([
|
files: new Map([
|
||||||
[PATHS.codexConfig, USER_CONFIG],
|
[PATHS.codexConfig, USER_CONFIG],
|
||||||
|
[PATHS.settingsFile, JSON.stringify({ enabled: true, mod: 'aiturk' })],
|
||||||
[PATHS.bridgeScript, '# bridge'],
|
[PATHS.bridgeScript, '# bridge'],
|
||||||
[PATHS.pythonw, ''],
|
[PATHS.pythonw, ''],
|
||||||
[PATHS.python, '']
|
[PATHS.python, '']
|
||||||
@@ -192,6 +193,7 @@ describe('applyCodexLink', () => {
|
|||||||
|
|
||||||
expect(status).toEqual({
|
expect(status).toEqual({
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
mod: 'aiturk',
|
||||||
codexFound: true,
|
codexFound: true,
|
||||||
venvReady: true,
|
venvReady: true,
|
||||||
foreign: false,
|
foreign: false,
|
||||||
@@ -328,7 +330,11 @@ describe('applyCodexLink', () => {
|
|||||||
'autostart clear',
|
'autostart clear',
|
||||||
'stop'
|
'stop'
|
||||||
])
|
])
|
||||||
expect(JSON.parse(world.files.get(PATHS.settingsFile) ?? '')).toEqual({ enabled: false, kapsam: 'ide' })
|
expect(JSON.parse(world.files.get(PATHS.settingsFile) ?? '')).toEqual({
|
||||||
|
enabled: false,
|
||||||
|
mod: 'aiturk',
|
||||||
|
kapsam: 'ide'
|
||||||
|
})
|
||||||
// Byte for byte what the user had before the feature touched it.
|
// Byte for byte what the user had before the feature touched it.
|
||||||
expect(world.files.get(PATHS.codexConfig)).toBe(USER_CONFIG)
|
expect(world.files.get(PATHS.codexConfig)).toBe(USER_CONFIG)
|
||||||
expect(world.autostart).toBeNull()
|
expect(world.autostart).toBeNull()
|
||||||
@@ -344,7 +350,8 @@ describe('applyCodexLink', () => {
|
|||||||
world.configWriteFails = true
|
world.configWriteFails = true
|
||||||
|
|
||||||
expect(await link.apply({ enabled: false })).toMatchObject({
|
expect(await link.apply({ enabled: false })).toMatchObject({
|
||||||
bridgeRunning: true, lastError: 'config-write-failed'
|
bridgeRunning: true,
|
||||||
|
lastError: 'config-write-failed'
|
||||||
})
|
})
|
||||||
expect(world.files.get(PATHS.codexConfig)).toBe(linkedConfig)
|
expect(world.files.get(PATHS.codexConfig)).toBe(linkedConfig)
|
||||||
expect(world.autostart).toBe(autostart)
|
expect(world.autostart).toBe(autostart)
|
||||||
@@ -358,7 +365,7 @@ describe('applyCodexLink', () => {
|
|||||||
|
|
||||||
it('stays off across applies and comes back when turned on', async () => {
|
it('stays off across applies and comes back when turned on', async () => {
|
||||||
const { link, world } = createWorld()
|
const { link, world } = createWorld()
|
||||||
world.files.set(PATHS.settingsFile, '{"enabled": false, "kapsam": "ide", "not": "ours"}')
|
world.files.set(PATHS.settingsFile, '{"enabled": false, "mod": "aiturk", "kapsam": "ide", "not": "ours"}')
|
||||||
|
|
||||||
expect(await link.apply()).toMatchObject({ enabled: false, bridgeRunning: false })
|
expect(await link.apply()).toMatchObject({ enabled: false, bridgeRunning: false })
|
||||||
expect(spawns(world)).toEqual([])
|
expect(spawns(world)).toEqual([])
|
||||||
@@ -367,6 +374,7 @@ describe('applyCodexLink', () => {
|
|||||||
expect(await link.apply({ enabled: true })).toMatchObject({ enabled: true, bridgeRunning: true })
|
expect(await link.apply({ enabled: true })).toMatchObject({ enabled: true, bridgeRunning: true })
|
||||||
expect(JSON.parse(world.files.get(PATHS.settingsFile) ?? '')).toEqual({
|
expect(JSON.parse(world.files.get(PATHS.settingsFile) ?? '')).toEqual({
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
mod: 'aiturk',
|
||||||
kapsam: 'ide',
|
kapsam: 'ide',
|
||||||
not: 'ours'
|
not: 'ours'
|
||||||
})
|
})
|
||||||
@@ -424,3 +432,44 @@ describe('getStatus', () => {
|
|||||||
expect(effects(world)).toEqual([])
|
expect(effects(world)).toEqual([])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('connection mode', () => {
|
||||||
|
it('defaults to direct, starts bridge and autostart without writing a line', async () => {
|
||||||
|
const { link, world } = createWorld()
|
||||||
|
world.files.delete(PATHS.settingsFile)
|
||||||
|
expect(await link.apply()).toMatchObject({ mod: 'dogrudan', bridgeRunning: true })
|
||||||
|
expect(world.files.get(PATHS.codexConfig)).toBe(USER_CONFIG)
|
||||||
|
expect(spawns(world)).toHaveLength(1)
|
||||||
|
expect(world.autostart).not.toBeNull()
|
||||||
|
})
|
||||||
|
it('removes the old line and preserves the current bridge in direct mode', async () => {
|
||||||
|
const { link, world } = createWorld({ listening: true })
|
||||||
|
world.files.set(PATHS.settingsFile, '{"mod":"dogrudan"}')
|
||||||
|
world.files.set(PATHS.codexConfig, `${CODEX_LINK_LINE}\r\n${USER_CONFIG}`)
|
||||||
|
await link.apply()
|
||||||
|
expect(world.files.get(PATHS.codexConfig)).toBe(USER_CONFIG)
|
||||||
|
expect(spawns(world)).toHaveLength(0)
|
||||||
|
expect(world.events).not.toContain('stop')
|
||||||
|
expect(world.autostart).not.toBeNull()
|
||||||
|
})
|
||||||
|
it('switches both ways, persists mode and preserves unrelated preferences', async () => {
|
||||||
|
const { link, world } = createWorld({ listening: true })
|
||||||
|
world.files.set(PATHS.settingsFile, '{"mod":"dogrudan","custom":{"keep":1}}')
|
||||||
|
await link.apply({ mod: 'aiturk' })
|
||||||
|
expect(world.files.get(PATHS.codexConfig)).toContain(CODEX_LINK_LINE)
|
||||||
|
await link.apply({ mod: 'dogrudan' })
|
||||||
|
expect(world.files.get(PATHS.codexConfig)).toBe(USER_CONFIG)
|
||||||
|
expect(JSON.parse(world.files.get(PATHS.settingsFile)!)).toMatchObject({ mod: 'dogrudan', custom: { keep: 1 } })
|
||||||
|
await link.apply()
|
||||||
|
expect(world.files.get(PATHS.codexConfig)).toBe(USER_CONFIG)
|
||||||
|
expect(world.events).not.toContain('stop')
|
||||||
|
})
|
||||||
|
it.each(['dogrudan', 'aiturk'] as const)('preserves foreign URL in %s', async mod => {
|
||||||
|
const { link, world } = createWorld()
|
||||||
|
const foreign = 'openai_base_url = "https://other.example"\n'
|
||||||
|
world.files.set(PATHS.codexConfig, foreign)
|
||||||
|
await link.apply({ mod })
|
||||||
|
expect(world.files.get(PATHS.codexConfig)).toBe(foreign)
|
||||||
|
expect(spawns(world)).toHaveLength(0)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -186,6 +186,7 @@ export function createCodexLink(deps: CodexLinkDeps): CodexLink {
|
|||||||
bridge: BridgeProbe
|
bridge: BridgeProbe
|
||||||
): CodexLinkStatus => ({
|
): CodexLinkStatus => ({
|
||||||
enabled,
|
enabled,
|
||||||
|
mod: readSettings().mod,
|
||||||
codexFound: config.found,
|
codexFound: config.found,
|
||||||
venvReady: interpreter() !== null,
|
venvReady: interpreter() !== null,
|
||||||
foreign: config.owner === 'foreign',
|
foreign: config.owner === 'foreign',
|
||||||
@@ -329,7 +330,8 @@ export function createCodexLink(deps: CodexLinkDeps): CodexLink {
|
|||||||
return bridge
|
return bridge
|
||||||
}
|
}
|
||||||
|
|
||||||
const edit = addCodexLinkLine(config.text ?? '')
|
const edit =
|
||||||
|
readSettings().mod === 'aiturk' ? addCodexLinkLine(config.text ?? '') : removeCodexLinkLine(config.text ?? '')
|
||||||
|
|
||||||
if (edit.changed) {
|
if (edit.changed) {
|
||||||
try {
|
try {
|
||||||
@@ -364,9 +366,16 @@ export function createCodexLink(deps: CodexLinkDeps): CodexLink {
|
|||||||
const run = async (patch?: CodexLinkPatch): Promise<CodexLinkStatus> => {
|
const run = async (patch?: CodexLinkPatch): Promise<CodexLinkStatus> => {
|
||||||
lastError = null
|
lastError = null
|
||||||
|
|
||||||
if (typeof patch?.enabled === 'boolean') {
|
if (typeof patch?.enabled === 'boolean' || patch?.mod === 'aiturk' || patch?.mod === 'dogrudan') {
|
||||||
try {
|
try {
|
||||||
deps.writeTextAtomic(paths.settingsFile, serializeCodexLinkSettings(readSettings().stored, patch.enabled))
|
deps.writeTextAtomic(
|
||||||
|
paths.settingsFile,
|
||||||
|
serializeCodexLinkSettings(
|
||||||
|
readSettings().stored,
|
||||||
|
patch?.enabled ?? readSettings().enabled,
|
||||||
|
patch?.mod ?? readSettings().mod
|
||||||
|
)
|
||||||
|
)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// The preference did not stick, so do not act on it either: what the
|
// The preference did not stick, so do not act on it either: what the
|
||||||
// machine does must match what the file says after a restart.
|
// machine does must match what the file says after a restart.
|
||||||
|
|||||||
@@ -17318,6 +17318,7 @@ ipcMain.handle('aiturk:codex-link:set', async (event, patch) => {
|
|||||||
|
|
||||||
return codexLink.apply({
|
return codexLink.apply({
|
||||||
enabled: typeof patch?.enabled === 'boolean' ? patch.enabled : undefined,
|
enabled: typeof patch?.enabled === 'boolean' ? patch.enabled : undefined,
|
||||||
|
mod: patch?.mod === 'aiturk' || patch?.mod === 'dogrudan' ? patch.mod : undefined,
|
||||||
sync: patch?.sync === true
|
sync: patch?.sync === true
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "aiturk-ide",
|
"name": "aiturk-ide",
|
||||||
"productName": "AITURK IDE",
|
"productName": "AITURK IDE",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.0.0-beta.37",
|
"version": "1.0.0-beta.38",
|
||||||
"description": "TurkServis resmi yapay zekâ geliştirme ortamı. Hermes Agent tabanlıdır.",
|
"description": "TurkServis resmi yapay zekâ geliştirme ortamı. Hermes Agent tabanlıdır.",
|
||||||
"author": "AITURK / TurkServis",
|
"author": "AITURK / TurkServis",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ vi.mock('@/store/notifications', () => ({ notifyError }))
|
|||||||
|
|
||||||
const RUNNING: CodexLinkStatus = {
|
const RUNNING: CodexLinkStatus = {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
mod: 'aiturk',
|
||||||
codexFound: true,
|
codexFound: true,
|
||||||
venvReady: true,
|
venvReady: true,
|
||||||
foreign: false,
|
foreign: false,
|
||||||
@@ -198,3 +199,18 @@ describe('Codex Link settings', () => {
|
|||||||
expect(get).not.toHaveBeenCalled()
|
expect(get).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
describe('connection mode controls', () => {
|
||||||
|
it('sends the mode and renders the saved direct preference', async () => {
|
||||||
|
get.mockResolvedValue({ ...RUNNING, mod: 'dogrudan' })
|
||||||
|
set.mockResolvedValue({ ...RUNNING, mod: 'aiturk' })
|
||||||
|
renderPage()
|
||||||
|
const direct = await screen.findByRole('radio', { name: 'Doğrudan (önerilen): abonelik modelleri köprüden bağımsız' })
|
||||||
|
await waitFor(() => expect((direct as HTMLInputElement).checked).toBe(true))
|
||||||
|
fireEvent.click(screen.getByRole('radio', { name: 'AITURK: combolar Codex listesinde, tüm modeller köprüden' }))
|
||||||
|
await waitFor(() => expect(set).toHaveBeenCalledWith({ mod: 'aiturk' }))
|
||||||
|
await waitFor(() => expect((direct as HTMLInputElement).checked).toBe(false))
|
||||||
|
expect(screen.getByText("Codex'i yeniden başlatın")).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -57,7 +57,7 @@ export function codexLinkStatusLines(status: CodexLinkStatus, copy: CodexLinkCop
|
|||||||
|
|
||||||
if (status.ideDurum === 'anahtar-yok') {
|
if (status.ideDurum === 'anahtar-yok') {
|
||||||
lines.push({ id: 'ide', text: copy.signIn, tone: 'warn' })
|
lines.push({ id: 'ide', text: copy.signIn, tone: 'warn' })
|
||||||
} else if (status.ideDurum === 'tamam' && status.ideModel !== null) {
|
} else if (status.mod === 'aiturk' && status.ideDurum === 'tamam' && status.ideModel !== null) {
|
||||||
lines.push({ id: 'ide', text: copy.models(status.ideModel), tone: 'ok' })
|
lines.push({ id: 'ide', text: copy.models(status.ideModel), tone: 'ok' })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,6 +170,27 @@ export function CodexLinkSettings() {
|
|||||||
title={copy.title}
|
title={copy.title}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<ListRow
|
||||||
|
title={copy.modeTitle}
|
||||||
|
below={
|
||||||
|
<fieldset disabled={!status || busy !== null} className="grid gap-2 py-2">
|
||||||
|
{(['dogrudan', 'aiturk'] as const).map(mod => (
|
||||||
|
<label key={mod} className="flex min-h-11 cursor-pointer items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="codex-link-mode"
|
||||||
|
value={mod}
|
||||||
|
checked={(status?.mod ?? 'dogrudan') === mod}
|
||||||
|
onChange={() => void change({ mod }, 'toggle')}
|
||||||
|
/>
|
||||||
|
<span>{mod === 'dogrudan' ? copy.directMode : copy.aiturkMode}</span>
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
<p className="text-(--ui-text-tertiary)">{copy.modeRestartNote}</p>
|
||||||
|
</fieldset>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
{!status && !loadFailed && <ListRowSkeleton wide />}
|
{!status && !loadFailed && <ListRowSkeleton wide />}
|
||||||
|
|
||||||
{loadFailed && !status && (
|
{loadFailed && !status && (
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ import { useI18n } from './context'
|
|||||||
// AITURK-distribution strings (see ./aiturk): Turkish for `tr`, English for
|
// AITURK-distribution strings (see ./aiturk): Turkish for `tr`, English for
|
||||||
// every other locale.
|
// every other locale.
|
||||||
const en = {
|
const en = {
|
||||||
|
modeTitle: 'Connection mode',
|
||||||
|
directMode: 'Direct (recommended): subscription models independent of the bridge',
|
||||||
|
aiturkMode: 'AITURK: combos in the Codex list, all models through the bridge',
|
||||||
|
modeRestartNote: 'Restart Codex.',
|
||||||
nav: 'Codex Link',
|
nav: 'Codex Link',
|
||||||
title: 'Codex Link',
|
title: 'Codex Link',
|
||||||
description:
|
description:
|
||||||
@@ -39,6 +43,10 @@ const en = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const tr: typeof en = {
|
const tr: typeof en = {
|
||||||
|
modeTitle: 'Bağlantı modu',
|
||||||
|
directMode: 'Doğrudan (önerilen): abonelik modelleri köprüden bağımsız',
|
||||||
|
aiturkMode: 'AITURK: combolar Codex listesinde, tüm modeller köprüden',
|
||||||
|
modeRestartNote: "Codex'i yeniden başlatın",
|
||||||
nav: 'Codex Bağlama',
|
nav: 'Codex Bağlama',
|
||||||
title: 'Codex Bağlama',
|
title: 'Codex Bağlama',
|
||||||
description:
|
description:
|
||||||
@@ -71,10 +79,45 @@ const tr: typeof en = {
|
|||||||
restartNote: "Codex'te yeni modelleri görmek için Codex'i yeniden açın."
|
restartNote: "Codex'te yeni modelleri görmek için Codex'i yeniden açın."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const modeTranslations = {
|
||||||
|
zh: {
|
||||||
|
modeTitle: '连接模式',
|
||||||
|
directMode: '直接(推荐):订阅模型独立于桥接',
|
||||||
|
aiturkMode: 'AITURK:组合显示在 Codex 列表中,所有模型通过桥接',
|
||||||
|
modeRestartNote: '请重启 Codex。'
|
||||||
|
},
|
||||||
|
'zh-hant': {
|
||||||
|
modeTitle: '連線模式',
|
||||||
|
directMode: '直接(建議):訂閱模型獨立於橋接',
|
||||||
|
aiturkMode: 'AITURK:組合顯示在 Codex 清單中,所有模型透過橋接',
|
||||||
|
modeRestartNote: '請重新啟動 Codex。'
|
||||||
|
},
|
||||||
|
ja: {
|
||||||
|
modeTitle: '接続モード',
|
||||||
|
directMode: '直接(推奨):サブスクリプションモデルはブリッジから独立',
|
||||||
|
aiturkMode: 'AITURK:Codex の一覧にコンボを表示、すべてのモデルはブリッジ経由',
|
||||||
|
modeRestartNote: 'Codex を再起動してください。'
|
||||||
|
},
|
||||||
|
ar: {
|
||||||
|
modeTitle: 'وضع الاتصال',
|
||||||
|
directMode: 'مباشر (موصى به): نماذج الاشتراك مستقلة عن الجسر',
|
||||||
|
aiturkMode: 'AITURK: مجموعات النماذج في قائمة Codex، وجميع النماذج عبر الجسر',
|
||||||
|
modeRestartNote: 'أعد تشغيل Codex.'
|
||||||
|
},
|
||||||
|
ru: {
|
||||||
|
modeTitle: 'Режим подключения',
|
||||||
|
directMode: 'Напрямую (рекомендуется): модели подписки независимы от моста',
|
||||||
|
aiturkMode: 'AITURK: комбинации в списке Codex, все модели через мост',
|
||||||
|
modeRestartNote: 'Перезапустите Codex.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export type CodexLinkCopy = typeof en
|
export type CodexLinkCopy = typeof en
|
||||||
|
|
||||||
export function useCodexLinkCopy(): CodexLinkCopy {
|
export function useCodexLinkCopy(): CodexLinkCopy {
|
||||||
const { locale } = useI18n()
|
const { locale } = useI18n()
|
||||||
|
|
||||||
return locale === 'tr' ? tr : en
|
if (locale === 'tr') return tr
|
||||||
|
if (locale === 'en') return en
|
||||||
|
return { ...en, ...modeTranslations[locale] }
|
||||||
}
|
}
|
||||||
Generated
+1
-1
@@ -65,7 +65,7 @@
|
|||||||
},
|
},
|
||||||
"apps/desktop": {
|
"apps/desktop": {
|
||||||
"name": "aiturk-ide",
|
"name": "aiturk-ide",
|
||||||
"version": "1.0.0-beta.37",
|
"version": "1.0.0-beta.38",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@assistant-ui/core": "0.2.23",
|
"@assistant-ui/core": "0.2.23",
|
||||||
|
|||||||
Reference in new issue
Block a user