@azure-id/orc 1.2.1 → 1.4.0
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/CHANGELOG.md +151 -0
- package/README-id.md +44 -0
- package/README.md +54 -62
- package/bin/cli.js +2429 -4
- package/bin/verify-contracts.js +35 -1
- package/bin/verify-package.js +15 -0
- package/bin/webui/api.js +71 -0
- package/bin/webui/app.html +213 -210
- package/bin/webui/css/06-responsive.css +28 -0
- package/bin/webui/css/panels/hookui.css +379 -0
- package/bin/webui/fixtures/hookui.js +409 -0
- package/bin/webui/fixtures/index.js +17 -0
- package/bin/webui/i18n/en/hookui.json +119 -0
- package/bin/webui/i18n/en/nav.json +22 -21
- package/bin/webui/i18n/en/overview.json +4 -1
- package/bin/webui/i18n/en/tour.json +3 -1
- package/bin/webui/i18n/id/hookui.json +119 -0
- package/bin/webui/i18n/id/nav.json +22 -21
- package/bin/webui/i18n/id/overview.json +4 -1
- package/bin/webui/i18n/id/tour.json +3 -1
- package/bin/webui/js/01-i18n.js +152 -151
- package/bin/webui/js/90-tour.js +6 -0
- package/bin/webui/js/panels/hookui.js +891 -0
- package/bin/webui/js/panels/overview.js +7 -0
- package/package.json +1 -1
- package/templates/hooks/README.md +80 -2
- package/templates/hooks/orc-statusline-render.js +921 -0
- package/templates/hooks/orc-statusline.js +715 -68
- package/templates/hooks/orc-subagent-line.js +219 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hookui.title": "Antarmuka CLI Hook",
|
|
3
|
+
"hookui.sub": "Bangun baris status ORC. Tiga baris. Tiap baris memuat 1 sampai 5 bagian.",
|
|
4
|
+
"hookui.noData": "Baris status tidak dapat dibaca.",
|
|
5
|
+
"hookui.gate": "Status",
|
|
6
|
+
"hookui.on": "hidup",
|
|
7
|
+
"hookui.off": "mati",
|
|
8
|
+
"hookui.saved": "tata letak tersimpan",
|
|
9
|
+
"hookui.gateOnNote": "Baris Anda yang tampil. Dua baris bawaan ORC dimatikan.",
|
|
10
|
+
"hookui.gateOffNote": "ORC menampilkan dua baris bawaannya. Bangun tata letak di bawah, lalu aktifkan ini. Tidak ada yang berubah sampai Anda melakukannya.",
|
|
11
|
+
"hookui.turnOn": "Aktifkan",
|
|
12
|
+
"hookui.turnOff": "Nonaktifkan",
|
|
13
|
+
"hookui.switchFailed": "Saklar tidak dapat diubah.",
|
|
14
|
+
"hookui.cautions": "Peringatan",
|
|
15
|
+
"hookui.preview": "Pratinjau",
|
|
16
|
+
"hookui.previewUnavailable": "Pratinjau tidak dapat digambar sampai tata letak sah.",
|
|
17
|
+
"hookui.degrades": "Tata letak yang sama, dalam tiga bentuk yang dapat dipaksakan terminal.",
|
|
18
|
+
"hookui.degNoColor": "tanpa warna",
|
|
19
|
+
"hookui.degAscii": "teks polos",
|
|
20
|
+
"hookui.degNoMotion": "tanpa gerak",
|
|
21
|
+
"hookui.cells": "lebar {n} sel",
|
|
22
|
+
"hookui.cellsPlural": "lebar {n} sel",
|
|
23
|
+
"hookui.board": "Papan",
|
|
24
|
+
"hookui.lineN": "Baris {n}",
|
|
25
|
+
"hookui.lineNPlural": "Baris {n}",
|
|
26
|
+
"hookui.add": "+ tambah bagian",
|
|
27
|
+
"hookui.addTo": "+ baris {n}",
|
|
28
|
+
"hookui.addToPlural": "+ baris {n}",
|
|
29
|
+
"hookui.toLine": "baris {n}",
|
|
30
|
+
"hookui.toLinePlural": "baris {n}",
|
|
31
|
+
"hookui.fillFirst": "Isi baris {n} dulu.",
|
|
32
|
+
"hookui.lineFull": "Baris {n} sudah penuh.",
|
|
33
|
+
"hookui.separator": "Antar bagian",
|
|
34
|
+
"hookui.sepChange": "pemisah baris {n} = \"{s}\"",
|
|
35
|
+
"hookui.moveLeft": "Pindah ke kiri",
|
|
36
|
+
"hookui.moveRight": "Pindah ke kanan",
|
|
37
|
+
"hookui.edit": "Ubah bagian ini",
|
|
38
|
+
"hookui.remove": "Lepas bagian ini",
|
|
39
|
+
"hookui.removed": "{id} dilepas",
|
|
40
|
+
"hookui.moved": "{id} pindah ke baris {n}",
|
|
41
|
+
"hookui.added": "{id} ditambahkan ke baris {n}",
|
|
42
|
+
"hookui.unknownComponent": "Versi ORC ini tidak punya bagian tersebut.",
|
|
43
|
+
"hookui.shape": "Bentuk",
|
|
44
|
+
"hookui.shapeChange": "bentuk {id} = {r}",
|
|
45
|
+
"hookui.glyphs": "Simbol",
|
|
46
|
+
"hookui.glyphChange": "simbol {id} = {v}",
|
|
47
|
+
"hookui.words": "Kata",
|
|
48
|
+
"hookui.label": "Nama",
|
|
49
|
+
"hookui.labelPlaceholder": "ketik nama apa saja",
|
|
50
|
+
"hookui.labelChange": "nama {id} = \"{v}\"",
|
|
51
|
+
"hookui.case": "Huruf",
|
|
52
|
+
"hookui.caseChange": "huruf {id} = {v}",
|
|
53
|
+
"hookui.colour": "Warna",
|
|
54
|
+
"hookui.labelColour": "Warna nama",
|
|
55
|
+
"hookui.valueColour": "Warna nilai",
|
|
56
|
+
"hookui.colourChange": "warna {id} = {v}",
|
|
57
|
+
"hookui.ramp": "Warna menurut nilai",
|
|
58
|
+
"hookui.rampChange": "warna menurut nilai {id} = {v}",
|
|
59
|
+
"hookui.stateColourWarn": "Bagian ini menyampaikan sesuatu lewat warnanya ({s}). Warna tetap menyembunyikan itu.",
|
|
60
|
+
"hookui.emphasis": "Ketebalan",
|
|
61
|
+
"hookui.emphasisChange": "ketebalan {id} = {v}",
|
|
62
|
+
"hookui.fontWhy": "Terminal Anda yang menentukan ukuran huruf. ORC tidak dapat mengubahnya. Tebal terlihat lebih besar dan redup terlihat lebih kecil. Agar sebuah bagian memakai ruang lebih, beri lebih banyak sel.",
|
|
63
|
+
"hookui.numbers": "Angka",
|
|
64
|
+
"hookui.format": "Bentuk",
|
|
65
|
+
"hookui.formatChange": "bentuk {id} = {v}",
|
|
66
|
+
"hookui.compact": "Bentuk singkat",
|
|
67
|
+
"hookui.compactChange": "bentuk singkat {id} = {v}",
|
|
68
|
+
"hookui.minWidthWarn": "Angka yang berubah lebar mendorong semua yang di kanannya. Tetapkan lebar minimum untuk mencegahnya.",
|
|
69
|
+
"hookui.whenToShow": "Kapan ditampilkan",
|
|
70
|
+
"hookui.hideChange": "sembunyikan {id} saat = {v}",
|
|
71
|
+
"hookui.explain": "Asal nilai ini",
|
|
72
|
+
"hookui.explainFailed": "Itu tidak dapat dibaca sekarang.",
|
|
73
|
+
"hookui.palette": "Bagian yang dapat ditambahkan",
|
|
74
|
+
"hookui.searchPlaceholder": "cari",
|
|
75
|
+
"hookui.matches": "{n} cocok",
|
|
76
|
+
"hookui.matchesPlural": "{n} cocok",
|
|
77
|
+
"hookui.noMatch": "Tidak ada yang cocok.",
|
|
78
|
+
"hookui.presets": "Tata letak siap pakai",
|
|
79
|
+
"hookui.active": "dipakai",
|
|
80
|
+
"hookui.apply": "Pakai ini",
|
|
81
|
+
"hookui.applyTitle": "Pakai \"{name}\"?",
|
|
82
|
+
"hookui.applyWarn": "Ini MENGGANTI seluruh tata letak Anda. Setiap bagian yang Anda pasang dilepas dan diganti yang siap pakai. Ini tidak dapat dibatalkan.",
|
|
83
|
+
"hookui.applyFailed": "Tata letak itu tidak dapat dipakai.",
|
|
84
|
+
"hookui.resetLayout": "Mulai lagi",
|
|
85
|
+
"hookui.resetTitle": "Mulai lagi?",
|
|
86
|
+
"hookui.resetWarn": "Ini MENGGANTI tata letak Anda dengan bawaan ORC. Setiap bagian yang Anda pasang dilepas. Ini tidak dapat dibatalkan.",
|
|
87
|
+
"hookui.advanced": "Lainnya",
|
|
88
|
+
"hookui.theme": "Set warna",
|
|
89
|
+
"hookui.themeChange": "set warna = {v}",
|
|
90
|
+
"hookui.recompile": "Bangun ulang",
|
|
91
|
+
"hookui.recompiled": "Sudah dibangun ulang.",
|
|
92
|
+
"hookui.recompileFailed": "Pembangunan ulang gagal.",
|
|
93
|
+
"hookui.compileNote": "ORC membangun ulang tata letak setelah setiap perubahan, jadi Anda tidak perlu. Tombol ini untuk setelah Anda memperbarui ORC.",
|
|
94
|
+
"hookui.clone": "Salin",
|
|
95
|
+
"hookui.cloned": "{id} disalin",
|
|
96
|
+
"hookui.expand": "Pecah",
|
|
97
|
+
"hookui.expanded": "{id} dipecah jadi bagiannya",
|
|
98
|
+
"hookui.minWidth": "Lebar minimal",
|
|
99
|
+
"hookui.minWidthChange": "lebar minimal {id} = {v}",
|
|
100
|
+
"hookui.precision": "Desimal",
|
|
101
|
+
"hookui.precisionChange": "desimal {id} = {v}",
|
|
102
|
+
"hookui.width": "Sel",
|
|
103
|
+
"hookui.widthChange": "sel {id} = {v}",
|
|
104
|
+
"hookui.widthWhy": "Lebih banyak sel adalah satu-satunya cara membesar di terminal.",
|
|
105
|
+
"hookui.before": "Sebelum",
|
|
106
|
+
"hookui.beforeChange": "sebelum {id} = \"{v}\"",
|
|
107
|
+
"hookui.after": "Sesudah",
|
|
108
|
+
"hookui.afterChange": "sesudah {id} = \"{v}\"",
|
|
109
|
+
"hookui.narrow": "Di terminal sempit",
|
|
110
|
+
"hookui.minCols": "Tampil di atas",
|
|
111
|
+
"hookui.minColsChange": "{id} tampil di atas {v} kolom",
|
|
112
|
+
"hookui.priority": "Urutan dilepas",
|
|
113
|
+
"hookui.priorityChange": "urutan dilepas {id} = {v}",
|
|
114
|
+
"hookui.priorityWhy": "Saat baris tidak muat, 1 dilepas dulu dan 5 terakhir. Anda yang memilih apa yang bertahan, bukan tepi kanan.",
|
|
115
|
+
"hookui.boardStatus": "Baris status",
|
|
116
|
+
"hookui.boardSubagent": "Satu baris per agen",
|
|
117
|
+
"hookui.boardStatusSub": "Dua baris di bawah terminal Anda.",
|
|
118
|
+
"hookui.boardSubagentSub": "Baris yang digambar Claude Code untuk tiap subagen di panel agen. Satu baris, dan menunjukkan agen itu berjalan di model apa dan sudah memakai berapa."
|
|
119
|
+
}
|
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
{
|
|
2
|
-
"nav.overview": "Ringkasan",
|
|
3
|
-
"nav.settings": "Pengaturan",
|
|
4
|
-
"nav.lanes": "Lane",
|
|
5
|
-
"nav.runs": "Run",
|
|
6
|
-
"nav.knowledge": "Pengetahuan",
|
|
7
|
-
"nav.stats": "Statistik",
|
|
8
|
-
"nav.flow": "Flow",
|
|
9
|
-
"nav.crosslink": "Crosslink",
|
|
10
|
-
"nav.learn": "Belajar",
|
|
11
|
-
"nav.mockrun": "Contoh Penggunaan Skill",
|
|
12
|
-
"nav.experiment": "Eksperimen",
|
|
13
|
-
"nav.maintenance": "Pemeliharaan",
|
|
14
|
-
"nav.pact": "Janji",
|
|
15
|
-
"nav.boundary": "Batas",
|
|
16
|
-
"nav.handoff": "Ubah Sendiri",
|
|
17
|
-
"nav.challenge": "Challenge",
|
|
18
|
-
"nav.extra": "Extra",
|
|
19
|
-
"nav.wait": "Tunggu",
|
|
20
|
-
"nav.docs": "Dokumen"
|
|
21
|
-
|
|
1
|
+
{
|
|
2
|
+
"nav.overview": "Ringkasan",
|
|
3
|
+
"nav.settings": "Pengaturan",
|
|
4
|
+
"nav.lanes": "Lane",
|
|
5
|
+
"nav.runs": "Run",
|
|
6
|
+
"nav.knowledge": "Pengetahuan",
|
|
7
|
+
"nav.stats": "Statistik",
|
|
8
|
+
"nav.flow": "Flow",
|
|
9
|
+
"nav.crosslink": "Crosslink",
|
|
10
|
+
"nav.learn": "Belajar",
|
|
11
|
+
"nav.mockrun": "Contoh Penggunaan Skill",
|
|
12
|
+
"nav.experiment": "Eksperimen",
|
|
13
|
+
"nav.maintenance": "Pemeliharaan",
|
|
14
|
+
"nav.pact": "Janji",
|
|
15
|
+
"nav.boundary": "Batas",
|
|
16
|
+
"nav.handoff": "Ubah Sendiri",
|
|
17
|
+
"nav.challenge": "Challenge",
|
|
18
|
+
"nav.extra": "Extra",
|
|
19
|
+
"nav.wait": "Tunggu",
|
|
20
|
+
"nav.docs": "Dokumen",
|
|
21
|
+
"nav.hookui": "Antarmuka CLI Hook"
|
|
22
|
+
}
|
|
@@ -88,5 +88,8 @@
|
|
|
88
88
|
"overview.extraOrphan.chip": "{n} pengiriman",
|
|
89
89
|
"overview.extraOrphan.chipPlural": "{n} pengiriman",
|
|
90
90
|
"overview.extraOrphan.slug": "tidak pernah melapor kembali",
|
|
91
|
-
"overview.extraOrphan.where": "Extra · Pemulihan"
|
|
91
|
+
"overview.extraOrphan.where": "Extra · Pemulihan",
|
|
92
|
+
"overview.item.statuslineOrphaned.cta": "Buka Antarmuka CLI Hook",
|
|
93
|
+
"overview.item.statuslineInvalid.cta": "Buka Antarmuka CLI Hook",
|
|
94
|
+
"overview.item.statuslineUnreadable.cta": "Buka Antarmuka CLI Hook"
|
|
92
95
|
}
|
|
@@ -29,5 +29,7 @@
|
|
|
29
29
|
"tour.13.title": "Dokumen panjang, dalam satu gambar",
|
|
30
30
|
"tour.13.text": "Setiap blok adalah satu bagian, seukuran panjangnya dan diberi warna sesuai keadaannya. Tidak ada yang memuat dokumen itu sendiri di sini - ORC bekerja dari peta ini, dan itulah sebabnya dokumen panjang tidak menghabiskan sesi.",
|
|
31
31
|
"tour.14.title": "Pekerjaan yang berjalan di tempat lain",
|
|
32
|
-
"tour.14.text": "Satu garis dari nol sampai seratus: seberapa berat skor sebuah tugas. Setiap bagiannya menunjukkan di mana pekerjaan itu benar-benar berjalan — Claude berwarna biru, penyedia yang Anda hubungkan berwarna hijau. Rentang yang belum Anda hubungkan tetap punya tempatnya, jadi \"saya sengaja meninggalkan pekerjaan berat di Claude\" tidak pernah terlihat seperti \"tidak ada pekerjaan berat\". Tidak ada yang keluar dari mesin ini sampai Anda menghubungkan sesuatu, mengujinya, dan menekan Apply."
|
|
32
|
+
"tour.14.text": "Satu garis dari nol sampai seratus: seberapa berat skor sebuah tugas. Setiap bagiannya menunjukkan di mana pekerjaan itu benar-benar berjalan — Claude berwarna biru, penyedia yang Anda hubungkan berwarna hijau. Rentang yang belum Anda hubungkan tetap punya tempatnya, jadi \"saya sengaja meninggalkan pekerjaan berat di Claude\" tidak pernah terlihat seperti \"tidak ada pekerjaan berat\". Tidak ada yang keluar dari mesin ini sampai Anda menghubungkan sesuatu, mengujinya, dan menekan Apply.",
|
|
33
|
+
"tour.15.title": "Bangun baris status Anda sendiri",
|
|
34
|
+
"tour.15.text": "Tiga baris, tiap baris memuat sampai lima bagian. Baris 1 harus terisi sebelum baris 2 bisa. Pratinjau di atas persis seperti yang akan tampil di terminal Anda - digambar oleh kode yang sama dengan yang dijalankan bilah itu."
|
|
33
35
|
}
|
package/bin/webui/js/01-i18n.js
CHANGED
|
@@ -1,151 +1,152 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/* 01-i18n.js — orc ui client
|
|
3
|
-
LANGS, loadLang, setLang, t, applyStaticText, cycleLang.
|
|
4
|
-
|
|
5
|
-
Keys are written out IN FULL, never assembled from a fragment. The v0.48.1
|
|
6
|
-
file split is by key PREFIX only; `docs.ship.confirm` is still spelled
|
|
7
|
-
`docs.ship.confirm` at every call site.
|
|
8
|
-
|
|
9
|
-
Loaded by app.html in the order its numeric prefix names. Classic
|
|
10
|
-
script, no import/export: an ES module import carries no query string,
|
|
11
|
-
and every static request here needs the per-launch session token. */
|
|
12
|
-
|
|
13
|
-
/* ------------------------------------------------------------------- i18n -- */
|
|
14
|
-
/*
|
|
15
|
-
THE SCOPE RULE, and it is deliberately narrow: only the panel's OWN prose is
|
|
16
|
-
translated. Everything that arrives from `bin/cli.js --json` stays exactly as
|
|
17
|
-
the CLI wrote it — config keys and their descriptions, values, agent names,
|
|
18
|
-
model ids, file paths, commands, `orc doctor` messages, command output. Those
|
|
19
|
-
are identifiers, not sentences: a translated config key is a key that does not
|
|
20
|
-
exist, and a translated command is a command you cannot type. So `t()` is
|
|
21
|
-
never applied to a value read out of a payload, and neither string table
|
|
22
|
-
contains a single ORC key name.
|
|
23
|
-
|
|
24
|
-
The tables are plain JSON served as static assets. Adding a language is a
|
|
25
|
-
FOLDER in i18n/ plus one row in LANGS — no build step, no library, no
|
|
26
|
-
dependency, and no server change (serve.js walks i18n/ at boot).
|
|
27
|
-
*/
|
|
28
|
-
const LANGS = [
|
|
29
|
-
{ code: "en", label: "English" },
|
|
30
|
-
{ code: "id", label: "Bahasa Indonesia" },
|
|
31
|
-
];
|
|
32
|
-
const LANG_KEY = "orc-ui-lang";
|
|
33
|
-
|
|
34
|
-
// v0.48.1 — one file per namespace instead of one 53 KB table, so changing the
|
|
35
|
-
// Docs panel's wording means opening i18n/en/docs.json rather than paging
|
|
36
|
-
// through every string in the panel.
|
|
37
|
-
//
|
|
38
|
-
// A plain array, and deliberately NOT an index file to fetch: an index would be
|
|
39
|
-
// one more round trip and one more thing to forget to update. A namespace owns
|
|
40
|
-
// one or more key PREFIXES (`stats` owns `stats.*` and `cost.*`), which the
|
|
41
|
-
// test suite asserts — a key whose prefix belongs to no namespace would load
|
|
42
|
-
// nowhere and render as a raw dotted string.
|
|
43
|
-
const NAMESPACES = [
|
|
44
|
-
"common",
|
|
45
|
-
"nav",
|
|
46
|
-
"banner",
|
|
47
|
-
"overview",
|
|
48
|
-
"settings",
|
|
49
|
-
"lanes",
|
|
50
|
-
"runs",
|
|
51
|
-
"knowledge",
|
|
52
|
-
"stats",
|
|
53
|
-
"flow",
|
|
54
|
-
"crosslink",
|
|
55
|
-
"learn",
|
|
56
|
-
"mockrun",
|
|
57
|
-
"maintenance",
|
|
58
|
-
"pact",
|
|
59
|
-
"boundary",
|
|
60
|
-
"wait",
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
let
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
//
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (s === undefined) s = key;
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
//
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
/* 01-i18n.js — orc ui client
|
|
3
|
+
LANGS, loadLang, setLang, t, applyStaticText, cycleLang.
|
|
4
|
+
|
|
5
|
+
Keys are written out IN FULL, never assembled from a fragment. The v0.48.1
|
|
6
|
+
file split is by key PREFIX only; `docs.ship.confirm` is still spelled
|
|
7
|
+
`docs.ship.confirm` at every call site.
|
|
8
|
+
|
|
9
|
+
Loaded by app.html in the order its numeric prefix names. Classic
|
|
10
|
+
script, no import/export: an ES module import carries no query string,
|
|
11
|
+
and every static request here needs the per-launch session token. */
|
|
12
|
+
|
|
13
|
+
/* ------------------------------------------------------------------- i18n -- */
|
|
14
|
+
/*
|
|
15
|
+
THE SCOPE RULE, and it is deliberately narrow: only the panel's OWN prose is
|
|
16
|
+
translated. Everything that arrives from `bin/cli.js --json` stays exactly as
|
|
17
|
+
the CLI wrote it — config keys and their descriptions, values, agent names,
|
|
18
|
+
model ids, file paths, commands, `orc doctor` messages, command output. Those
|
|
19
|
+
are identifiers, not sentences: a translated config key is a key that does not
|
|
20
|
+
exist, and a translated command is a command you cannot type. So `t()` is
|
|
21
|
+
never applied to a value read out of a payload, and neither string table
|
|
22
|
+
contains a single ORC key name.
|
|
23
|
+
|
|
24
|
+
The tables are plain JSON served as static assets. Adding a language is a
|
|
25
|
+
FOLDER in i18n/ plus one row in LANGS — no build step, no library, no
|
|
26
|
+
dependency, and no server change (serve.js walks i18n/ at boot).
|
|
27
|
+
*/
|
|
28
|
+
const LANGS = [
|
|
29
|
+
{ code: "en", label: "English" },
|
|
30
|
+
{ code: "id", label: "Bahasa Indonesia" },
|
|
31
|
+
];
|
|
32
|
+
const LANG_KEY = "orc-ui-lang";
|
|
33
|
+
|
|
34
|
+
// v0.48.1 — one file per namespace instead of one 53 KB table, so changing the
|
|
35
|
+
// Docs panel's wording means opening i18n/en/docs.json rather than paging
|
|
36
|
+
// through every string in the panel.
|
|
37
|
+
//
|
|
38
|
+
// A plain array, and deliberately NOT an index file to fetch: an index would be
|
|
39
|
+
// one more round trip and one more thing to forget to update. A namespace owns
|
|
40
|
+
// one or more key PREFIXES (`stats` owns `stats.*` and `cost.*`), which the
|
|
41
|
+
// test suite asserts — a key whose prefix belongs to no namespace would load
|
|
42
|
+
// nowhere and render as a raw dotted string.
|
|
43
|
+
const NAMESPACES = [
|
|
44
|
+
"common",
|
|
45
|
+
"nav",
|
|
46
|
+
"banner",
|
|
47
|
+
"overview",
|
|
48
|
+
"settings",
|
|
49
|
+
"lanes",
|
|
50
|
+
"runs",
|
|
51
|
+
"knowledge",
|
|
52
|
+
"stats",
|
|
53
|
+
"flow",
|
|
54
|
+
"crosslink",
|
|
55
|
+
"learn",
|
|
56
|
+
"mockrun",
|
|
57
|
+
"maintenance",
|
|
58
|
+
"pact",
|
|
59
|
+
"boundary",
|
|
60
|
+
"wait",
|
|
61
|
+
"hookui",
|
|
62
|
+
"handoff",
|
|
63
|
+
"challenge",
|
|
64
|
+
"docs",
|
|
65
|
+
"extra",
|
|
66
|
+
"experiment",
|
|
67
|
+
"tour",
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
let lang = "en";
|
|
71
|
+
// English is loaded first and kept as the FALLBACK table: a key the other
|
|
72
|
+
// language has not translated yet renders in English rather than as a raw
|
|
73
|
+
// dotted key. A half-finished translation degrades to mixed prose, never to
|
|
74
|
+
// debug output on somebody's screen.
|
|
75
|
+
let DICT = {};
|
|
76
|
+
let DICT_EN = {};
|
|
77
|
+
|
|
78
|
+
// The namespaces are fetched in parallel and merged into ONE flat table, so
|
|
79
|
+
// `t()` is unchanged and no call site knows the split happened.
|
|
80
|
+
//
|
|
81
|
+
// A namespace that fails to load falls back to ENGLISH for its keys rather than
|
|
82
|
+
// rejecting the whole language: a network hiccup on one file should cost that
|
|
83
|
+
// file's prose, not the entire panel. English itself has no fallback below it,
|
|
84
|
+
// so a failure there rejects and boot() lands on raw keys — ugly, never blank.
|
|
85
|
+
function loadLang(code) {
|
|
86
|
+
return Promise.all(
|
|
87
|
+
NAMESPACES.map((ns) =>
|
|
88
|
+
fetch(`/i18n/${code}/${ns}.json`, { headers: { "x-orc-token": TOKEN }, cache: "no-store" })
|
|
89
|
+
.then((r) => {
|
|
90
|
+
if (!r.ok) throw new Error(`i18n ${code}/${ns} ${r.status}`);
|
|
91
|
+
return r.json();
|
|
92
|
+
})
|
|
93
|
+
.catch((e) => {
|
|
94
|
+
if (code === "en") throw e;
|
|
95
|
+
return null;
|
|
96
|
+
})
|
|
97
|
+
)
|
|
98
|
+
).then((parts) => Object.assign({}, ...parts.filter(Boolean)));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// A missing or broken table must never blank the UI. Every failure path here
|
|
102
|
+
// lands on English, so the worst case is untranslated text — never empty text.
|
|
103
|
+
async function setLang(code, opts) {
|
|
104
|
+
const rerender = !opts || opts.rerender !== false;
|
|
105
|
+
const want = LANGS.some((l) => l.code === code) ? code : "en";
|
|
106
|
+
try {
|
|
107
|
+
DICT = want === "en" ? DICT_EN : await loadLang(want);
|
|
108
|
+
lang = want;
|
|
109
|
+
} catch (_) {
|
|
110
|
+
DICT = DICT_EN;
|
|
111
|
+
lang = "en";
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
localStorage.setItem(LANG_KEY, lang);
|
|
115
|
+
} catch (_) {}
|
|
116
|
+
document.documentElement.setAttribute("lang", lang);
|
|
117
|
+
applyStaticText();
|
|
118
|
+
if (rerender) route();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function t(key, vars) {
|
|
122
|
+
let s = DICT[key];
|
|
123
|
+
if (s === undefined) s = DICT_EN[key];
|
|
124
|
+
if (s === undefined) s = key;
|
|
125
|
+
if (vars) for (const k of Object.keys(vars)) s = s.split("{" + k + "}").join(String(vars[k]));
|
|
126
|
+
return s;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// English has one plural form and Indonesian has none, so count-aware lookup is
|
|
130
|
+
// a second key rather than a plural-rules engine: `n === 1` picks the singular.
|
|
131
|
+
const tn = (n, key, vars) => t(n === 1 ? key : key + "Plural", Object.assign({ n }, vars || {}));
|
|
132
|
+
|
|
133
|
+
// The parts of the shell that are markup rather than a render function. Called
|
|
134
|
+
// on every language change; panels re-render themselves through `route()`.
|
|
135
|
+
function applyStaticText() {
|
|
136
|
+
for (const node of document.querySelectorAll("[data-i18n]")) node.textContent = t(node.dataset.i18n);
|
|
137
|
+
const tt = $("#theme-toggle");
|
|
138
|
+
if (tt) tt.textContent = document.documentElement.getAttribute("data-theme") === "light" ? t("rail.dark") : t("rail.light");
|
|
139
|
+
const hint = $("#shortcut-hint");
|
|
140
|
+
if (hint) hint.title = t("rail.shortcuts");
|
|
141
|
+
const label = $("#lang-label");
|
|
142
|
+
if (label) label.textContent = (LANGS.find((l) => l.code === lang) || LANGS[0]).label;
|
|
143
|
+
const btn = $("#lang-toggle");
|
|
144
|
+
if (btn) btn.title = t("lang.title");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Two languages, so the control is a TOGGLE rather than a menu: one click, no
|
|
148
|
+
// dropdown to open. A third language would make this a <select>; two do not.
|
|
149
|
+
function cycleLang() {
|
|
150
|
+
const i = LANGS.findIndex((l) => l.code === lang);
|
|
151
|
+
setLang(LANGS[(i + 1) % LANGS.length].code);
|
|
152
|
+
}
|
package/bin/webui/js/90-tour.js
CHANGED
|
@@ -419,6 +419,12 @@ const TOUR_STEPS = [
|
|
|
419
419
|
unconditionally; the header strip sits outside the tabs entirely and is the
|
|
420
420
|
fallback. Same rule a fourth time: point at something with a SIZE. */
|
|
421
421
|
{ panel: "extra", selector: ".ex-boundary, .ex-strip", title: "tour.14.title", text: "tour.14.text" },
|
|
422
|
+
/* v1.3.0 — the board. Same rule a fifth time: it must point at something with
|
|
423
|
+
a SIZE. `.hk-zone` is the one element on this panel that renders in EVERY
|
|
424
|
+
state, including the first run — an empty line 1 still draws its zone,
|
|
425
|
+
because a drop target that only appears once something is in it is a drop
|
|
426
|
+
target nobody finds. The gate card is the fallback. */
|
|
427
|
+
{ panel: "hookui", selector: ".hk-zone, .card", title: "tour.15.title", text: "tour.15.text" },
|
|
422
428
|
];
|
|
423
429
|
|
|
424
430
|
function startFirstRunTour(root) {
|