diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 58439813..00000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1 +0,0 @@ -name: CI diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 216cbe71..170f1c21 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -112,6 +112,7 @@ jobs: SIG="$(ls "$BUNDLE"/macos/*.app.tar.gz.sig)" # Pull tauri's generated latest.json from the private release. + # threatcrush-disable-next-line secret-generic-credential GH_TOKEN="$GITHUB_TOKEN" gh release download "$TAG" \ --repo "$GITHUB_REPOSITORY" --pattern latest.json --dir . --clobber @@ -127,9 +128,14 @@ jobs: ' # Create the public release (once) and upload all four assets. + # GH_TOKEN is assigned from an env var, not a literal — the three + # lines below are scanner false positives, not embedded tokens. + # threatcrush-disable-next-line secret-generic-credential if ! GH_TOKEN="$RELEASES_TOKEN" gh release view "$TAG" --repo "$PUBLIC_REPO" >/dev/null 2>&1; then + # threatcrush-disable-next-line secret-generic-credential GH_TOKEN="$RELEASES_TOKEN" gh release create "$TAG" --repo "$PUBLIC_REPO" \ --title "MyDevTools $TAG" --notes "MyDevTools desktop $TAG" fi + # threatcrush-disable-next-line secret-generic-credential GH_TOKEN="$RELEASES_TOKEN" gh release upload "$TAG" --repo "$PUBLIC_REPO" \ --clobber "$DMG" "$TARGZ" "$SIG" latest.json diff --git a/.threatcrushignore b/.threatcrushignore new file mode 100644 index 00000000..06458bee --- /dev/null +++ b/.threatcrushignore @@ -0,0 +1,37 @@ +# Paths ThreatCrush should not scan. Everything listed here is sample data, +# UI copy, or generated output — never a credential this repo actually holds. +# Real code stays scanned: for a one-off false positive in source, put a +# `threatcrush-disable-next-line ` comment above the line instead of +# excluding the whole file. + +# Locale catalogues (27 locales x 2 apps). These are the UI strings for the +# SSH-key, JWT, S3 and database tools: example PEM markers, sample connection +# strings and placeholder keys shown to the user. 41 findings, all copy. +apps/*/messages + +# Blog posts: long-form tutorial prose with illustrative credentials in code +# fences ("mongodb://admin:secret@localhost", a demo master key). +apps/*/src/lib/blog/posts.ts + +# Audit lab scripts: fake tokens injected into a throwaway browser session to +# measure egress. Nothing here authenticates against anything. +scripts/audit + +# Generated / local-only trees. Not in a CI checkout, noisy in a local run. +.claude/worktrees +.open-next + +# Test suites: fixture credentials ("hunter2000", a PEM header over the string +# "abc") that exist to exercise parsers and redaction. Keep real secrets out of +# fixtures — nothing scans them any more. +**/__tests__ + +# Upstream false positive: `manifest-typosquat` reads the `color` dependency +# (30M downloads/week, imported in 6 files here) as a one-edit lookalike of +# `colors`, because ThreatCrush's POPULAR_NPM list has `colors` but not +# `color`. The rule has no per-rule suppression and JSON takes no comments, so +# the whole manifest goes. Cost: this file loses the install/postinstall +# lifecycle-script check too — it has no such scripts today, and every other +# package.json in the repo is still scanned. Drop this line once `color` is +# allowlisted upstream. +apps/desktop-ui/package.json \ No newline at end of file diff --git a/apps/desktop-ui/messages/af.json b/apps/desktop-ui/messages/af.json index 1223018e..aa448f90 100644 --- a/apps/desktop-ui/messages/af.json +++ b/apps/desktop-ui/messages/af.json @@ -709,6 +709,7 @@ "all": "Alles", "total": "Totaal", "allProjects": "Alle projekte", + "projectsHeading": "Projekte", "export": "Uitvoer", "kanban": "Kanban", "list": "Lys", @@ -966,6 +967,16 @@ "emptyHintMobile": "Tik die +-knoppie om jou eerste wagwoord by te voeg", "emptyHintDesktop": "Jy het nog geen wagwoorde gestoor nie. Klik die \"Voeg wagwoord by\"-knoppie hierbo om te begin.", "addPassword": "Voeg wagwoord by", + "title": "Wagwoorde", + "filters": { + "heading": "Kluisgesondheid", + "all": "Alle wagwoorde", + "weak": "Swak", + "reused": "Hergebruik", + "noUrl": "Geen URL", + "tagsHeading": "Etikette", + "allTags": "Alle etikette" + }, "searchPlaceholderMobile": "Soek in jou kluis...", "searchPlaceholderDesktop": "Soek kluis...", "importExport": "Invoer / uitvoer", @@ -1161,6 +1172,8 @@ "emptyHint": "Skep 'n stel vir elke projek en omgewing (bv. produksie API-sleutels). Alles word in jou blaaier geënkripteer voor oplaai.", "searchPlaceholder": "Soek projek, omgewing, etikette, notas…", "tagFilterPlaceholder": "Filter volgens etiket", + "allProjects": "Alle projekte", + "projectsHeading": "Projekte", "variableCount": "{count, plural, one {# veranderlike} other {# veranderlikes}}", "updatedAgo": "Bygewerk {time}", "copyDotEnv": "Kopieer .env", @@ -5106,6 +5119,10 @@ "ToolPageHeader": { "offline": "100% aflyn" }, + "ToolSidebar": { + "show": "Wys sybalk", + "hide": "Versteek sybalk" + }, "DesktopUpdate": { "title": "MyDevTools word bygewerk", "downloading": "Opdatering word afgelaai…", diff --git a/apps/desktop-ui/messages/ar.json b/apps/desktop-ui/messages/ar.json index 38d21da9..3721af91 100644 --- a/apps/desktop-ui/messages/ar.json +++ b/apps/desktop-ui/messages/ar.json @@ -709,6 +709,7 @@ "all": "الكل", "total": "الإجمالي", "allProjects": "كل المشاريع", + "projectsHeading": "المشاريع", "export": "تصدير", "kanban": "كانبان", "list": "قائمة", @@ -966,6 +967,16 @@ "emptyHintMobile": "اضغط زر + لإضافة أول كلمة مرور", "emptyHintDesktop": "لم تخزّن كلمات مرور بعد. انقر \"إضافة كلمة مرور\" أعلاه للبدء.", "addPassword": "إضافة كلمة مرور", + "title": "كلمات المرور", + "filters": { + "heading": "سلامة الخزنة", + "all": "كل كلمات المرور", + "weak": "ضعيفة", + "reused": "مُعاد استخدامها", + "noUrl": "بدون رابط", + "tagsHeading": "الوسوم", + "allTags": "كل الوسوم" + }, "searchPlaceholderMobile": "ابحث في خزنتك...", "searchPlaceholderDesktop": "ابحث في الخزنة...", "importExport": "استيراد / تصدير", @@ -1161,6 +1172,8 @@ "emptyHint": "أنشئ مجموعة لكل مشروع وبيئة (مثل مفاتيح واجهة الإنتاج). يُشفّر كل شيء في متصفحك قبل الرفع.", "searchPlaceholder": "ابحث في المشروع والبيئة والوسوم والملاحظات…", "tagFilterPlaceholder": "تصفية حسب الوسم", + "allProjects": "كل المشاريع", + "projectsHeading": "المشاريع", "variableCount": "{count, plural, one {# متغير} other {# متغيرات}}", "updatedAgo": "عُدّل {time}", "copyDotEnv": "نسخ .env", @@ -5095,6 +5108,10 @@ "ToolPageHeader": { "offline": "100% دون اتصال" }, + "ToolSidebar": { + "show": "إظهار الشريط الجانبي", + "hide": "إخفاء الشريط الجانبي" + }, "DesktopUpdate": { "title": "جارٍ تحديث MyDevTools", "downloading": "جارٍ تنزيل التحديث…", diff --git a/apps/desktop-ui/messages/ca.json b/apps/desktop-ui/messages/ca.json index 44bedc74..31509047 100644 --- a/apps/desktop-ui/messages/ca.json +++ b/apps/desktop-ui/messages/ca.json @@ -709,6 +709,7 @@ "all": "Totes", "total": "Total", "allProjects": "Tots els projectes", + "projectsHeading": "Projectes", "export": "Exporta", "kanban": "Kanban", "list": "Llista", @@ -966,6 +967,16 @@ "emptyHintMobile": "Toca el botó + per afegir la primera contrasenya", "emptyHintDesktop": "Encara no has desat cap contrasenya. Fes clic a «Afegeix contrasenya» a dalt per començar.", "addPassword": "Afegeix contrasenya", + "title": "Contrasenyes", + "filters": { + "heading": "Salut de la caixa forta", + "all": "Totes les contrasenyes", + "weak": "Febles", + "reused": "Reutilitzades", + "noUrl": "Sense URL", + "tagsHeading": "Etiquetes", + "allTags": "Totes les etiquetes" + }, "searchPlaceholderMobile": "Cerca al coffre...", "searchPlaceholderDesktop": "Cerca al coffre...", "importExport": "Importa / exporta", @@ -1161,6 +1172,8 @@ "emptyHint": "Crea un conjunt per a cada projecte i entorn (p. ex. claus d'API de producció). Tot es xifra al navegador abans de pujar-lo.", "searchPlaceholder": "Cerca projecte, entorn, etiquetes, notes…", "tagFilterPlaceholder": "Filtra per etiqueta", + "allProjects": "Tots els projectes", + "projectsHeading": "Projectes", "variableCount": "{count, plural, one {# variable} other {# variables}}", "updatedAgo": "Actualitzat {time}", "copyDotEnv": "Copia .env", @@ -5095,6 +5108,10 @@ "ToolPageHeader": { "offline": "100% fora de línia" }, + "ToolSidebar": { + "show": "Mostra la barra lateral", + "hide": "Amaga la barra lateral" + }, "DesktopUpdate": { "title": "S'està actualitzant MyDevTools", "downloading": "Baixant l'actualització…", diff --git a/apps/desktop-ui/messages/cs.json b/apps/desktop-ui/messages/cs.json index 28dc5e22..3794e777 100644 --- a/apps/desktop-ui/messages/cs.json +++ b/apps/desktop-ui/messages/cs.json @@ -709,6 +709,7 @@ "all": "Vše", "total": "Celkem", "allProjects": "Všechny projekty", + "projectsHeading": "Projekty", "export": "Export", "kanban": "Kanban", "list": "Seznam", @@ -966,6 +967,16 @@ "emptyHintMobile": "Klepněte na + a přidejte první heslo", "emptyHintDesktop": "Zatím nemáte uložená žádná hesla. Začněte tlačítkem „Přidat heslo“ výše.", "addPassword": "Přidat heslo", + "title": "Hesla", + "filters": { + "heading": "Stav trezoru", + "all": "Všechna hesla", + "weak": "Slabá", + "reused": "Opakovaná", + "noUrl": "Bez URL", + "tagsHeading": "Štítky", + "allTags": "Všechny štítky" + }, "searchPlaceholderMobile": "Hledat v trezoru...", "searchPlaceholderDesktop": "Hledat v trezoru...", "importExport": "Import / export", @@ -1161,6 +1172,8 @@ "emptyHint": "Vytvořte sadu pro každý projekt a prostředí (např. produkční API klíče). Vše se před nahráním šifruje v prohlížeči.", "searchPlaceholder": "Hledat projekt, prostředí, značky, poznámky…", "tagFilterPlaceholder": "Filtrovat podle značky", + "allProjects": "Všechny projekty", + "projectsHeading": "Projekty", "variableCount": "{count, plural, one {# proměnná} few {# proměnné} other {# proměnných}}", "updatedAgo": "Aktualizováno {time}", "copyDotEnv": "Kopírovat .env", @@ -5095,6 +5108,10 @@ "ToolPageHeader": { "offline": "100% offline" }, + "ToolSidebar": { + "show": "Zobrazit postranní panel", + "hide": "Skrýt postranní panel" + }, "DesktopUpdate": { "title": "Aktualizace MyDevTools", "downloading": "Stahování aktualizace…", diff --git a/apps/desktop-ui/messages/da.json b/apps/desktop-ui/messages/da.json index 5e24bb60..cce90a07 100644 --- a/apps/desktop-ui/messages/da.json +++ b/apps/desktop-ui/messages/da.json @@ -709,6 +709,7 @@ "all": "Alle", "total": "Total", "allProjects": "Alle projekter", + "projectsHeading": "Projekter", "export": "Eksportere", "kanban": "Kanban", "list": "Liste", @@ -966,6 +967,16 @@ "emptyHintMobile": "Tryk på knappen + for at tilføje din første adgangskode", "emptyHintDesktop": "Du har ikke gemt nogen adgangskoder endnu. Klik på knappen \"Tilføj adgangskode\" ovenfor for at komme i gang.", "addPassword": "Tilføj adgangskode", + "title": "Adgangskoder", + "filters": { + "heading": "Boksens tilstand", + "all": "Alle adgangskoder", + "weak": "Svage", + "reused": "Genbrugte", + "noUrl": "Ingen URL", + "tagsHeading": "Tags", + "allTags": "Alle tags" + }, "searchPlaceholderMobile": "Søg i din boks...", "searchPlaceholderDesktop": "Søg i boksen...", "importExport": "Import/eksport", @@ -1161,6 +1172,8 @@ "emptyHint": "Opret et sæt for hvert projekt og miljø (f.eks. produktions-API-nøgler). Alt krypteres i din browser før upload.", "searchPlaceholder": "Søg projekt, miljø, tags, noter…", "tagFilterPlaceholder": "Filtrér efter tag", + "allProjects": "Alle projekter", + "projectsHeading": "Projekter", "variableCount": "{count, plural, one {# variabel} other {# variabler}}", "updatedAgo": "Opdateret {time}", "copyDotEnv": "Kopiér .env", @@ -5095,6 +5108,10 @@ "ToolPageHeader": { "offline": "100 % offline" }, + "ToolSidebar": { + "show": "Vis sidepanel", + "hide": "Skjul sidepanel" + }, "DesktopUpdate": { "title": "Opdaterer MyDevTools", "downloading": "Downloader opdatering…", diff --git a/apps/desktop-ui/messages/de.json b/apps/desktop-ui/messages/de.json index b36a0801..2f5636be 100644 --- a/apps/desktop-ui/messages/de.json +++ b/apps/desktop-ui/messages/de.json @@ -709,6 +709,7 @@ "all": "Alle", "total": "Gesamt", "allProjects": "Alle Projekte", + "projectsHeading": "Projekte", "export": "Exportieren", "kanban": "Kanban", "list": "Liste", @@ -966,6 +967,16 @@ "emptyHintMobile": "Tippen Sie auf +, um Ihr erstes Passwort hinzuzufügen", "emptyHintDesktop": "Noch keine Passwörter. Klicken Sie oben auf „Passwort hinzufügen“.", "addPassword": "Passwort hinzufügen", + "title": "Passwörter", + "filters": { + "heading": "Tresor-Status", + "all": "Alle Passwörter", + "weak": "Schwach", + "reused": "Mehrfach genutzt", + "noUrl": "Ohne URL", + "tagsHeading": "Tags", + "allTags": "Alle Tags" + }, "searchPlaceholderMobile": "Tresor durchsuchen...", "searchPlaceholderDesktop": "Tresor durchsuchen...", "importExport": "Import / Export", @@ -1161,6 +1172,8 @@ "emptyHint": "Legen Sie ein Set pro Projekt und Umgebung an (z. B. Produktions-API-Schlüssel). Alles wird vor dem Upload im Browser verschlüsselt.", "searchPlaceholder": "Projekt, Umgebung, Tags, Notizen durchsuchen…", "tagFilterPlaceholder": "Nach Tag filtern", + "allProjects": "Alle Projekte", + "projectsHeading": "Projekte", "variableCount": "{count, plural, one {# Variable} other {# Variablen}}", "updatedAgo": "Aktualisiert {time}", "copyDotEnv": ".env kopieren", @@ -5095,6 +5108,10 @@ "ToolPageHeader": { "offline": "100 % offline" }, + "ToolSidebar": { + "show": "Seitenleiste einblenden", + "hide": "Seitenleiste ausblenden" + }, "DesktopUpdate": { "title": "MyDevTools wird aktualisiert", "downloading": "Update wird heruntergeladen…", diff --git a/apps/desktop-ui/messages/el.json b/apps/desktop-ui/messages/el.json index dfbb9a66..980da36a 100644 --- a/apps/desktop-ui/messages/el.json +++ b/apps/desktop-ui/messages/el.json @@ -709,6 +709,7 @@ "all": "Ολοι", "total": "Σύνολο", "allProjects": "Όλα τα Έργα", + "projectsHeading": "Έργα", "export": "Εξαγωγή", "kanban": "Kanban", "list": "Λίστα", @@ -966,6 +967,16 @@ "emptyHintMobile": "Πατήστε το κουμπί + για να προσθέσετε τον πρώτο σας κωδικό πρόσβασης", "emptyHintDesktop": "Δεν έχετε αποθηκεύσει ακόμα κανέναν κωδικό πρόσβασης. Κάντε κλικ στο κουμπί \"Προσθήκη κωδικού πρόσβασης\" παραπάνω για να ξεκινήσετε.", "addPassword": "Προσθήκη κωδικού πρόσβασης", + "title": "Κωδικοί", + "filters": { + "heading": "Υγεία θησαυροφυλακίου", + "all": "Όλοι οι κωδικοί", + "weak": "Αδύναμοι", + "reused": "Επαναχρησιμοποιημένοι", + "noUrl": "Χωρίς URL", + "tagsHeading": "Ετικέτες", + "allTags": "Όλες οι ετικέτες" + }, "searchPlaceholderMobile": "Αναζητήστε το θησαυροφυλάκιό σας...", "searchPlaceholderDesktop": "Θησαυροφυλάκιο αναζήτησης...", "importExport": "Εισαγωγή / εξαγωγή", @@ -1161,6 +1172,8 @@ "emptyHint": "Δημιουργήστε ένα σύνολο για κάθε έργο και περιβάλλον (π.χ. κλειδιά API παραγωγής). Όλα κρυπτογραφούνται στο πρόγραμμα περιήγησης πριν την αποστολή.", "searchPlaceholder": "Αναζήτηση έργου, περιβάλλοντος, ετικετών, σημειώσεων…", "tagFilterPlaceholder": "Φιλτράρισμα κατά ετικέτα", + "allProjects": "Όλα τα έργα", + "projectsHeading": "Έργα", "variableCount": "{count, plural, one {# μεταβλητή} other {# μεταβλητές}}", "updatedAgo": "Ενημερώθηκε {time}", "copyDotEnv": "Αντιγραφή .env", @@ -5095,6 +5108,10 @@ "ToolPageHeader": { "offline": "100% εκτός σύνδεσης" }, + "ToolSidebar": { + "show": "Εμφάνιση πλαϊνής μπάρας", + "hide": "Απόκρυψη πλαϊνής μπάρας" + }, "DesktopUpdate": { "title": "Ενημέρωση MyDevTools", "downloading": "Λήψη ενημέρωσης…", diff --git a/apps/desktop-ui/messages/en.json b/apps/desktop-ui/messages/en.json index ee10a0be..19f82f1e 100644 --- a/apps/desktop-ui/messages/en.json +++ b/apps/desktop-ui/messages/en.json @@ -709,6 +709,7 @@ "all": "All", "total": "Total", "allProjects": "All Projects", + "projectsHeading": "Projects", "export": "Export", "kanban": "Kanban", "list": "List", @@ -966,6 +967,16 @@ "emptyHintMobile": "Tap the + button to add your first password", "emptyHintDesktop": "You have not stored any passwords yet. Click the \"Add password\" button above to get started.", "addPassword": "Add password", + "title": "Passwords", + "filters": { + "heading": "Vault health", + "all": "All passwords", + "weak": "Weak", + "reused": "Reused", + "noUrl": "No URL", + "tagsHeading": "Tags", + "allTags": "All tags" + }, "searchPlaceholderMobile": "Search your vault...", "searchPlaceholderDesktop": "Search vault...", "importExport": "Import / export", @@ -1161,6 +1172,8 @@ "emptyHint": "Create a set for each project and environment (e.g. production API keys). Everything is encrypted in your browser before upload.", "searchPlaceholder": "Search project, environment, tags, notes...", "tagFilterPlaceholder": "Filter by tag", + "allProjects": "All projects", + "projectsHeading": "Projects", "variableCount": "{count, plural, one {# variable} other {# variables}}", "updatedAgo": "Updated {time}", "copyDotEnv": "Copy .env", @@ -5116,6 +5129,10 @@ "ToolPageHeader": { "offline": "100% offline" }, + "ToolSidebar": { + "show": "Show sidebar", + "hide": "Hide sidebar" + }, "DesktopUpdate": { "title": "Updating MyDevTools", "downloading": "Downloading update…", diff --git a/apps/desktop-ui/messages/es.json b/apps/desktop-ui/messages/es.json index 247e3ca5..decea828 100644 --- a/apps/desktop-ui/messages/es.json +++ b/apps/desktop-ui/messages/es.json @@ -709,6 +709,7 @@ "all": "Todas", "total": "Total", "allProjects": "Todos los proyectos", + "projectsHeading": "Proyectos", "export": "Exportar", "kanban": "Kanban", "list": "Lista", @@ -966,6 +967,16 @@ "emptyHintMobile": "Toca el botón + para añadir tu primera contraseña", "emptyHintDesktop": "Aún no has guardado contraseñas. Haz clic en \"Añadir contraseña\" arriba para empezar.", "addPassword": "Añadir contraseña", + "title": "Contraseñas", + "filters": { + "heading": "Estado de la bóveda", + "all": "Todas las contraseñas", + "weak": "Débiles", + "reused": "Reutilizadas", + "noUrl": "Sin URL", + "tagsHeading": "Etiquetas", + "allTags": "Todas las etiquetas" + }, "searchPlaceholderMobile": "Buscar en tu bóveda...", "searchPlaceholderDesktop": "Buscar en la bóveda...", "importExport": "Importar / exportar", @@ -1161,6 +1172,8 @@ "emptyHint": "Crea un conjunto por proyecto y entorno (p. ej. claves de API de producción). Todo se cifra en tu navegador antes de subirlo.", "searchPlaceholder": "Buscar proyecto, entorno, etiquetas, notas…", "tagFilterPlaceholder": "Filtrar por etiqueta", + "allProjects": "Todos los proyectos", + "projectsHeading": "Proyectos", "variableCount": "{count, plural, one {# variable} other {# variables}}", "updatedAgo": "Actualizado {time}", "copyDotEnv": "Copiar .env", @@ -5106,6 +5119,10 @@ "ToolPageHeader": { "offline": "100% sin conexión" }, + "ToolSidebar": { + "show": "Mostrar barra lateral", + "hide": "Ocultar barra lateral" + }, "DesktopUpdate": { "title": "Actualizando MyDevTools", "downloading": "Descargando actualización…", diff --git a/apps/desktop-ui/messages/fa.json b/apps/desktop-ui/messages/fa.json index fd960552..d2cdc59a 100644 --- a/apps/desktop-ui/messages/fa.json +++ b/apps/desktop-ui/messages/fa.json @@ -709,6 +709,7 @@ "all": "همه", "total": "مجموع", "allProjects": "همه پروژه ها", + "projectsHeading": "پروژه‌ها", "export": "صادرات", "kanban": "کانبان", "list": "فهرست کنید", @@ -966,6 +967,16 @@ "emptyHintMobile": "روی دکمه + ضربه بزنید تا اولین رمز عبور خود را اضافه کنید", "emptyHintDesktop": "شما هنوز هیچ رمز عبوری را ذخیره نکرده اید. برای شروع روی دکمه «افزودن رمز عبور» در بالا کلیک کنید.", "addPassword": "اضافه کردن رمز عبور", + "title": "گذرواژه‌ها", + "filters": { + "heading": "سلامت گاوصندوق", + "all": "همه گذرواژه‌ها", + "weak": "ضعیف", + "reused": "تکراری", + "noUrl": "بدون نشانی", + "tagsHeading": "برچسب‌ها", + "allTags": "همه برچسب‌ها" + }, "searchPlaceholderMobile": "خزانه خود را جستجو کنید ...", "searchPlaceholderDesktop": "صندوق جستجو...", "importExport": "واردات / صادرات", @@ -1161,6 +1172,8 @@ "emptyHint": "برای هر پروژه و محیط یک مجموعه بسازید (مثلاً کلیدهای API تولید). همه قبل از آپلود در مرورگر شما رمزگذاری می‌شوند.", "searchPlaceholder": "جستجوی پروژه، محیط، برچسب، یادداشت…", "tagFilterPlaceholder": "فیلتر با برچسب", + "allProjects": "همه پروژه‌ها", + "projectsHeading": "پروژه‌ها", "variableCount": "{count, plural, one {# متغیر} other {# متغیر}}", "updatedAgo": "به‌روزرسانی {time}", "copyDotEnv": "کپی .env", @@ -5106,6 +5119,10 @@ "ToolPageHeader": { "offline": "۱۰۰٪ آفلاین" }, + "ToolSidebar": { + "show": "نمایش نوار کناری", + "hide": "پنهان کردن نوار کناری" + }, "DesktopUpdate": { "title": "در حال به‌روزرسانی MyDevTools", "downloading": "در حال دانلود به‌روزرسانی…", diff --git a/apps/desktop-ui/messages/fr.json b/apps/desktop-ui/messages/fr.json index dd2b7081..39f13772 100644 --- a/apps/desktop-ui/messages/fr.json +++ b/apps/desktop-ui/messages/fr.json @@ -709,6 +709,7 @@ "all": "Toutes", "total": "Total", "allProjects": "Tous les projets", + "projectsHeading": "Projets", "export": "Exporter", "kanban": "Kanban", "list": "Liste", @@ -966,6 +967,16 @@ "emptyHintMobile": "Appuyez sur + pour ajouter votre premier mot de passe", "emptyHintDesktop": "Aucun mot de passe enregistré. Cliquez sur « Ajouter un mot de passe » ci-dessus.", "addPassword": "Ajouter un mot de passe", + "title": "Mots de passe", + "filters": { + "heading": "État du coffre", + "all": "Tous les mots de passe", + "weak": "Faibles", + "reused": "Réutilisés", + "noUrl": "Sans URL", + "tagsHeading": "Étiquettes", + "allTags": "Toutes les étiquettes" + }, "searchPlaceholderMobile": "Rechercher dans le coffre…", "searchPlaceholderDesktop": "Rechercher…", "importExport": "Importer / exporter", @@ -1161,6 +1172,8 @@ "emptyHint": "Créez un jeu pour chaque projet et environnement (ex. clés API de production). Tout est chiffré dans votre navigateur avant l'envoi.", "searchPlaceholder": "Rechercher projet, environnement, étiquettes, notes…", "tagFilterPlaceholder": "Filtrer par étiquette", + "allProjects": "Tous les projets", + "projectsHeading": "Projets", "variableCount": "{count, plural, one {# variable} other {# variables}}", "updatedAgo": "Mis à jour {time}", "copyDotEnv": "Copier .env", @@ -5106,6 +5119,10 @@ "ToolPageHeader": { "offline": "100 % hors ligne" }, + "ToolSidebar": { + "show": "Afficher la barre latérale", + "hide": "Masquer la barre latérale" + }, "DesktopUpdate": { "title": "Mise à jour de MyDevTools", "downloading": "Téléchargement de la mise à jour…", diff --git a/apps/desktop-ui/messages/id.json b/apps/desktop-ui/messages/id.json index f04b3d09..4bc081a7 100644 --- a/apps/desktop-ui/messages/id.json +++ b/apps/desktop-ui/messages/id.json @@ -709,6 +709,7 @@ "all": "Semua", "total": "Total", "allProjects": "Semua Projek", + "projectsHeading": "Proyek", "export": "Ekspor", "kanban": "Kanban", "list": "Dunia", @@ -966,6 +967,16 @@ "emptyHintMobile": "Ketuk tombol + untuk menambahkan kata sandi pertama Anda", "emptyHintDesktop": "Anda belum menyimpan kata sandi apa pun. Klik tombol \"Tambahkan kata sandi\" di atas untuk memulai.", "addPassword": "Tambah Kata Sandi", + "title": "Kata sandi", + "filters": { + "heading": "Kesehatan brankas", + "all": "Semua kata sandi", + "weak": "Lemah", + "reused": "Dipakai ulang", + "noUrl": "Tanpa URL", + "tagsHeading": "Tag", + "allTags": "Semua tag" + }, "searchPlaceholderMobile": "Cari brankas Anda...", "searchPlaceholderDesktop": "Cari brankas...", "importExport": "Impor/Ekspor", @@ -1161,6 +1172,8 @@ "emptyHint": "Buat set untuk setiap proyek dan lingkungan (mis. kunci API produksi). Semua dienkripsi di browser sebelum diunggah.", "searchPlaceholder": "Cari proyek, lingkungan, tag, catatan…", "tagFilterPlaceholder": "Saring menurut tag", + "allProjects": "Semua proyek", + "projectsHeading": "Proyek", "variableCount": "{count, plural, other {# variabel}}", "updatedAgo": "Diperbarui {time}", "copyDotEnv": "Salin .env", @@ -5095,6 +5108,10 @@ "ToolPageHeader": { "offline": "100% offline" }, + "ToolSidebar": { + "show": "Tampilkan bilah sisi", + "hide": "Sembunyikan bilah sisi" + }, "DesktopUpdate": { "title": "Memperbarui MyDevTools", "downloading": "Mengunduh pembaruan…", diff --git a/apps/desktop-ui/messages/it.json b/apps/desktop-ui/messages/it.json index a5d4201b..7b28eb2d 100644 --- a/apps/desktop-ui/messages/it.json +++ b/apps/desktop-ui/messages/it.json @@ -709,6 +709,7 @@ "all": "Tutto", "total": "Totale", "allProjects": "Tutti i progetti", + "projectsHeading": "Progetti", "export": "Esportare", "kanban": "Kanban", "list": "Lista", @@ -966,6 +967,16 @@ "emptyHintMobile": "Tocca il pulsante + per aggiungere la tua prima password", "emptyHintDesktop": "Non hai ancora memorizzato alcuna password. Fai clic sul pulsante \"Aggiungi password\" qui sopra per iniziare.", "addPassword": "Aggiungi password", + "title": "Password", + "filters": { + "heading": "Stato della cassaforte", + "all": "Tutte le password", + "weak": "Deboli", + "reused": "Riutilizzate", + "noUrl": "Senza URL", + "tagsHeading": "Tag", + "allTags": "Tutti i tag" + }, "searchPlaceholderMobile": "Cerca nel tuo caveau...", "searchPlaceholderDesktop": "Cerca nel caveau...", "importExport": "Importa/esporta", @@ -1161,6 +1172,8 @@ "emptyHint": "Crea un set per ogni progetto e ambiente (es. chiavi API di produzione). Tutto viene crittografato nel browser prima del caricamento.", "searchPlaceholder": "Cerca progetto, ambiente, tag, note…", "tagFilterPlaceholder": "Filtra per tag", + "allProjects": "Tutti i progetti", + "projectsHeading": "Progetti", "variableCount": "{count, plural, one {# variabile} other {# variabili}}", "updatedAgo": "Aggiornato {time}", "copyDotEnv": "Copia .env", @@ -5106,6 +5119,10 @@ "ToolPageHeader": { "offline": "100% offline" }, + "ToolSidebar": { + "show": "Mostra barra laterale", + "hide": "Nascondi barra laterale" + }, "DesktopUpdate": { "title": "Aggiornamento di MyDevTools", "downloading": "Download dell'aggiornamento…", diff --git a/apps/desktop-ui/messages/ja.json b/apps/desktop-ui/messages/ja.json index 049c0a75..67b09e7d 100644 --- a/apps/desktop-ui/messages/ja.json +++ b/apps/desktop-ui/messages/ja.json @@ -709,6 +709,7 @@ "all": "すべて", "total": "合計", "allProjects": "すべてのプロジェクト", + "projectsHeading": "プロジェクト", "export": "エクスポート", "kanban": "カンバン", "list": "リスト", @@ -966,6 +967,16 @@ "emptyHintMobile": "+ボタンをタップして最初のパスワードを追加してください", "emptyHintDesktop": "パスワードはまだ保存されていません。上の「パスワードを追加」ボタンをクリックして始めましょう。", "addPassword": "パスワードを追加", + "title": "パスワード", + "filters": { + "heading": "保管庫の状態", + "all": "すべてのパスワード", + "weak": "脆弱", + "reused": "使い回し", + "noUrl": "URLなし", + "tagsHeading": "タグ", + "allTags": "すべてのタグ" + }, "searchPlaceholderMobile": "ボールトを検索...", "searchPlaceholderDesktop": "ボールトを検索...", "importExport": "インポート / エクスポート", @@ -1161,6 +1172,8 @@ "emptyHint": "プロジェクトと環境ごとにセットを作成します(例: 本番 API キー)。アップロード前にブラウザで暗号化されます。", "searchPlaceholder": "プロジェクト・環境・タグ・メモを検索…", "tagFilterPlaceholder": "タグで絞り込み", + "allProjects": "すべてのプロジェクト", + "projectsHeading": "プロジェクト", "variableCount": "{count, plural, other {# 件の変数}}", "updatedAgo": "更新 {time}", "copyDotEnv": ".env をコピー", @@ -5095,6 +5108,10 @@ "ToolPageHeader": { "offline": "完全オフライン" }, + "ToolSidebar": { + "show": "サイドバーを表示", + "hide": "サイドバーを非表示" + }, "DesktopUpdate": { "title": "MyDevTools を更新しています", "downloading": "更新をダウンロードしています…", diff --git a/apps/desktop-ui/messages/ko.json b/apps/desktop-ui/messages/ko.json index 57c45243..55eebce7 100644 --- a/apps/desktop-ui/messages/ko.json +++ b/apps/desktop-ui/messages/ko.json @@ -709,6 +709,7 @@ "all": "전체", "total": "합계", "allProjects": "모든 프로젝트", + "projectsHeading": "프로젝트", "export": "내보내기", "kanban": "칸반", "list": "목록", @@ -966,6 +967,16 @@ "emptyHintMobile": "+ 버튼을 눌러 첫 번째 비밀번호를 추가하세요", "emptyHintDesktop": "아직 저장된 비밀번호가 없습니다. 위의 \"비밀번호 추가\" 버튼을 클릭하여 시작하세요.", "addPassword": "비밀번호 추가", + "title": "비밀번호", + "filters": { + "heading": "보관함 상태", + "all": "모든 비밀번호", + "weak": "취약", + "reused": "재사용", + "noUrl": "URL 없음", + "tagsHeading": "태그", + "allTags": "모든 태그" + }, "searchPlaceholderMobile": "볼트 검색...", "searchPlaceholderDesktop": "볼트 검색...", "importExport": "가져오기 / 내보내기", @@ -1161,6 +1172,8 @@ "emptyHint": "프로젝트와 환경마다 세트를 만드세요(예: 프로덕션 API 키). 업로드 전 브라우저에서 암호화됩니다.", "searchPlaceholder": "프로젝트, 환경, 태그, 메모 검색…", "tagFilterPlaceholder": "태그로 필터", + "allProjects": "모든 프로젝트", + "projectsHeading": "프로젝트", "variableCount": "{count, plural, other {#개 변수}}", "updatedAgo": "업데이트 {time}", "copyDotEnv": ".env 복사", @@ -5095,6 +5108,10 @@ "ToolPageHeader": { "offline": "100% 오프라인" }, + "ToolSidebar": { + "show": "사이드바 표시", + "hide": "사이드바 숨기기" + }, "DesktopUpdate": { "title": "MyDevTools 업데이트 중", "downloading": "업데이트 다운로드 중…", diff --git a/apps/desktop-ui/messages/ms.json b/apps/desktop-ui/messages/ms.json index 098a4975..64617f74 100644 --- a/apps/desktop-ui/messages/ms.json +++ b/apps/desktop-ui/messages/ms.json @@ -709,6 +709,7 @@ "all": "Semua", "total": "Jumlah", "allProjects": "Semua Projek", + "projectsHeading": "Projek", "export": "Eksport", "kanban": "Kanban", "list": "Senarai", @@ -966,6 +967,16 @@ "emptyHintMobile": "Ketik butang + untuk menambah kata laluan pertama anda", "emptyHintDesktop": "Anda belum menyimpan sebarang kata laluan. Klik butang \"Tambah kata laluan\" di atas untuk bermula.", "addPassword": "Tambah kata laluan", + "title": "Kata laluan", + "filters": { + "heading": "Kesihatan peti besi", + "all": "Semua kata laluan", + "weak": "Lemah", + "reused": "Diguna semula", + "noUrl": "Tiada URL", + "tagsHeading": "Tag", + "allTags": "Semua tag" + }, "searchPlaceholderMobile": "Cari peti besi anda...", "searchPlaceholderDesktop": "Cari peti besi...", "importExport": "Import / eksport", @@ -1161,6 +1172,8 @@ "emptyHint": "Cipta set untuk setiap projek dan persekitaran (cth. kunci API pengeluaran). Semua disulitkan dalam pelayar sebelum dimuat naik.", "searchPlaceholder": "Cari projek, persekitaran, teg, nota…", "tagFilterPlaceholder": "Tapis mengikut teg", + "allProjects": "Semua projek", + "projectsHeading": "Projek", "variableCount": "{count, plural, other {# pemboleh ubah}}", "updatedAgo": "Dikemas kini {time}", "copyDotEnv": "Salin .env", @@ -5106,6 +5119,10 @@ "ToolPageHeader": { "offline": "100% luar talian" }, + "ToolSidebar": { + "show": "Tunjukkan bar sisi", + "hide": "Sembunyikan bar sisi" + }, "DesktopUpdate": { "title": "Mengemas kini MyDevTools", "downloading": "Memuat turun kemas kini…", diff --git a/apps/desktop-ui/messages/nb.json b/apps/desktop-ui/messages/nb.json index ea489d08..1f6ae854 100644 --- a/apps/desktop-ui/messages/nb.json +++ b/apps/desktop-ui/messages/nb.json @@ -709,6 +709,7 @@ "all": "Alle", "total": "Totalt", "allProjects": "Alle prosjekter", + "projectsHeading": "Prosjekter", "export": "Eksporter", "kanban": "Kanban", "list": "Liste", @@ -966,6 +967,16 @@ "emptyHintMobile": "Trykk på +-knappen for å legge til ditt første passord", "emptyHintDesktop": "Du har ikke lagret noen passord ennå. Klikk på «Legg til passord»-knappen ovenfor for å komme i gang.", "addPassword": "Legg til passord", + "title": "Passord", + "filters": { + "heading": "Hvelvets tilstand", + "all": "Alle passord", + "weak": "Svake", + "reused": "Gjenbrukte", + "noUrl": "Ingen URL", + "tagsHeading": "Merkelapper", + "allTags": "Alle merkelapper" + }, "searchPlaceholderMobile": "Søk i hvelvet ditt...", "searchPlaceholderDesktop": "Søk i hvelv...", "importExport": "Importer / eksporter", @@ -1161,6 +1172,8 @@ "emptyHint": "Opprett et sett for hvert prosjekt og miljø (f.eks. produksjons-API-nøkler). Alt krypteres i nettleseren før opplasting.", "searchPlaceholder": "Søk prosjekt, miljø, tagger, notater…", "tagFilterPlaceholder": "Filtrer på tagg", + "allProjects": "Alle prosjekter", + "projectsHeading": "Prosjekter", "variableCount": "{count, plural, one {# variabel} other {# variabler}}", "updatedAgo": "Oppdatert {time}", "copyDotEnv": "Kopier .env", @@ -5095,6 +5108,10 @@ "ToolPageHeader": { "offline": "100 % offline" }, + "ToolSidebar": { + "show": "Vis sidepanel", + "hide": "Skjul sidepanel" + }, "DesktopUpdate": { "title": "Oppdaterer MyDevTools", "downloading": "Laster ned oppdatering…", diff --git a/apps/desktop-ui/messages/nl.json b/apps/desktop-ui/messages/nl.json index 4c09bf9c..557b7429 100644 --- a/apps/desktop-ui/messages/nl.json +++ b/apps/desktop-ui/messages/nl.json @@ -709,6 +709,7 @@ "all": "Alles", "total": "Totaal", "allProjects": "Alle projecten", + "projectsHeading": "Projecten", "export": "Exporteren", "kanban": "Kanban", "list": "Lijst", @@ -966,6 +967,16 @@ "emptyHintMobile": "Tik op de + knop om je eerste wachtwoord toe te voegen", "emptyHintDesktop": "Je hebt nog geen wachtwoorden opgeslagen. Klik op de knop \"Wachtwoord toevoegen\" hierboven om te beginnen.", "addPassword": "Wachtwoord toevoegen", + "title": "Wachtwoorden", + "filters": { + "heading": "Kluisstatus", + "all": "Alle wachtwoorden", + "weak": "Zwak", + "reused": "Hergebruikt", + "noUrl": "Geen URL", + "tagsHeading": "Tags", + "allTags": "Alle tags" + }, "searchPlaceholderMobile": "Doorzoek je kluis...", "searchPlaceholderDesktop": "Kluis doorzoeken...", "importExport": "Importeren / exporteren", @@ -1161,6 +1172,8 @@ "emptyHint": "Maak een set per project en omgeving (bijv. productie-API-sleutels). Alles wordt in uw browser versleuteld vóór upload.", "searchPlaceholder": "Zoek project, omgeving, tags, notities…", "tagFilterPlaceholder": "Filter op tag", + "allProjects": "Alle projecten", + "projectsHeading": "Projecten", "variableCount": "{count, plural, one {# variabele} other {# variabelen}}", "updatedAgo": "Bijgewerkt {time}", "copyDotEnv": ".env kopiëren", @@ -5095,6 +5108,10 @@ "ToolPageHeader": { "offline": "100% offline" }, + "ToolSidebar": { + "show": "Zijbalk tonen", + "hide": "Zijbalk verbergen" + }, "DesktopUpdate": { "title": "MyDevTools wordt bijgewerkt", "downloading": "Update wordt gedownload…", diff --git a/apps/desktop-ui/messages/pl.json b/apps/desktop-ui/messages/pl.json index a2e52692..8d11d849 100644 --- a/apps/desktop-ui/messages/pl.json +++ b/apps/desktop-ui/messages/pl.json @@ -709,6 +709,7 @@ "all": "All", "total": "Total", "allProjects": "All Projects", + "projectsHeading": "Projekty", "export": "Export", "kanban": "Kanban", "list": "List", @@ -966,6 +967,16 @@ "emptyHintMobile": "Tap the + button to add your first password", "emptyHintDesktop": "You have not stored any passwords yet. Click the \"Add password\" button above to get started.", "addPassword": "Add password", + "title": "Hasła", + "filters": { + "heading": "Stan sejfu", + "all": "Wszystkie hasła", + "weak": "Słabe", + "reused": "Powtórzone", + "noUrl": "Bez adresu URL", + "tagsHeading": "Tagi", + "allTags": "Wszystkie tagi" + }, "searchPlaceholderMobile": "Search your vault...", "searchPlaceholderDesktop": "Search vault...", "importExport": "Import / export", @@ -1161,6 +1172,8 @@ "emptyHint": "Utwórz zestaw dla każdego projektu i środowiska (np. klucze API produkcji). Wszystko jest szyfrowane w przeglądarce przed wysłaniem.", "searchPlaceholder": "Szukaj projektu, środowiska, tagów, notatek…", "tagFilterPlaceholder": "Filtruj według tagu", + "allProjects": "Wszystkie projekty", + "projectsHeading": "Projekty", "variableCount": "{count, plural, one {# zmienna} few {# zmienne} other {# zmiennych}}", "updatedAgo": "Zaktualizowano {time}", "copyDotEnv": "Kopiuj .env", @@ -5095,6 +5108,10 @@ "ToolPageHeader": { "offline": "100% offline" }, + "ToolSidebar": { + "show": "Pokaż pasek boczny", + "hide": "Ukryj pasek boczny" + }, "DesktopUpdate": { "title": "Aktualizowanie MyDevTools", "downloading": "Pobieranie aktualizacji…", diff --git a/apps/desktop-ui/messages/pt-BR.json b/apps/desktop-ui/messages/pt-BR.json index b683c90e..55a8c1bb 100644 --- a/apps/desktop-ui/messages/pt-BR.json +++ b/apps/desktop-ui/messages/pt-BR.json @@ -709,6 +709,7 @@ "all": "Todas", "total": "Total", "allProjects": "Todos os Projetos", + "projectsHeading": "Projetos", "export": "Exportar", "kanban": "Kanban", "list": "Lista", @@ -966,6 +967,16 @@ "emptyHintMobile": "Toque no botão + para adicionar sua primeira senha", "emptyHintDesktop": "Você ainda não armazenou nenhuma senha. Clique no botão \"Adicionar senha\" acima para começar.", "addPassword": "Adicionar senha", + "title": "Senhas", + "filters": { + "heading": "Saúde do cofre", + "all": "Todas as senhas", + "weak": "Fracas", + "reused": "Reutilizadas", + "noUrl": "Sem URL", + "tagsHeading": "Tags", + "allTags": "Todas as tags" + }, "searchPlaceholderMobile": "Buscar no cofre...", "searchPlaceholderDesktop": "Buscar no cofre...", "importExport": "Importar / exportar", @@ -1161,6 +1172,8 @@ "emptyHint": "Crie um conjunto para cada projeto e ambiente (ex.: chaves de API de produção). Tudo é criptografado no navegador antes do envio.", "searchPlaceholder": "Buscar projeto, ambiente, tags, notas…", "tagFilterPlaceholder": "Filtrar por tag", + "allProjects": "Todos os projetos", + "projectsHeading": "Projetos", "variableCount": "{count, plural, one {# variável} other {# variáveis}}", "updatedAgo": "Atualizado {time}", "copyDotEnv": "Copiar .env", @@ -5095,6 +5108,10 @@ "ToolPageHeader": { "offline": "100% offline" }, + "ToolSidebar": { + "show": "Mostrar barra lateral", + "hide": "Ocultar barra lateral" + }, "DesktopUpdate": { "title": "Atualizando o MyDevTools", "downloading": "Baixando atualização…", diff --git a/apps/desktop-ui/messages/pt.json b/apps/desktop-ui/messages/pt.json index 0ebe342f..85d6e1d9 100644 --- a/apps/desktop-ui/messages/pt.json +++ b/apps/desktop-ui/messages/pt.json @@ -709,6 +709,7 @@ "all": "Todas", "total": "Total", "allProjects": "Todos os Projetos", + "projectsHeading": "Projetos", "export": "Exportar", "kanban": "Kanban", "list": "Lista", @@ -966,6 +967,16 @@ "emptyHintMobile": "Toca no botão + para adicionar a tua primeira palavra-passe", "emptyHintDesktop": "Ainda não armazenaste nenhuma palavra-passe. Clica no botão \"Adicionar palavra-passe\" acima para começar.", "addPassword": "Adicionar palavra-passe", + "title": "Palavras-passe", + "filters": { + "heading": "Estado do cofre", + "all": "Todas as palavras-passe", + "weak": "Fracas", + "reused": "Reutilizadas", + "noUrl": "Sem URL", + "tagsHeading": "Etiquetas", + "allTags": "Todas as etiquetas" + }, "searchPlaceholderMobile": "Pesquisar no cofre...", "searchPlaceholderDesktop": "Pesquisar no cofre...", "importExport": "Importar / exportar", @@ -1161,6 +1172,8 @@ "emptyHint": "Crie um conjunto por projeto e ambiente (ex.: chaves API de produção). Tudo é encriptado no navegador antes do envio.", "searchPlaceholder": "Pesquisar projeto, ambiente, etiquetas, notas…", "tagFilterPlaceholder": "Filtrar por etiqueta", + "allProjects": "Todos os projetos", + "projectsHeading": "Projetos", "variableCount": "{count, plural, one {# variável} other {# variáveis}}", "updatedAgo": "Atualizado {time}", "copyDotEnv": "Copiar .env", @@ -5095,6 +5108,10 @@ "ToolPageHeader": { "offline": "100% offline" }, + "ToolSidebar": { + "show": "Mostrar barra lateral", + "hide": "Ocultar barra lateral" + }, "DesktopUpdate": { "title": "A atualizar o MyDevTools", "downloading": "A transferir a atualização…", diff --git a/apps/desktop-ui/messages/ru.json b/apps/desktop-ui/messages/ru.json index 279dd21f..1b4d844c 100644 --- a/apps/desktop-ui/messages/ru.json +++ b/apps/desktop-ui/messages/ru.json @@ -709,6 +709,7 @@ "all": "Все", "total": "Итого", "allProjects": "Все проекты", + "projectsHeading": "Проекты", "export": "Экспорт", "kanban": "Канбан", "list": "Список", @@ -966,6 +967,16 @@ "emptyHintMobile": "Нажмите кнопку +, чтобы добавить первый пароль", "emptyHintDesktop": "У вас пока нет сохранённых паролей. Нажмите «Добавить пароль», чтобы начать.", "addPassword": "Добавить пароль", + "title": "Пароли", + "filters": { + "heading": "Состояние хранилища", + "all": "Все пароли", + "weak": "Слабые", + "reused": "Повторяющиеся", + "noUrl": "Без URL", + "tagsHeading": "Теги", + "allTags": "Все теги" + }, "searchPlaceholderMobile": "Поиск по хранилищу...", "searchPlaceholderDesktop": "Поиск по хранилищу...", "importExport": "Импорт / экспорт", @@ -1161,6 +1172,8 @@ "emptyHint": "Создайте набор для каждого проекта и среды (например, ключи API продакшена). Всё шифруется в браузере перед загрузкой.", "searchPlaceholder": "Поиск по проекту, среде, тегам, заметкам…", "tagFilterPlaceholder": "Фильтр по тегу", + "allProjects": "Все проекты", + "projectsHeading": "Проекты", "variableCount": "{count, plural, one {# переменная} few {# переменные} other {# переменных}}", "updatedAgo": "Обновлено {time}", "copyDotEnv": "Копировать .env", @@ -5095,6 +5108,10 @@ "ToolPageHeader": { "offline": "100% офлайн" }, + "ToolSidebar": { + "show": "Показать боковую панель", + "hide": "Скрыть боковую панель" + }, "DesktopUpdate": { "title": "Обновление MyDevTools", "downloading": "Загрузка обновления…", diff --git a/apps/desktop-ui/messages/sv.json b/apps/desktop-ui/messages/sv.json index f44fa205..25a741b4 100644 --- a/apps/desktop-ui/messages/sv.json +++ b/apps/desktop-ui/messages/sv.json @@ -709,6 +709,7 @@ "all": "Alla", "total": "Totalt", "allProjects": "Alla projekt", + "projectsHeading": "Projekt", "export": "Exportera", "kanban": "Kanban", "list": "Lista", @@ -966,6 +967,16 @@ "emptyHintMobile": "Tryck på +-knappen för att lägga till ditt första lösenord", "emptyHintDesktop": "Du har inte lagrat några lösenord ännu. Klicka på knappen \"Lägg till lösenord\" ovan för att komma igång.", "addPassword": "Lägg till lösenord", + "title": "Lösenord", + "filters": { + "heading": "Valvets status", + "all": "Alla lösenord", + "weak": "Svaga", + "reused": "Återanvända", + "noUrl": "Ingen URL", + "tagsHeading": "Taggar", + "allTags": "Alla taggar" + }, "searchPlaceholderMobile": "Sök i ditt valv...", "searchPlaceholderDesktop": "Sök i valvet...", "importExport": "Importera / exportera", @@ -1161,6 +1172,8 @@ "emptyHint": "Skapa en uppsättning för varje projekt och miljö (t.ex. produktions-API-nycklar). Allt krypteras i webbläsaren före uppladdning.", "searchPlaceholder": "Sök projekt, miljö, taggar, anteckningar…", "tagFilterPlaceholder": "Filtrera på tagg", + "allProjects": "Alla projekt", + "projectsHeading": "Projekt", "variableCount": "{count, plural, one {# variabel} other {# variabler}}", "updatedAgo": "Uppdaterad {time}", "copyDotEnv": "Kopiera .env", @@ -5106,6 +5119,10 @@ "ToolPageHeader": { "offline": "100 % offline" }, + "ToolSidebar": { + "show": "Visa sidopanel", + "hide": "Dölj sidopanel" + }, "DesktopUpdate": { "title": "Uppdaterar MyDevTools", "downloading": "Laddar ner uppdatering…", diff --git a/apps/desktop-ui/messages/tr.json b/apps/desktop-ui/messages/tr.json index 796be883..7ce7d861 100644 --- a/apps/desktop-ui/messages/tr.json +++ b/apps/desktop-ui/messages/tr.json @@ -709,6 +709,7 @@ "all": "Tümü", "total": "Toplam", "allProjects": "Tüm Projeler", + "projectsHeading": "Projeler", "export": "Dışa Aktar", "kanban": "Kanban", "list": "Liste", @@ -966,6 +967,16 @@ "emptyHintMobile": "İlk şifrenizi eklemek için + düğmesine dokunun", "emptyHintDesktop": "Henüz kayıtlı şifreniz yok. Başlamak için yukarıdaki \"Şifre ekle\" düğmesine tıklayın.", "addPassword": "Şifre ekle", + "title": "Parolalar", + "filters": { + "heading": "Kasa durumu", + "all": "Tüm parolalar", + "weak": "Zayıf", + "reused": "Tekrar kullanılan", + "noUrl": "URL yok", + "tagsHeading": "Etiketler", + "allTags": "Tüm etiketler" + }, "searchPlaceholderMobile": "Kasanızda arayın...", "searchPlaceholderDesktop": "Kasada ara...", "importExport": "İçe / dışa aktar", @@ -1161,6 +1172,8 @@ "emptyHint": "Her proje ve ortam için bir küme oluşturun (ör. üretim API anahtarları). Yüklemeden önce her şey tarayıcınızda şifrelenir.", "searchPlaceholder": "Proje, ortam, etiket, not ara…", "tagFilterPlaceholder": "Etikete göre filtrele", + "allProjects": "Tüm projeler", + "projectsHeading": "Projeler", "variableCount": "{count, plural, one {# değişken} other {# değişken}}", "updatedAgo": "Güncellendi {time}", "copyDotEnv": ".env kopyala", @@ -5106,6 +5119,10 @@ "ToolPageHeader": { "offline": "%100 çevrimdışı" }, + "ToolSidebar": { + "show": "Kenar çubuğunu göster", + "hide": "Kenar çubuğunu gizle" + }, "DesktopUpdate": { "title": "MyDevTools güncelleniyor", "downloading": "Güncelleme indiriliyor…", diff --git a/apps/desktop-ui/messages/uk.json b/apps/desktop-ui/messages/uk.json index ec3ee262..962a3608 100644 --- a/apps/desktop-ui/messages/uk.json +++ b/apps/desktop-ui/messages/uk.json @@ -709,6 +709,7 @@ "all": "Усі", "total": "Всього", "allProjects": "Усі проєкти", + "projectsHeading": "Проєкти", "export": "Експорт", "kanban": "Канбан", "list": "Список", @@ -966,6 +967,16 @@ "emptyHintMobile": "Натисніть кнопку +, щоб додати перший пароль", "emptyHintDesktop": "Ви ще не зберегли жодного паролю. Натисніть кнопку «Додати пароль» вище, щоб почати.", "addPassword": "Додати пароль", + "title": "Паролі", + "filters": { + "heading": "Стан сховища", + "all": "Усі паролі", + "weak": "Слабкі", + "reused": "Повторювані", + "noUrl": "Без URL", + "tagsHeading": "Теги", + "allTags": "Усі теги" + }, "searchPlaceholderMobile": "Пошук у сховищі...", "searchPlaceholderDesktop": "Пошук у сховищі...", "importExport": "Імпорт / експорт", @@ -1161,6 +1172,8 @@ "emptyHint": "Створіть набір для кожного проєкту й середовища (наприклад, ключі API продакшену). Усе шифрується в браузері перед завантаженням.", "searchPlaceholder": "Пошук за проєктом, середовищем, тегами, нотатками…", "tagFilterPlaceholder": "Фільтр за тегом", + "allProjects": "Усі проєкти", + "projectsHeading": "Проєкти", "variableCount": "{count, plural, one {# змінна} few {# змінні} other {# змінних}}", "updatedAgo": "Оновлено {time}", "copyDotEnv": "Копіювати .env", @@ -5106,6 +5119,10 @@ "ToolPageHeader": { "offline": "100% офлайн" }, + "ToolSidebar": { + "show": "Показати бічну панель", + "hide": "Сховати бічну панель" + }, "DesktopUpdate": { "title": "Оновлення MyDevTools", "downloading": "Завантаження оновлення…", diff --git a/apps/desktop-ui/messages/vi.json b/apps/desktop-ui/messages/vi.json index e1f27695..85b51fda 100644 --- a/apps/desktop-ui/messages/vi.json +++ b/apps/desktop-ui/messages/vi.json @@ -709,6 +709,7 @@ "all": "Tất cả", "total": "Tổng", "allProjects": "Tất cả dự án", + "projectsHeading": "Dự án", "export": "Xuất", "kanban": "Kanban", "list": "Danh sách", @@ -966,6 +967,16 @@ "emptyHintMobile": "Nhấn nút + để thêm mật khẩu đầu tiên", "emptyHintDesktop": "Bạn chưa lưu mật khẩu nào. Nhấn nút \"Thêm mật khẩu\" ở trên để bắt đầu.", "addPassword": "Thêm mật khẩu", + "title": "Mật khẩu", + "filters": { + "heading": "Tình trạng kho", + "all": "Tất cả mật khẩu", + "weak": "Yếu", + "reused": "Dùng lại", + "noUrl": "Không có URL", + "tagsHeading": "Thẻ", + "allTags": "Tất cả thẻ" + }, "searchPlaceholderMobile": "Tìm trong kho...", "searchPlaceholderDesktop": "Tìm trong kho...", "importExport": "Nhập / xuất", @@ -1161,6 +1172,8 @@ "emptyHint": "Tạo một bộ cho mỗi dự án và môi trường (ví dụ khóa API production). Mọi thứ được mã hóa trong trình duyệt trước khi tải lên.", "searchPlaceholder": "Tìm dự án, môi trường, thẻ, ghi chú…", "tagFilterPlaceholder": "Lọc theo thẻ", + "allProjects": "Tất cả dự án", + "projectsHeading": "Dự án", "variableCount": "{count, plural, other {# biến}}", "updatedAgo": "Cập nhật {time}", "copyDotEnv": "Sao chép .env", @@ -5095,6 +5108,10 @@ "ToolPageHeader": { "offline": "100% ngoại tuyến" }, + "ToolSidebar": { + "show": "Hiện thanh bên", + "hide": "Ẩn thanh bên" + }, "DesktopUpdate": { "title": "Đang cập nhật MyDevTools", "downloading": "Đang tải bản cập nhật…", diff --git a/apps/desktop-ui/messages/zh.json b/apps/desktop-ui/messages/zh.json index 3e9414ec..f4d0c425 100644 --- a/apps/desktop-ui/messages/zh.json +++ b/apps/desktop-ui/messages/zh.json @@ -709,6 +709,7 @@ "all": "全部", "total": "总计", "allProjects": "所有项目", + "projectsHeading": "项目", "export": "导出", "kanban": "看板", "list": "列表", @@ -966,6 +967,16 @@ "emptyHintMobile": "点击 + 添加第一条密码", "emptyHintDesktop": "尚未保存任何密码。点击上方「添加密码」开始。", "addPassword": "添加密码", + "title": "密码", + "filters": { + "heading": "保险库健康度", + "all": "所有密码", + "weak": "弱密码", + "reused": "重复使用", + "noUrl": "无网址", + "tagsHeading": "标签", + "allTags": "所有标签" + }, "searchPlaceholderMobile": "搜索保险库…", "searchPlaceholderDesktop": "搜索保险库…", "importExport": "导入 / 导出", @@ -1161,6 +1172,8 @@ "emptyHint": "为每个项目与环境创建集合(例如生产环境 API 密钥)。上传前均在浏览器中加密。", "searchPlaceholder": "搜索项目、环境、标签、备注…", "tagFilterPlaceholder": "按标签筛选", + "allProjects": "所有项目", + "projectsHeading": "项目", "variableCount": "{count, plural, other {# 个变量}}", "updatedAgo": "更新于 {time}", "copyDotEnv": "复制 .env", @@ -5095,6 +5108,10 @@ "ToolPageHeader": { "offline": "100% 离线" }, + "ToolSidebar": { + "show": "显示侧边栏", + "hide": "隐藏侧边栏" + }, "DesktopUpdate": { "title": "正在更新 MyDevTools", "downloading": "正在下载更新…", diff --git a/apps/desktop-ui/scripts/build-tauri.mjs b/apps/desktop-ui/scripts/build-tauri.mjs index 9e991686..29b3534e 100644 --- a/apps/desktop-ui/scripts/build-tauri.mjs +++ b/apps/desktop-ui/scripts/build-tauri.mjs @@ -65,5 +65,7 @@ if (result.status !== 0) { } const outDir = path.join(uiRoot, "out"); +// outDir is derived from this script's own location and quoted by JSON.stringify. +// threatcrush-disable-next-line js-shell-exec-interpolation const pageCount = execSync(`find ${JSON.stringify(outDir)} -name '*.html' | wc -l`).toString().trim(); console.log(`build-tauri: OK — ${pageCount} HTML pages in apps/desktop-ui/out/`); diff --git a/apps/desktop-ui/src/app/app/api-keys/page.tsx b/apps/desktop-ui/src/app/app/api-keys/page.tsx index 2e644108..a4d39fae 100644 --- a/apps/desktop-ui/src/app/app/api-keys/page.tsx +++ b/apps/desktop-ui/src/app/app/api-keys/page.tsx @@ -4,7 +4,6 @@ import { useEffect, useRef } from "react" import { AddApiKeyDialog } from "@/components/api-key-vault/add-api-key-dialog" import { ApiKeyList } from "@/components/api-key-vault/api-key-list" import { useApiKeyVaultStore, type ApiKeyEntry } from "@/store/api-key-vault-store" -import { ToolHeader } from "@/components/tools/tool-header" import { useVaultGuard } from "@/hooks/use-vault-guard" import { VaultLockedPlaceholder } from "@/components/vault-locked-placeholder" import { VaultRestoringSkeleton } from "@/components/vault-restoring-skeleton" @@ -104,25 +103,11 @@ export default function ApiKeyVaultPage() { if (!user) return null + // Tool identity and the add action live in the ToolSidebarLayout header + // that ApiKeyList renders — no separate page header row. return ( -
- - -
- -
- +
+ {isMobile && }
) diff --git a/apps/desktop-ui/src/app/app/data-explorer/page.tsx b/apps/desktop-ui/src/app/app/data-explorer/page.tsx index 0ff02bd3..9da9816e 100644 --- a/apps/desktop-ui/src/app/app/data-explorer/page.tsx +++ b/apps/desktop-ui/src/app/app/data-explorer/page.tsx @@ -3,14 +3,12 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { useTranslations } from "next-intl"; import { toast } from "sonner"; -import { IconAlertTriangle, IconMenu2 } from "@tabler/icons-react"; +import { IconAlertTriangle, IconDatabase, IconPlus } from "@tabler/icons-react"; import { Button } from "@/components/ui/button"; -import { Sheet, SheetContent, SheetTitle } from "@/components/ui/sheet"; -import { VisuallyHidden } from "@radix-ui/react-visually-hidden"; import useAuth from "@/utils/useAuth"; import { useMasterKeyStore } from "@/store/master-key-store"; import { useVaultGuard } from "@/hooks/use-vault-guard"; -import { useMediaQuery } from "@/hooks/use-media-query"; +import { ToolSidebarLayout } from "@/components/tools/tool-sidebar"; import { VaultLockedPlaceholder } from "@/components/vault-locked-placeholder"; import { VaultRestoringSkeleton } from "@/components/vault-restoring-skeleton"; import { UnifiedTabBar } from "@/components/data-explorer/unified-tab-bar"; @@ -31,12 +29,10 @@ export default function DataExplorerPage() { const { user } = useAuth(); const { encryptionKey } = useMasterKeyStore(); const { isUnlocked, isRestoring } = useVaultGuard(); - const isWide = useMediaQuery("(min-width: 768px)"); const [connections, setConnections] = useState([]); const [tabs, setTabs] = useState([]); const [activeTabId, setActiveTabId] = useState(null); - const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false); const [isInitialized, setIsInitialized] = useState(false); const [connectionDialogOpen, setConnectionDialogOpen] = useState(false); const [editingConnection, setEditingConnection] = useState(null); @@ -108,7 +104,6 @@ export default function DataExplorerPage() { const openTab = useCallback( (connection: UnifiedConnection, req: OpenTabRequest) => { const id = `${connection.id}:${req.key}`; - setMobileSidebarOpen(false); setTabs((prev) => { if (prev.some((tab) => tab.id === id)) return prev; return [ @@ -253,35 +248,24 @@ export default function DataExplorerPage() { })(); return ( -
- {isWide ? ( -
{sidebar}
- ) : ( - - - - {t("title")} - - {sidebar} - - - )} + + + + } + sidebar={sidebar} + >
- {/* Narrow viewports keep the sidebar in the Sheet; without this - trigger there is no way to add a connection or open a tab. */} - {!isWide && ( -
- -
- )} void reloadConnections()} /> -
+
); } diff --git a/apps/desktop-ui/src/app/app/environment-manager/page.tsx b/apps/desktop-ui/src/app/app/environment-manager/page.tsx index 450d7fa4..3d8a95ad 100644 --- a/apps/desktop-ui/src/app/app/environment-manager/page.tsx +++ b/apps/desktop-ui/src/app/app/environment-manager/page.tsx @@ -3,7 +3,6 @@ import { useEffect, useRef } from "react" import { AddEnvironmentSetDialog } from "@/components/environment-manager/add-environment-set-dialog" import { EnvironmentSetList } from "@/components/environment-manager/environment-set-list" -import { ToolHeader } from "@/components/tools/tool-header" import { useEnvironmentManagerStore, type EnvSetEntry } from "@/store/environment-manager-store" import { useVaultGuard } from "@/hooks/use-vault-guard" import { VaultLockedPlaceholder } from "@/components/vault-locked-placeholder" @@ -110,22 +109,11 @@ export default function EnvironmentManagerPage() { if (!user) return null + // Tool identity lives in the ToolSidebarLayout header EnvironmentSetList + // renders — no separate page header row. return ( -
- {!isMobile && ( - - )} - -
- -
- +
+ {isMobile && }
) diff --git a/apps/desktop-ui/src/app/app/layout.tsx b/apps/desktop-ui/src/app/app/layout.tsx index 6d19ddd7..12b60cbd 100644 --- a/apps/desktop-ui/src/app/app/layout.tsx +++ b/apps/desktop-ui/src/app/app/layout.tsx @@ -1,26 +1,13 @@ import { Suspense } from "react"; -import { cookies } from "next/headers"; -import { SidebarProvider } from "@/components/ui/sidebar"; import { AppContent } from "./app-content"; -// Static export (Tauri desktop) cannot use request APIs; fall back to defaults there. -const isTauriBuild = process.env.TAURI_BUILD === "1"; - -export default async function Layout({ children }: { children: React.ReactNode }) { - let defaultOpen = true; - if (!isTauriBuild) { - const cookieStore = await cookies(); - defaultOpen = cookieStore.get("sidebar:state")?.value !== "false"; - } - +export default function Layout({ children }: { children: React.ReactNode }) { return ( - - {/* Suspense boundary so tool pages that read useSearchParams() (base64, - jwt-decoder, hash-generator, url-encode, json-formatter, qr-code) pass - the static export prerender instead of erroring on a CSR bailout. */} - - {children} - - + // Suspense boundary so tool pages that read useSearchParams() (base64, + // jwt-decoder, hash-generator, url-encode, json-formatter, qr-code) pass + // the static export prerender instead of erroring on a CSR bailout. + + {children} + ); } diff --git a/apps/desktop-ui/src/app/app/notes/context/NotesContext.tsx b/apps/desktop-ui/src/app/app/notes/context/NotesContext.tsx index c80c09ec..b9add677 100644 --- a/apps/desktop-ui/src/app/app/notes/context/NotesContext.tsx +++ b/apps/desktop-ui/src/app/app/notes/context/NotesContext.tsx @@ -37,8 +37,6 @@ interface NotesUI { setActiveNoteId: (id: string | null) => void; focusMode: boolean; setFocusMode: (v: boolean) => void; - sidebarOpen: boolean; - setSidebarOpen: (v: boolean) => void; } interface NotesActions { @@ -65,7 +63,6 @@ export function NotesProvider({ children }: { children: React.ReactNode }) { const [isContentLoading, setIsContentLoading] = useState(false); const [activeNoteId, setActiveNoteId] = useState(null); const [focusMode, setFocusMode] = useState(false); - const [sidebarOpen, setSidebarOpen] = useState(true); // Decrypted bodies, tagged with the key they were decrypted under: a vault // lock/unlock invalidates the whole map on read, with no reset effect. const [contentStore, setContentStore] = useState({ key: null, map: EMPTY_CONTENT }); @@ -402,8 +399,8 @@ export function NotesProvider({ children }: { children: React.ReactNode }) { ); const uiValue = useMemo( - () => ({ activeNoteId, setActiveNoteId, focusMode, setFocusMode, sidebarOpen, setSidebarOpen }), - [activeNoteId, focusMode, sidebarOpen] + () => ({ activeNoteId, setActiveNoteId, focusMode, setFocusMode }), + [activeNoteId, focusMode] ); const actionsValue = useMemo( diff --git a/apps/desktop-ui/src/app/app/notes/notes-client-layout.tsx b/apps/desktop-ui/src/app/app/notes/notes-client-layout.tsx index 615ce2c7..94cc182a 100644 --- a/apps/desktop-ui/src/app/app/notes/notes-client-layout.tsx +++ b/apps/desktop-ui/src/app/app/notes/notes-client-layout.tsx @@ -2,69 +2,33 @@ import { NotesProvider, useNotesUI } from "./context/NotesContext"; import NotesSidebar from "@/components/notes/NotesSidebar"; -import { useMediaQuery } from "@/hooks/use-media-query"; -import { Sheet, SheetContent, SheetTrigger, SheetHeader, SheetTitle } from "@/components/ui/sheet"; -import { Menu, PanelLeftOpen, NotebookPen } from "lucide-react"; -import { Button } from "@/components/ui/button"; +import { ToolSidebarLayout } from "@/components/tools/tool-sidebar"; +import { NotebookPen } from "lucide-react"; import { useTranslations } from "next-intl"; function NotesLayout({ children }: { children: React.ReactNode }) { - const t = useTranslations("Notes.layout"); const tSidebar = useTranslations("Notes.sidebar"); - const isDesktop = useMediaQuery("(min-width: 768px)"); - const { focusMode, sidebarOpen, setSidebarOpen } = useNotesUI(); - const sidebarCollapsed = isDesktop && !focusMode && !sidebarOpen; + const { focusMode } = useNotesUI(); - return ( -
- {isDesktop && !focusMode && sidebarOpen && } - -
- {sidebarCollapsed && ( -
- -
- - - - {tSidebar("title")} -
-
- )} - {!isDesktop && !focusMode && ( -
- - - - - - - {t("mobileNavTitle")} - - - - -
- )} + // Focus mode drops the panel entirely — no column, no re-open affordance. + if (focusMode) { + return ( +
{children}
-
+ ); + } + + return ( + } + className="bg-background mobile-nav-offset" + > + {children} + ); } diff --git a/apps/desktop-ui/src/app/app/password-manager/page.tsx b/apps/desktop-ui/src/app/app/password-manager/page.tsx index 7a34f0ff..fd29359a 100644 --- a/apps/desktop-ui/src/app/app/password-manager/page.tsx +++ b/apps/desktop-ui/src/app/app/password-manager/page.tsx @@ -20,9 +20,6 @@ import { fetchAllPages } from "@/lib/fetch-all-pages" import { EncryptedToolPlaceholder } from "@/components/encrypted-tool-placeholder" import { useActiveWorkspace } from "@/store/workspace-store" import { hasWorkspaceEncryption } from "@/lib/workspace-rbac" -import { IconShieldLock } from "@tabler/icons-react" -import { CATEGORY_ACCENT } from "@/components/dashboard/types" -import { cn } from "@/lib/utils" const PASSWORDS_PAGE_SIZE = 500 @@ -122,34 +119,11 @@ export default function PasswordManagerPage() { if (!user) return null + // Tool identity and the add action live in the ToolSidebarLayout header + // PasswordList renders — no separate page header row. return ( -
- {!isMobile && ( -
-
- - - -

{t("title")}

-
- -
- )} - +
- {isMobile && }
) diff --git a/apps/desktop-ui/src/app/app/s3-drive/page.tsx b/apps/desktop-ui/src/app/app/s3-drive/page.tsx index f41dca8f..1baab488 100644 --- a/apps/desktop-ui/src/app/app/s3-drive/page.tsx +++ b/apps/desktop-ui/src/app/app/s3-drive/page.tsx @@ -12,9 +12,9 @@ import { listConnections } from "@/lib/s3-drive-api" import { decryptData } from "@/lib/encryption" import { BucketSidebar } from "@/components/s3-drive/bucket-sidebar" import { FileBrowser } from "@/components/s3-drive/file-browser" +import { ToolSidebarLayout } from "@/components/tools/tool-sidebar" import { IconBucket, IconCloud, IconArrowRight } from "@tabler/icons-react" import { Skeleton } from "@/components/ui/skeleton" -import { cn } from "@/lib/utils" import { DesktopOnlineGate } from "@/components/desktop/desktop-online-gate" export default function S3DrivePage() { @@ -67,7 +67,7 @@ function S3DriveInner() { return (
{/* Sidebar skeleton */} -
+
@@ -120,11 +120,12 @@ function S3DriveInner() { const activeConn = connections.find((c) => c.id === activeConnectionId) ?? null return ( -
-
- -
- + } + >
{activeConn ? ( )}
-
+ ) } diff --git a/apps/desktop-ui/src/app/app/to-do/TaskContainer.tsx b/apps/desktop-ui/src/app/app/to-do/TaskContainer.tsx index 99be9f73..8f5f43ee 100644 --- a/apps/desktop-ui/src/app/app/to-do/TaskContainer.tsx +++ b/apps/desktop-ui/src/app/app/to-do/TaskContainer.tsx @@ -19,6 +19,11 @@ import { useTaskContext } from "@/app/app/to-do/context/TaskContext"; import { useWorkspaceMembers, memberLabel } from "@/app/app/to-do/hooks/useWorkspaceMembers"; import { useProjectContext } from "@/app/app/to-do/context/ProjectContext"; import { ListTodo, Circle, LayoutGrid, List, Search, X, Plus, Folder, Archive, ArchiveRestore, UserRound } from "lucide-react"; +import { + ToolSidebarFilterList, + ToolSidebarLayout, + type ToolSidebarFilterItem, +} from "@/components/tools/tool-sidebar"; import { Button } from "@/components/ui/button"; import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; import { STATUS_CONFIG } from "./config/constants"; @@ -35,7 +40,6 @@ import { DrawerClose, DrawerTrigger, } from "@/components/ui/drawer"; -import { SidebarTrigger } from "@/components/ui/sidebar"; import { Badge } from "@/components/ui/badge"; import { Select, @@ -200,7 +204,25 @@ export const TaskContainer = () => { return () => window.removeEventListener("keydown", handleKeyDown); }, [hasSearchFilter, isMobile]); + const projectFilters: ToolSidebarFilterItem[] = [ + { id: "all", label: tFilters("allProjects") }, + ...projects.map((project) => ({ id: project.id, label: project.name, dot: project.color })), + ]; + return ( + + } + >
{liveStatsMessage} @@ -212,8 +234,6 @@ export const TaskContainer = () => { style={{ paddingTop: "calc(env(safe-area-inset-top, 0px) + 0.5rem)" }} >
- -
{
-
- - - -
-

- {tPage("myTasksTitle")} -

-

- {tPage("statsLineDesktop", { total: allTaskStats.total, percent: completionRate })} -

-
-
+ {/* Tool name + icon live in the ToolSidebarLayout panel header. */} +

+ {tPage("statsLineDesktop", { total: allTaskStats.total, percent: completionRate })} +

{/* Enhanced Stats with Progress Bars - Hidden on mobile to save space */}
@@ -397,27 +408,7 @@ export const TaskContainer = () => { )}
- {/* Project Filter */} - + {/* Project filter lives in the ToolSidebarLayout panel. */} {/* Assignee Filter — only meaningful in a shared workspace */} {isShared && ( @@ -739,5 +730,6 @@ export const TaskContainer = () => { )}
+ ); }; diff --git a/apps/desktop-ui/src/app/app/to-do/utils/__tests__/statusOrder.test.ts b/apps/desktop-ui/src/app/app/to-do/utils/__tests__/statusOrder.test.ts new file mode 100644 index 00000000..83e81bec --- /dev/null +++ b/apps/desktop-ui/src/app/app/to-do/utils/__tests__/statusOrder.test.ts @@ -0,0 +1,51 @@ +import { insertOrder, needsRespread, ORDER_STEP } from "../statusOrder"; + +describe("insertOrder", () => { + it("starts at 0 in an empty column", () => { + expect(insertOrder([], 0)).toBe(0); + }); + + it("appends a step past the last card", () => { + expect(insertOrder([0, 1000], 2)).toBe(2000); + }); + + it("prepends a step before the first card", () => { + expect(insertOrder([0, 1000], 0)).toBe(-ORDER_STEP); + }); + + it("lands between two neighbours", () => { + expect(insertOrder([0, 1000], 1)).toBe(500); + }); + + it("always returns an integer — Rust reads statusOrder with as_i64", () => { + // An odd gap must not produce a fraction, or the row is skipped server-side. + expect(Number.isInteger(insertOrder([0, 1], 1))).toBe(true); + expect(Number.isInteger(insertOrder([0, 7], 1))).toBe(true); + }); + + it("clamps an out-of-range index instead of returning NaN", () => { + expect(insertOrder([0, 1000], 99)).toBe(2000); + expect(insertOrder([0, 1000], -5)).toBe(-ORDER_STEP); + }); + + it("keeps legacy 1/2/3 orders sortable without a migration", () => { + // Rows written before statusOrder meant "position" are tightly packed. + const order = insertOrder([1, 2, 3], 3); + expect(order).toBeGreaterThan(3); + }); +}); + +describe("needsRespread", () => { + it("is false when the insert found room", () => { + expect(needsRespread([0, 1000], 1, 500)).toBe(false); + }); + + it("is true when the gap between neighbours is exhausted", () => { + // No integer exists strictly between 5 and 6. + expect(needsRespread([5, 6], 1, insertOrder([5, 6], 1))).toBe(true); + }); + + it("is false at the head of a column, where there is always room", () => { + expect(needsRespread([0, 1000], 0, -ORDER_STEP)).toBe(false); + }); +}); diff --git a/apps/desktop-ui/src/app/app/to-do/utils/statusOrder.ts b/apps/desktop-ui/src/app/app/to-do/utils/statusOrder.ts new file mode 100644 index 00000000..c86f5df5 --- /dev/null +++ b/apps/desktop-ui/src/app/app/to-do/utils/statusOrder.ts @@ -0,0 +1,36 @@ +/** + * Position of a card within its board column. + * + * Cards are spaced `ORDER_STEP` apart so that dropping one between two neighbours is a + * single PATCH of the moved card instead of a renumber of the whole column. When the gap + * between two neighbours is used up, the column is respread — see `needsRespread`. + * + * Integers only: the Rust handler reads `statusOrder` with `as_i64` inside a `filter_map`, + * so a fractional value is silently skipped when it computes the next order on create. + */ +export const ORDER_STEP = 1000; + +/** + * The `statusOrder` for a card inserted at `index` of a column whose existing cards have + * `orders` (ascending, excluding the card being moved). + */ +export function insertOrder(orders: number[], index: number): number { + const at = Math.max(0, Math.min(index, orders.length)); + const prev = orders[at - 1]; + const next = orders[at]; + + if (prev === undefined && next === undefined) return 0; + if (prev === undefined) return next - ORDER_STEP; + if (next === undefined) return prev + ORDER_STEP; + return Math.floor((prev + next) / 2); +} + +/** + * True when `insertOrder` could not find room between the neighbours, so the column has to + * be respread at even intervals before the position is meaningful. + */ +export function needsRespread(orders: number[], index: number, order: number): boolean { + const at = Math.max(0, Math.min(index, orders.length)); + const prev = orders[at - 1]; + return prev !== undefined && order <= prev; +} diff --git a/apps/desktop-ui/src/components/api-client/api-client.tsx b/apps/desktop-ui/src/components/api-client/api-client.tsx index 3a62396a..943e9519 100644 --- a/apps/desktop-ui/src/components/api-client/api-client.tsx +++ b/apps/desktop-ui/src/components/api-client/api-client.tsx @@ -29,6 +29,7 @@ import { parseCurlCommand } from "@/utils/curl-parser" import { generateCode } from "./generate-code" import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard" import { CollectionsSidebar } from "./collections/collections-sidebar" +import { ToolSidebarLayout } from "@/components/tools/tool-sidebar" import dynamic from "next/dynamic" import { RequestMethod, @@ -44,13 +45,11 @@ import type { ScriptContext } from "./workers/scripts-runner.worker" import { useTranslations } from "next-intl" import { toast } from "sonner" import { useIsMobile } from "@/components/hooks/use-mobile" -import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet" import { Button } from "@/components/ui/button" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" -import { FolderOpen, PanelRight, MoreVertical, MoreHorizontal, Cookie, Download, Activity, Keyboard } from "lucide-react" +import { FolderOpen, MoreVertical, MoreHorizontal, Cookie, Download, Activity, Keyboard } from "lucide-react" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, DropdownMenuSeparator } from "@/components/ui/dropdown-menu" import { IconCode, IconSettings } from "@tabler/icons-react" -import { cn } from "@/lib/utils" import { ensureHttpScheme } from "@/lib/url-normalize" import { encodeBasicCredentials } from "@/lib/basic-auth" import { ensureFreshToken } from "@/lib/oauth2" @@ -131,8 +130,6 @@ function ApiClientInner() { const sessionVarsRef = React.useRef>({}) const isMobile = useIsMobile() - const [collectionsOpen, setCollectionsOpen] = React.useState(false) - const [sidebarOpen, setSidebarOpen] = React.useState(true) const [mobilePanel, setMobilePanel] = React.useState<'request' | 'response'>('request') const [envMgrOpen, setEnvMgrOpen] = React.useState(false) const [codeGenOpen, setCodeGenOpen] = React.useState(false) @@ -1105,31 +1102,15 @@ function ApiClientInner() { ) return ( -
+ } + className="mobile-nav-offset" + >
- {/* Mobile Collections Button */} - {isMobile && ( - - - - - -
-
- { - handleLoadRequest(request) - setCollectionsOpen(false) - }} - /> -
- - - )}
{isMobile ? ( /* Mobile: secondary actions collapsed into dropdown */ @@ -1270,15 +1251,6 @@ function ApiClientInner() {
- )} {/* Dialogs rendered outside the conditional so state is preserved */} @@ -1462,22 +1434,7 @@ function ApiClientInner() {
- - {/* Desktop Collections Sidebar */} - {!isMobile && ( -
- setSidebarOpen(false)} - /> -
- )} -
+ ) } diff --git a/apps/desktop-ui/src/components/api-client/collections/collections-sidebar.tsx b/apps/desktop-ui/src/components/api-client/collections/collections-sidebar.tsx index 1e8c41b6..d4164ce7 100644 --- a/apps/desktop-ui/src/components/api-client/collections/collections-sidebar.tsx +++ b/apps/desktop-ui/src/components/api-client/collections/collections-sidebar.tsx @@ -6,7 +6,7 @@ import { Checkbox } from "@/components/ui/checkbox" import { ScrollArea } from "@/components/ui/scroll-area" import { Collection, CollectionRequest } from "../types" import { CollectionItem } from "./collection-item" -import { FolderPlus, Trash2, Pencil, MoreHorizontal, Search, X, FileDown, Play, Server, Link2, Globe, PanelRightClose } from "lucide-react" +import { FolderPlus, Trash2, Pencil, MoreHorizontal, Search, X, FileDown, Play, Server, Link2, Globe } from "lucide-react" import { buildShareUrl } from "@/lib/share-link" import { apiFetch } from "@/lib/desktop/api-fetch" import { toast } from "sonner" @@ -47,16 +47,23 @@ import { useHistoryState, useHistoryActions } from "../context/history-context" import { CollectionsSidebarSkeleton, HistoryListSkeleton } from "../skeletons" import { useDebouncedValue } from "@/lib/use-debounced-value" import { isDesktop } from "@/lib/desktop/is-desktop" +import { ToolSidebarActions, useToolSidebarPanel } from "@/components/tools/tool-sidebar" interface CollectionsSidebarProps { onLoadRequest: (request: CollectionRequest) => void - onCollapse?: () => void } -export function CollectionsSidebar({ - onLoadRequest, - onCollapse, -}: CollectionsSidebarProps) { +export function CollectionsSidebar({ onLoadRequest: loadRequest }: CollectionsSidebarProps) { + const panel = useToolSidebarPanel() + // On mobile the panel is a sheet over the request pane — loading a request + // has to dismiss it or the loaded request stays hidden behind the sheet. + const onLoadRequest = React.useCallback( + (request: CollectionRequest) => { + loadRequest(request) + if (panel?.isMobile) panel.close() + }, + [loadRequest, panel], + ) const { collections, isLoading } = useCollectionsState() const { addFolder: onAddFolder, deleteItem: onDelete, toggleFolder: onToggle, createCollection: onCreateCollection, renameCollection: onRenameCollection, renameFolder: onRenameFolder, patchFolder, deleteMultipleCollections: onDeleteMultiple } = useCollectionsActions() const { history, isLoading: isHistoryLoading } = useHistoryState() @@ -189,45 +196,34 @@ export function CollectionsSidebar({ } return ( -
+ // Header (icon, title, collapse) is owned by ToolSidebarLayout in + // components/api-client/api-client.tsx — this renders the body only. + <> + + {selectedCollections.size > 0 && ( + + )} + + +
-
-

{t("title")}

-
- {selectedCollections.size > 0 && ( - - )} - - {onCollapse && ( - - )} -
-
+ ) } diff --git a/apps/desktop-ui/src/components/api-client/request-tabs.tsx b/apps/desktop-ui/src/components/api-client/request-tabs.tsx index d257efa8..d12e2e4f 100644 --- a/apps/desktop-ui/src/components/api-client/request-tabs.tsx +++ b/apps/desktop-ui/src/components/api-client/request-tabs.tsx @@ -1189,6 +1189,7 @@ export function RequestTabs({