feat: step 5 claimed done

This commit is contained in:
2026-04-27 22:36:53 +02:00
parent 0e1d8852f7
commit 2f09bf527d
20 changed files with 1740 additions and 115 deletions

View File

@@ -45,6 +45,37 @@
"render.video.vimeoTitle": "Vimeo-Video",
"render.video.youtubeTitle": "YouTube-Video",
"sidebar.chat.yesterday": "Gestern",
"translationValidation.title": "Übersetzungen validieren",
"translationValidation.summary": "Geprüfte DB-Zeilen: %{dbRows} · Geprüfte Dateien: %{files} · Ungültige DB-Zeilen: %{invalidDb} · Ungültige Dateien: %{invalidFiles}",
"translationValidation.databaseTitle": "Ungültige Übersetzungszeilen in der Datenbank",
"translationValidation.filesystemTitle": "Ungültige Übersetzungsdateien auf dem Datenträger",
"translationValidation.noneDatabase": "Keine ungültigen Übersetzungszeilen gefunden.",
"translationValidation.noneFilesystem": "Keine ungültigen Übersetzungsdateien gefunden.",
"translationValidation.issue.sameLanguage": "Übersetzungssprache entspricht der kanonischen Beitragssprache",
"translationValidation.issue.missingSource": "Übersetzung verweist auf einen fehlenden Quellbeitrag",
"translationValidation.issue.doNotTranslate": "Beitrag ist als nicht-übersetzen markiert, hat aber Übersetzungen",
"translationValidation.issue.contentInDatabase": "Veröffentlichte Übersetzung hat Inhalt in der DB statt im Dateisystem",
"translationValidation.field.translationFor": "Quellbeitrag",
"translationValidation.field.translationId": "Übersetzungszeile",
"translationValidation.field.title": "Titel",
"translationValidation.field.languages": "Sprachen",
"translationValidation.field.filePath": "Datei",
"translationValidation.languagesWithCanonical": "%{canonical} = %{translation}",
"translationValidation.revalidate": "Erneut validieren",
"translationValidation.fix": "Probleme beheben",
"translationValidation.toast.fixSuccess": "%{dbRows} DB-Zeilen und %{files} Dateien gelöscht, %{flushed} Übersetzungen auf Datenträger geschrieben",
"chat.newChat": "Neuer Chat",
"chat.welcomeTitle": "Willkommen beim KI-Assistenten",
"chat.welcomeDescription": "Ich kann dir mit interaktiven Visualisierungen bei deinem Blog helfen. Frag mich zum Beispiel nach:",
"chat.welcomeTipSearch": "Beiträgen zu einem bestimmten Thema",
"chat.welcomeTipChart": "einem Diagramm der pro Monat veröffentlichten Beiträge",
"chat.welcomeTipTable": "einem Tabellenvergleich meiner letzten Beiträge",
"chat.welcomeTipMetadata": "Metadaten für Beiträge oder Medien",
"chat.welcomeTipTabs": "Beitragsstatistiken pro Jahr in Tabs mit Diagrammen",
"chat.role.you": "Du",
"chat.role.assistant": "Assistent",
"chat.inputPlaceholder": "Nachricht eingeben...",
"gitDiff.changedFiles": "Geänderte Dateien",
"sidebar.tags": "Schlagwörter",
"sidebar.categories": "Kategorien",
"sidebar.clearTags": "Tags löschen",

View File

@@ -45,6 +45,37 @@
"render.video.vimeoTitle": "Vimeo video",
"render.video.youtubeTitle": "YouTube video",
"sidebar.chat.yesterday": "Yesterday",
"translationValidation.title": "Validate Translations",
"translationValidation.summary": "Checked DB rows: %{dbRows} · Checked files: %{files} · Invalid DB rows: %{invalidDb} · Invalid files: %{invalidFiles}",
"translationValidation.databaseTitle": "Invalid database translation rows",
"translationValidation.filesystemTitle": "Invalid translation files on disk",
"translationValidation.noneDatabase": "No invalid translation rows found.",
"translationValidation.noneFilesystem": "No invalid translation files found.",
"translationValidation.issue.sameLanguage": "Translation language matches canonical post language",
"translationValidation.issue.missingSource": "Translation points to a missing source post",
"translationValidation.issue.doNotTranslate": "Post is marked as do-not-translate but has translations",
"translationValidation.issue.contentInDatabase": "Published translation has content stuck in DB instead of filesystem",
"translationValidation.field.translationFor": "Source post",
"translationValidation.field.translationId": "Translation row",
"translationValidation.field.title": "Title",
"translationValidation.field.languages": "Languages",
"translationValidation.field.filePath": "File",
"translationValidation.languagesWithCanonical": "%{canonical} = %{translation}",
"translationValidation.revalidate": "Revalidate",
"translationValidation.fix": "Fix Issues",
"translationValidation.toast.fixSuccess": "Deleted %{dbRows} DB rows and %{files} files, flushed %{flushed} translations to disk",
"chat.newChat": "New Chat",
"chat.welcomeTitle": "Welcome to the AI Assistant",
"chat.welcomeDescription": "I can help you manage your blog with rich visualizations. Try asking me to:",
"chat.welcomeTipSearch": "Search for posts about a specific topic",
"chat.welcomeTipChart": "Show a chart of posts published per month",
"chat.welcomeTipTable": "Compare my recent posts in a table",
"chat.welcomeTipMetadata": "Update metadata for posts or media",
"chat.welcomeTipTabs": "Show post statistics by year in tabs with charts",
"chat.role.you": "You",
"chat.role.assistant": "Assistant",
"chat.inputPlaceholder": "Type a message...",
"gitDiff.changedFiles": "Changed files",
"sidebar.tags": "Tags",
"sidebar.categories": "Categories",
"sidebar.clearTags": "Clear tags",

View File

@@ -45,6 +45,37 @@
"render.video.vimeoTitle": "Vídeo de Vimeo",
"render.video.youtubeTitle": "Vídeo de YouTube",
"sidebar.chat.yesterday": "Ayer",
"translationValidation.title": "Validar traducciones",
"translationValidation.summary": "Filas de BD revisadas: %{dbRows} · Archivos revisados: %{files} · Filas de BD inválidas: %{invalidDb} · Archivos inválidos: %{invalidFiles}",
"translationValidation.databaseTitle": "Filas de traducción inválidas en la base de datos",
"translationValidation.filesystemTitle": "Archivos de traducción inválidos en disco",
"translationValidation.noneDatabase": "No se encontraron filas de traducción inválidas.",
"translationValidation.noneFilesystem": "No se encontraron archivos de traducción inválidos.",
"translationValidation.issue.sameLanguage": "El idioma de la traducción coincide con el idioma canónico de la entrada",
"translationValidation.issue.missingSource": "La traducción apunta a una entrada de origen inexistente",
"translationValidation.issue.doNotTranslate": "La entrada está marcada como no-traducir pero tiene traducciones",
"translationValidation.issue.contentInDatabase": "Traducción publicada con contenido en la BD en lugar del sistema de archivos",
"translationValidation.field.translationFor": "Entrada de origen",
"translationValidation.field.translationId": "Fila de traducción",
"translationValidation.field.title": "Título",
"translationValidation.field.languages": "Idiomas",
"translationValidation.field.filePath": "Archivo",
"translationValidation.languagesWithCanonical": "%{canonical} = %{translation}",
"translationValidation.revalidate": "Revalidar",
"translationValidation.fix": "Corregir problemas",
"translationValidation.toast.fixSuccess": "%{dbRows} filas de BD y %{files} archivos eliminados, %{flushed} traducciones escritas a disco",
"chat.newChat": "Nuevo chat",
"chat.welcomeTitle": "Bienvenido al asistente de IA",
"chat.welcomeDescription": "Puedo ayudarte a gestionar tu blog con visualizaciones interactivas. Prueba a pedirme que:",
"chat.welcomeTipSearch": "Busque entradas sobre un tema específico",
"chat.welcomeTipChart": "Muestre un gráfico de entradas publicadas por mes",
"chat.welcomeTipTable": "Compare mis entradas recientes en una tabla",
"chat.welcomeTipMetadata": "Actualice metadatos de entradas o medios",
"chat.welcomeTipTabs": "Muestre estadísticas por año en pestañas con gráficos",
"chat.role.you": "Tú",
"chat.role.assistant": "Asistente",
"chat.inputPlaceholder": "Escribe un mensaje...",
"gitDiff.changedFiles": "Archivos modificados",
"sidebar.tags": "Etiquetas",
"sidebar.categories": "Categorías",
"sidebar.clearTags": "Limpiar etiquetas",

View File

@@ -45,6 +45,37 @@
"render.video.vimeoTitle": "Vidéo Vimeo",
"render.video.youtubeTitle": "Vidéo YouTube",
"sidebar.chat.yesterday": "Hier",
"translationValidation.title": "Valider les traductions",
"translationValidation.summary": "Lignes BD vérifiées : %{dbRows} · Fichiers vérifiés : %{files} · Lignes BD invalides : %{invalidDb} · Fichiers invalides : %{invalidFiles}",
"translationValidation.databaseTitle": "Lignes de traduction invalides dans la base de données",
"translationValidation.filesystemTitle": "Fichiers de traduction invalides sur le disque",
"translationValidation.noneDatabase": "Aucune ligne de traduction invalide trouvée.",
"translationValidation.noneFilesystem": "Aucun fichier de traduction invalide trouvé.",
"translationValidation.issue.sameLanguage": "La langue de traduction correspond à la langue canonique de larticle",
"translationValidation.issue.missingSource": "La traduction pointe vers un article source manquant",
"translationValidation.issue.doNotTranslate": "L'article est marqué ne-pas-traduire mais a des traductions",
"translationValidation.issue.contentInDatabase": "Traduction publiée avec contenu encore en base au lieu du système de fichiers",
"translationValidation.field.translationFor": "Article source",
"translationValidation.field.translationId": "Ligne de traduction",
"translationValidation.field.title": "Titre",
"translationValidation.field.languages": "Langues",
"translationValidation.field.filePath": "Fichier",
"translationValidation.languagesWithCanonical": "%{canonical} = %{translation}",
"translationValidation.revalidate": "Revalider",
"translationValidation.fix": "Corriger les problèmes",
"translationValidation.toast.fixSuccess": "%{dbRows} lignes DB et %{files} fichiers supprimés, %{flushed} traductions écrites sur disque",
"chat.newChat": "Nouveau chat",
"chat.welcomeTitle": "Bienvenue dans lassistant IA",
"chat.welcomeDescription": "Je peux vous aider à gérer votre blog avec des visualisations riches. Essayez par exemple :",
"chat.welcomeTipSearch": "Rechercher des articles sur un sujet précis",
"chat.welcomeTipChart": "Afficher un graphique des articles publiés par mois",
"chat.welcomeTipTable": "Comparer mes derniers articles dans un tableau",
"chat.welcomeTipMetadata": "Mettre à jour les métadonnées des articles ou médias",
"chat.welcomeTipTabs": "Afficher les statistiques par année dans des onglets avec graphiques",
"chat.role.you": "Vous",
"chat.role.assistant": "Assistant IA",
"chat.inputPlaceholder": "Saisissez un message...",
"gitDiff.changedFiles": "Fichiers modifiés",
"sidebar.tags": "Étiquettes",
"sidebar.categories": "Catégories",
"sidebar.clearTags": "Effacer les étiquettes",

View File

@@ -45,6 +45,37 @@
"render.video.vimeoTitle": "Video Vimeo",
"render.video.youtubeTitle": "Video YouTube",
"sidebar.chat.yesterday": "Ieri",
"translationValidation.title": "Valida traduzioni",
"translationValidation.summary": "Righe DB controllate: %{dbRows} · File controllati: %{files} · Righe DB non valide: %{invalidDb} · File non validi: %{invalidFiles}",
"translationValidation.databaseTitle": "Righe di traduzione non valide nel database",
"translationValidation.filesystemTitle": "File di traduzione non validi sul disco",
"translationValidation.noneDatabase": "Nessuna riga di traduzione non valida trovata.",
"translationValidation.noneFilesystem": "Nessun file di traduzione non valido trovato.",
"translationValidation.issue.sameLanguage": "La lingua della traduzione coincide con la lingua canonica del post",
"translationValidation.issue.missingSource": "La traduzione punta a un post sorgente mancante",
"translationValidation.issue.doNotTranslate": "Il post è contrassegnato come non-tradurre ma ha traduzioni",
"translationValidation.issue.contentInDatabase": "Traduzione pubblicata con contenuto nel DB invece del filesystem",
"translationValidation.field.translationFor": "Post sorgente",
"translationValidation.field.translationId": "Riga traduzione",
"translationValidation.field.title": "Titolo",
"translationValidation.field.languages": "Lingue",
"translationValidation.field.filePath": "File",
"translationValidation.languagesWithCanonical": "%{canonical} = %{translation}",
"translationValidation.revalidate": "Rivalidare",
"translationValidation.fix": "Correggi problemi",
"translationValidation.toast.fixSuccess": "%{dbRows} righe DB e %{files} file eliminati, %{flushed} traduzioni scritte su disco",
"chat.newChat": "Nuova chat",
"chat.welcomeTitle": "Benvenuto nellassistente IA",
"chat.welcomeDescription": "Posso aiutarti a gestire il tuo blog con visualizzazioni interattive. Prova a chiedermi di:",
"chat.welcomeTipSearch": "Cercare post su un argomento specifico",
"chat.welcomeTipChart": "Mostrare un grafico dei post pubblicati per mese",
"chat.welcomeTipTable": "Confrontare i miei post recenti in una tabella",
"chat.welcomeTipMetadata": "Aggiornare i metadati di post o media",
"chat.welcomeTipTabs": "Mostrare statistiche per anno in schede con grafici",
"chat.role.you": "Tu",
"chat.role.assistant": "Assistente",
"chat.inputPlaceholder": "Scrivi un messaggio...",
"gitDiff.changedFiles": "File modificati",
"sidebar.tags": "Tag",
"sidebar.categories": "Categorie",
"sidebar.clearTags": "Cancella tag",

View File

@@ -3272,6 +3272,57 @@ button svg * {
font-weight: 700;
}
.chat-panel-header {
position: relative;
}
.chat-panel-header-actions {
display: flex;
align-items: center;
gap: 10px;
}
.chat-model-selector-button,
.chat-model-selector-option {
border: 1px solid var(--line, #3c3c3c);
border-radius: 10px;
background: var(--panel-2, #252526);
color: inherit;
}
.chat-model-selector-button {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
}
.chat-model-selector-menu {
position: absolute;
top: calc(100% - 4px);
right: 20px;
min-width: 220px;
display: flex;
flex-direction: column;
gap: 6px;
padding: 10px;
border: 1px solid var(--line, #3c3c3c);
border-radius: 12px;
background: var(--panel-1, #1e1e1e);
box-shadow: 0 14px 36px rgba(0, 0, 0, 0.28);
z-index: 2;
}
.chat-model-selector-option {
width: 100%;
padding: 8px 10px;
text-align: left;
}
.chat-model-selector-option.active {
border-color: var(--accent-color);
}
.chat-messages {
padding: 20px;
overflow: auto;
@@ -3300,6 +3351,56 @@ button svg * {
background: rgba(0, 122, 204, 0.15);
}
.chat-tool-markers {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 10px;
}
.chat-tool-marker {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 10px;
border-radius: 999px;
border: 1px solid var(--line, #3c3c3c);
background: rgba(255, 255, 255, 0.03);
font-size: 12px;
}
.chat-tool-surface {
max-width: min(820px, 100%);
margin-left: auto;
margin-right: auto;
border: 1px solid var(--line, #3c3c3c);
border-radius: 14px;
background: var(--panel-2, #252526);
padding: 16px;
}
.chat-tool-surface h3 {
margin: 0 0 12px;
}
.chat-tool-surface-table {
width: 100%;
border-collapse: collapse;
}
.chat-tool-surface-table th,
.chat-tool-surface-table td {
padding: 8px 10px;
border-bottom: 1px solid var(--line, #3c3c3c);
text-align: left;
}
.chat-tool-surface-json {
margin: 0;
white-space: pre-wrap;
font: 12px/1.5 "SFMono-Regular", Menlo, Monaco, Consolas, monospace;
}
.chat-input-container {
padding: 16px 20px 20px;
border-top: 1px solid var(--line, #3c3c3c);
@@ -3436,6 +3537,103 @@ button svg * {
font: 12px/1.5 "SFMono-Regular", Menlo, Monaco, Consolas, monospace;
}
.translation-validation-view,
.git-diff-view {
display: flex;
flex-direction: column;
gap: 16px;
min-height: 0;
}
.translation-validation-summary,
.translation-validation-section,
.git-diff-toolbar {
border: 1px solid var(--line, #3c3c3c);
border-radius: 12px;
background: var(--panel-2, #252526);
padding: 16px;
}
.translation-validation-summary p,
.git-diff-empty {
margin: 0;
color: var(--vscode-descriptionForeground);
}
.translation-validation-list {
display: grid;
gap: 12px;
margin-top: 12px;
}
.translation-validation-card {
border: 1px solid var(--line, #3c3c3c);
border-radius: 12px;
background: var(--panel-1, #1e1e1e);
padding: 16px;
}
.translation-validation-card-title {
margin: 0 0 12px;
font-weight: 600;
}
.translation-validation-card-meta {
margin: 0;
display: grid;
grid-template-columns: minmax(120px, 180px) minmax(0, 1fr);
gap: 8px 12px;
}
.translation-validation-card-meta dt {
color: var(--vscode-descriptionForeground);
}
.translation-validation-card-meta dd {
margin: 0;
word-break: break-word;
}
.translation-validation-actions {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.translation-validation-empty {
margin: 12px 0 0;
color: var(--vscode-descriptionForeground);
}
.git-diff-toolbar {
display: flex;
gap: 12px;
align-items: center;
flex-wrap: wrap;
}
.git-diff-toolbar label {
font-weight: 600;
}
.git-diff-toolbar select {
min-width: min(420px, 100%);
}
.git-diff-editor {
flex: 1;
min-height: 420px;
border: 1px solid var(--line, #3c3c3c);
border-radius: 12px;
overflow: hidden;
background: var(--panel-1, #1e1e1e);
}
.monaco-diff-editor-instance {
height: 100%;
min-height: 420px;
}
.linkish {
padding: 0;
border: none;

View File

@@ -238,6 +238,11 @@ document.addEventListener("DOMContentLoaded", () => {
document.head.appendChild(script);
});
const diffModelPath = (filePath, side) => {
const normalized = String(filePath || "working-tree").replace(/^\/+/, "");
return `inmemory://model/git-diff/${side}/${normalized}`;
};
const registerLiquidLanguage = (monaco) => {
if (liquidLanguageRegistered) {
return;
@@ -826,6 +831,134 @@ document.addEventListener("DOMContentLoaded", () => {
this.changeSubscription?.dispose();
this.editor?.dispose();
}
},
MonacoDiffEditor: {
mounted() {
this.host = this.el.querySelector(".monaco-diff-editor-instance");
this.originalInput = this.el.querySelector(".monaco-diff-original");
this.modifiedInput = this.el.querySelector(".monaco-diff-modified");
this.filePath = this.el.dataset.monacoDiffFilePath || "working-tree";
this.language = this.el.dataset.monacoDiffLanguage || "plaintext";
this.viewStyle = this.el.dataset.monacoDiffViewStyle || "inline";
this.wordWrap = this.el.dataset.monacoDiffWordWrap || "off";
this.hideUnchanged = this.el.dataset.monacoDiffHideUnchanged === "true";
this.readValues = () => ({
original: this.originalInput?.value || "",
modified: this.modifiedInput?.value || ""
});
this.applyDataset = () => {
this.filePath = this.el.dataset.monacoDiffFilePath || "working-tree";
this.language = this.el.dataset.monacoDiffLanguage || "plaintext";
this.viewStyle = this.el.dataset.monacoDiffViewStyle || "inline";
this.wordWrap = this.el.dataset.monacoDiffWordWrap || "off";
this.hideUnchanged = this.el.dataset.monacoDiffHideUnchanged === "true";
};
this.setModels = (monaco) => {
const values = this.readValues();
this.originalModel?.dispose();
this.modifiedModel?.dispose();
this.originalModel = monaco.editor.createModel(
values.original,
this.language,
monaco.Uri.parse(diffModelPath(this.filePath, "original"))
);
this.modifiedModel = monaco.editor.createModel(
values.modified,
this.language,
monaco.Uri.parse(diffModelPath(this.filePath, "modified"))
);
this.editor.setModel({ original: this.originalModel, modified: this.modifiedModel });
this.lastFilePath = this.filePath;
};
loadMonaco()
.then((monaco) => {
if (!this.host) {
return;
}
ensureMonacoTheme(monaco);
this.editor = monaco.editor.createDiffEditor(this.host, {
theme: "bds-theme",
automaticLayout: true,
readOnly: true,
renderSideBySide: this.viewStyle === "side-by-side",
minimap: { enabled: false },
scrollBeyondLastLine: false,
lineNumbers: "on",
diffCodeLens: false,
originalEditable: false,
wordWrap: this.wordWrap,
hideUnchangedRegions: { enabled: this.hideUnchanged },
ignoreTrimWhitespace: false
});
this.setModels(monaco);
})
.catch((error) => {
console.error("Failed to load Monaco diff editor", error);
});
},
updated() {
this.host = this.el.querySelector(".monaco-diff-editor-instance");
this.originalInput = this.el.querySelector(".monaco-diff-original");
this.modifiedInput = this.el.querySelector(".monaco-diff-modified");
this.applyDataset();
if (!this.editor) {
return;
}
loadMonaco().then((monaco) => {
ensureMonacoTheme(monaco);
monaco.editor.setTheme("bds-theme");
this.editor.updateOptions({
renderSideBySide: this.viewStyle === "side-by-side",
wordWrap: this.wordWrap,
hideUnchangedRegions: { enabled: this.hideUnchanged }
});
if (this.lastFilePath !== this.filePath) {
this.setModels(monaco);
return;
}
const values = this.readValues();
if (this.originalModel && this.originalModel.getLanguageId() !== this.language) {
monaco.editor.setModelLanguage(this.originalModel, this.language);
}
if (this.modifiedModel && this.modifiedModel.getLanguageId() !== this.language) {
monaco.editor.setModelLanguage(this.modifiedModel, this.language);
}
if (this.originalModel && this.originalModel.getValue() !== values.original) {
this.originalModel.setValue(values.original);
}
if (this.modifiedModel && this.modifiedModel.getValue() !== values.modified) {
this.modifiedModel.setValue(values.modified);
}
});
},
destroyed() {
this.originalModel?.dispose();
this.modifiedModel?.dispose();
this.editor?.dispose();
}
}
};