Skip to content

Commit 93158d0

Browse files
committed
Merge remote-tracking branch 'origin/master' into llm-gpt-5
2 parents 1805264 + 893d345 commit 93158d0

37 files changed

+718
-98
lines changed

src/.claude/settings.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,17 @@
1717
"WebFetch(domain:cocalc.com)",
1818
"WebFetch(domain:doc.cocalc.com)",
1919
"WebFetch(domain:docs.anthropic.com)",
20-
"WebFetch(domain:github.com)"
20+
"WebFetch(domain:github.com)",
21+
"Bash(git checkout:*)",
22+
"Bash(git push:*)",
23+
"Bash(NODE_OPTIONS=--max-old-space-size=8192 ../node_modules/.bin/tsc --noEmit)",
24+
"Bash(docker run:*)",
25+
"Bash(../node_modules/.bin/tsc:*)",
26+
"Bash(npm view:*)",
27+
"WebFetch(domain:www.anthropic.com)",
28+
"WebFetch(domain:mistral.ai)",
29+
"Bash(pnpm i18n:*)",
30+
"WebFetch(domain:simplelocalize.io)"
2131
],
2232
"deny": []
2333
}

src/CLAUDE.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ This file provides guidance to Claude Code (claude.ai/code) and also Gemini CLI
2020
- Some older code is JavaScript or CoffeeScript, which will be translated to TypeScript
2121
- Use ES modules (import/export) syntax, not CommonJS (require)
2222
- Organize the list of imports in such a way: installed npm packages are on top, newline, then are imports from @cocalc's code base. Sorted alphabetically.
23+
- **Backend Logging**: Use `getLogger` from `@cocalc/project/logger` for logging in backend code. Do NOT use `console.log`. Example: `const L = getLogger("module:name").debug;`
2324

2425
## Development Commands
2526

@@ -88,6 +89,13 @@ CoCalc is organized as a monorepo with key packages:
8889
- **Event Emitters**: Inter-service communication within backend
8990
- **REST-like APIs**: Some HTTP endpoints for specific operations
9091
- **API Schema**: API endpoints in `packages/next/pages/api/v2/` use Zod schemas in `packages/next/lib/api/schema/` for validation. These schemas must be kept in harmony with the TypeScript types sent from frontend applications using `apiPost` (in `packages/next/lib/api/post.ts`) or `api` (in `packages/frontend/client/api.ts`). When adding new fields to API requests, both the frontend types and the API schema validation must be updated.
92+
- **Conat Frontend → Hub Communication**: CoCalc uses a custom distributed messaging system called "Conat" for frontend-to-hub communication:
93+
1. **Frontend ConatClient** (`packages/frontend/conat/client.ts`): Manages WebSocket connection to hub, handles authentication, reconnection, and provides API interfaces
94+
2. **Core Protocol** (`packages/conat/core/client.ts`): NATS-like pub/sub/request/response messaging with automatic chunking, multiple encoding formats (MsgPack, JSON), and delivery confirmation
95+
3. **Hub API Structure** (`packages/conat/hub/api/`): Typed interfaces for different services (system, projects, db, purchases, jupyter) that map function calls to conat subjects
96+
4. **Message Flow**: Frontend calls like `hub.projects.setQuotas()` → ConatClient.callHub() → conat request to subject `hub.account.{account_id}.api` → Hub API dispatcher → actual service implementation
97+
5. **Authentication**: Each conat request includes account_id and is subject to permission checks at the hub level
98+
6. **Subjects**: Messages are routed using hierarchical subjects like `hub.account.{uuid}.{service}` or `project.{uuid}.{compute_server_id}.{service}`
9199

92100
### Key Technologies
93101

@@ -149,6 +157,40 @@ CoCalc is organized as a monorepo with key packages:
149157
- REFUSE to modify files when the git repository is on the `master` or `main` branch.
150158
- NEVER proactively create documentation files (`*.md`) or README files. Only create documentation files if explicitly requested by the User.
151159

160+
## React-intl / Internationalization (i18n)
161+
162+
CoCalc uses react-intl for internationalization with SimpleLocalize as the translation platform.
163+
164+
### Translation ID Naming Convention
165+
166+
Translation IDs follow a hierarchical pattern: `[directory].[subdir].[filename].[aspect].[label|title|tooltip|...]`
167+
168+
Examples:
169+
- `labels.masked_files` - for common UI labels
170+
- `account.sign-out.button.title` - for account sign-out dialog
171+
- `command.generic.force_build.label` - for command labels
172+
173+
### Translation Workflow
174+
175+
**For new translation keys:**
176+
1. Add the translation to source code (e.g., `packages/frontend/i18n/common.ts`)
177+
2. Run `pnpm i18n:extract` - updates `extracted.json` from source code
178+
3. Run `pnpm i18n:upload` - sends new strings to SimpleLocalize
179+
4. New keys are automatically translated to all languages
180+
5. Run `pnpm i18n:download` - fetches translations
181+
6. Run `pnpm i18n:compile` - compiles translation files
182+
183+
**For editing existing translation keys:**
184+
Same flow as above, but **before 3. i18n:upload**, delete the key. Only new keys are auto-translated. `pnpm i18n:delete [id]`.
185+
186+
### Translation File Structure
187+
188+
- `packages/frontend/i18n/README.md` - more information
189+
- `packages/frontend/i18n/common.ts` - shared translation definitions
190+
- `packages/frontend/i18n/extracted.json` - auto-generated, do not edit manually
191+
- `packages/frontend/i18n/[locale].json` - downloaded translations per language
192+
- `packages/frontend/i18n/[locale].compiled.json` - compiled for runtime use
193+
152194
# Ignore
153195

154196
- Ignore files covered by `.gitignore`

src/packages/comm/project-status/utils.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,28 @@ import type {
88
DiskUsageInfo,
99
} from "@cocalc/util/types/project-info/types";
1010

11-
// DiskUsage for /tmp !
11+
/**
12+
* Calculate resource usage statistics from cgroup data.
13+
*
14+
* This function processes cgroup resource information and calculates usage percentages
15+
* for both memory and CPU. It works with both cgroup v1 and v2 data structures,
16+
* handling the unified CGroup interface that abstracts the differences between versions.
17+
*/
1218
export function cgroup_stats(cg: CGroup, du?: DiskUsageInfo) {
13-
// why? /tmp is a memory disk in kucalc
19+
// DiskUsage for /tmp – add to memory usage since it's already been
20+
// calculated appropriately by the backend based on whether /tmp is tmpfs
1421
const mem_rss = cg.mem_stat.total_rss + (du?.usage ?? 0);
1522
const mem_tot = cg.mem_stat.hierarchical_memory_limit;
16-
const mem_pct = 100 * Math.min(1, mem_rss / mem_tot);
17-
const cpu_pct = 100 * Math.min(1, cg.cpu_usage_rate / cg.cpu_cores_limit);
23+
24+
// Handle unlimited (-1) and zero memory limits to avoid division by zero
25+
const mem_pct = mem_tot <= 0 ? 0 : 100 * Math.min(1, mem_rss / mem_tot);
26+
27+
// Handle unlimited (-1) and zero CPU limits to avoid division by zero
28+
const cpu_pct =
29+
cg.cpu_cores_limit <= 0
30+
? 0
31+
: 100 * Math.min(1, cg.cpu_usage_rate / cg.cpu_cores_limit);
32+
1833
const cpu_tot = cg.cpu_usage; // seconds
1934
return { mem_rss, mem_tot, mem_pct, cpu_pct, cpu_tot };
2035
}

src/packages/frontend/cspell.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@
4242
"tolerations",
4343
"undelete",
4444
"undeleting",
45-
"revealjs"
45+
"revealjs",
46+
"conat",
47+
"SocketIO"
4648
],
4749
"ignoreWords": [
4850
"antd",
@@ -67,7 +69,8 @@
6769
"xsmall",
6870
"flyouts",
6971
"buttonbar",
70-
"noconf"
72+
"noconf",
73+
"flyoutdragbar"
7174
],
7275
"flagWords": [],
7376
"ignorePaths": ["node_modules/**", "dist/**", "dist-ts/**", "build/**"],

src/packages/frontend/file-associations.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,13 @@ file_associations["csv"] = {
230230
opts: {},
231231
};
232232

233+
file_associations["json"] = {
234+
editor: "codemirror",
235+
icon: "js-square",
236+
opts: { mode: "javascript", indent_unit: 2, tab_size: 2 },
237+
name: "JSON",
238+
};
239+
233240
// At https://cs.lmu.edu/~ray/notes/gasexamples/ they use .s, so I'm also including that.
234241
// In fact, GCC only works on files if they end in .s.
235242
file_associations["asm"] = file_associations["s"] = {
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/bin/bash
2+
# Delete specific translation keys from SimpleLocalize
3+
# Usage: ./delete.sh key1 [key2 key3 ...]
4+
5+
if [ $# -eq 0 ]; then
6+
echo "Usage: $0 key1 [key2 key3 ...]"
7+
echo "Delete one or more translation keys from SimpleLocalize"
8+
echo ""
9+
echo "Example:"
10+
echo " $0 labels.masked_files"
11+
echo " $0 labels.masked_files account.sign-out.button.title"
12+
exit 1
13+
fi
14+
15+
# Check if SIMPLELOCALIZE_KEY is set
16+
if [ -z "${SIMPLELOCALIZE_KEY}" ]; then
17+
echo "Error: SIMPLELOCALIZE_KEY is not set or is empty. Please provide a valid API key." >&2
18+
exit 1
19+
fi
20+
21+
echo "Deleting translation keys from SimpleLocalize..."
22+
23+
# Loop through all provided keys
24+
for key in "$@"; do
25+
echo
26+
echo "Deleting '$key':"
27+
curl \
28+
--location \
29+
--request DELETE "https://api.simplelocalize.io/api/v1/translation-keys?key=$key" \
30+
--header "X-SimpleLocalize-Token: $SIMPLELOCALIZE_KEY"
31+
done
32+
33+
echo
34+
echo
35+
echo "Done! Now you should run:"
36+
echo " pnpm i18n:upload (to re-upload the key with new content)"
37+
echo " pnpm i18n:download (to fetch updated translations)"
38+
echo " pnpm i18n:compile (to compile translation files)"

src/packages/frontend/i18n/bin/upload.sh

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,27 @@ curl -s -X 'POST' 'https://api.simplelocalize.io/api/v2/jobs/auto-translate' \
1919
-H "X-SimpleLocalize-Token: $SIMPLELOCALIZE_KEY" \
2020
-H 'Content-Type: application/json' \
2121
-d '{"options": []}' | jq '.data|length'
22+
23+
echo "Waiting for auto-translation jobs to complete..."
24+
25+
# Wait for all auto-translation jobs to complete
26+
while true; do
27+
sleep 3
28+
29+
# Get all active jobs
30+
jobs_response=$(curl -s -X 'GET' 'https://api.simplelocalize.io/api/v1/jobs' \
31+
-H 'accept: application/json' \
32+
-H "X-SimpleLocalize-Token: $SIMPLELOCALIZE_KEY")
33+
34+
# Count jobs that are not yet completed (state != "SUCCESS")
35+
total_jobs=$(echo "$jobs_response" | jq '.data | length')
36+
success_jobs=$(echo "$jobs_response" | jq '[.data[] | select(.state == "SUCCESS")] | length')
37+
active_jobs=$((total_jobs - success_jobs))
38+
39+
if [ "$active_jobs" -eq 0 ]; then
40+
echo "✓ All auto-translation jobs completed!"
41+
break
42+
else
43+
echo " $active_jobs job(s) still running..."
44+
fi
45+
done

src/packages/frontend/i18n/common.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ export const labels = defineMessages({
489489
masked_files: {
490490
id: "labels.masked_files",
491491
defaultMessage:
492-
"{masked, select, true {Hide masked files} other {Show masked files}}. Masked files are autogenerated or temporary files, which are not meant to be edited. They are be grayed out.",
492+
"{masked, select, true {Hide masked files} other {Show masked files}}. Masked files are autogenerated or temporary files, which are not meant to be edited. They are grayed out.",
493493
description: "show/hide masked files in a file-explorer in a UI.",
494494
},
495495
folder: {

src/packages/frontend/i18n/trans/ar_EG.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -946,7 +946,7 @@
946946
"labels.linux_terminal": "لينكس تيرمنال",
947947
"labels.loading": "جار التحميل...",
948948
"labels.log": "تسجيل",
949-
"labels.masked_files": "{masked, select, true {إخفاء الملفات المخفية} other {إظهار الملفات المخفية}}. الملفات المخفية هي ملفات تم إنشاؤها تلقائيًا أو ملفات مؤقتة، ولا يُقصد تحريرها. ستكون باللون الرمادي.",
949+
"labels.masked_files": "{masked, select, true {إخفاء الملفات المقنعة} other {إظهار الملفات المقنعة}}. الملفات المقنعة هي ملفات تم إنشاؤها تلقائيًا أو ملفات مؤقتة، والتي لا يُقصد تعديلها. يتم تظليلها باللون الرمادي.",
950950
"labels.message.plural": "{num, plural, one {رسالة} other {رسائل}}",
951951
"labels.messages": "رسائل",
952952
"labels.messages.all_messages": "جميع الرسائل",
@@ -1222,7 +1222,7 @@
12221222
"project.no-internet-modal.add-license": "إضافة الترخيص",
12231223
"project.no-internet-modal.info": "<strong>تم تعطيل الوصول إلى الإنترنت لهذا المشروع.</strong> هذا التقييد يمنع تثبيت حزم بايثون (pip، conda) أو حزم R، واستخدام Git لاستنساخ المستودعات، وتنزيل مجموعات البيانات أو الوصول إلى واجهات برمجة التطبيقات. قد تتعطل الأوامر التي تحاول الوصول إلى الإنترنت أو تفشل في الاكتمال، حيث يتم حظر الاتصالات الشبكية لمنع سوء الاستخدام. <A>تعرف على المزيد</A>.",
12241224
"project.no-internet-modal.message": "لحل هذه المشكلة، <A1>تحتاج إلى تطبيق</A1> <A2>ترخيص صالح</A2> يوفر ترقيات أو <A3>شراء ترخيص</A3>.",
1225-
"project.no-internet-modal.title": "لا يوجد اتصال بالإنترنت",
1225+
"project.no-internet-modal.title": "المشروع \"{name}\" ليس لديه وصول إلى الإنترنت",
12261226
"project.open_file.what": "افتح الملف \"{path}\"",
12271227
"project.page.activity-bar-layout.title": "تخطيط شريط النشاط",
12281228
"project.page.activity-bar.explanation": "هذه الميزة تعدل وظيفة شريط الأزرار في الجانب الأيسر من المشروع. بشكل افتراضي، تعرض الأزرار صفحات كاملة وعلامات صغيرة للوحات المنبثقة. عند اختيار خيار \"الصفحات الكاملة\"، يتم عرض الأزرار فقط، وتفتح الصفحات الكاملة عند النقر. وعلى العكس، عند اختيار وضع \"اللوحات المنبثقة\"، تتوسع اللوحات المنبثقة فقط عند النقر. في كلتا الحالتين الأخيرتين، يمكن عرض نوع اللوحة البديل بالنقر مع الضغط على مفتاح Shift على الزر المقابل.",

src/packages/frontend/i18n/trans/de_DE.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -946,7 +946,7 @@
946946
"labels.linux_terminal": "Linux-Terminal",
947947
"labels.loading": "Laden...",
948948
"labels.log": "Protokoll",
949-
"labels.masked_files": "{masked, select, true {Maskierte Dateien verbergen} other {Maskierte Dateien anzeigen}}. Maskierte Dateien sind automatisch generierte oder temporäre Dateien, die nicht bearbeitet werden sollen. Sie werden ausgegraut.",
949+
"labels.masked_files": "{masked, select, true {Maskierte Dateien ausblenden} other {Maskierte Dateien anzeigen}}. Maskierte Dateien sind automatisch generierte oder temporäre Dateien, die nicht bearbeitet werden sollen. Sie sind ausgegraut.",
950950
"labels.message.plural": "{num, plural, one {Nachricht} other {Nachrichten}}",
951951
"labels.messages": "Nachrichten",
952952
"labels.messages.all_messages": "Alle Nachrichten",
@@ -1222,7 +1222,7 @@
12221222
"project.no-internet-modal.add-license": "Lizenz hinzufügen",
12231223
"project.no-internet-modal.info": "<strong>Der Internetzugang ist für dieses Projekt deaktiviert.</strong> Diese Einschränkung verhindert die Installation von Python-Paketen (pip, conda) oder R-Paketen, die Verwendung von Git zum Klonen von Repositories, das Herunterladen von Datensätzen oder den Zugriff auf APIs. Befehle, die versuchen, auf das Internet zuzugreifen, können hängen bleiben oder nicht abgeschlossen werden, da Netzwerkverbindungen blockiert sind, um Missbrauch zu verhindern. <A>Mehr erfahren</A>.",
12241224
"project.no-internet-modal.message": "Um dies zu beheben, müssen Sie eine <A2>gültige Lizenz</A2> anwenden, die Upgrades bereitstellt oder eine <A3>Lizenz kaufen</A3>.",
1225-
"project.no-internet-modal.title": "Kein Internetzugang",
1225+
"project.no-internet-modal.title": "Projekt \"{name}\" hat keinen Internetzugang",
12261226
"project.open_file.what": "öffne die Datei \"{path}\"",
12271227
"project.page.activity-bar-layout.title": "Layout der Aktivitätsleiste",
12281228
"project.page.activity-bar.explanation": "Diese Funktion ändert die Funktionalität der linken Seitenleiste des Projekts. Standardmäßig werden Schaltflächen für ganze Seiten und kleine Caret-Zeichen für Flyout-Panels angezeigt. Bei Auswahl der Option \"ganze Seiten\" werden nur Schaltflächen angezeigt, die beim Klicken ganze Seiten öffnen. Umgekehrt, wenn die Option \"Flyout-Panels\" gewählt wird, erweitern sich nur Flyout-Panels beim Klicken. In beiden letzteren Fällen kann der alternative Paneltyp durch Shift-Klicken auf die entsprechende Schaltfläche angezeigt werden.",

0 commit comments

Comments
 (0)