Enhancements for Clip Studio Assets
// ==UserScript==
// @name ecsa
// @name:en ecsa
// @name:ja ecsa
// @name:ko ecsa
// @namespace https://greasyfork.org/en/scripts/476919-ecsa
// @version 1.22
// @description Clip Studio Assets 素材商店強化工具
// @author boni1921
// @match https://assets.clip-studio.com/*/download-list
// @match https://assets.clip-studio.com/*/starred*
// @match https://assets.clip-studio.com/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_addStyle
// @grant GM_download
// @grant GM_setClipboard
// @grant GM_info
// @license GPL-3.0-only
// @icon https://www.google.com/s2/favicons?sz=64&domain=clip-studio.com
// @description:zh Clip Studio Assets 素材商店強化工具
// @description:en Enhancements for Clip Studio Assets
// @description:ja Clip Studio Assets 向け拡張機能
// @description:de Erweiterungen für Clip Studio Assets
// @description:es Mejoras para Clip Studio Assets
// @description:fr Améliorations pour Clip Studio Assets
// @description:ko Clip Studio Assets 개선 도구
// ==/UserScript==
(()=>{function V(e){let t=String(e||"").toLowerCase();return t.startsWith("zh")?"zh-CN":t.startsWith("de")?"de":t.startsWith("es")?"es":t.startsWith("fr")?"fr":t.startsWith("ja")?"ja":t.startsWith("ko")?"ko":t.startsWith("en")?"en":null}var o={messages:{en:{settingsTitle:"Script Settings",close:"Close",settingsButton:"ECSA Settings",openLinks:"Double-click the asset cover to open the asset link in a new tab",useSystemFont:"Use system font",enableAssetNotes:"Enable asset notes and not-interested marks",confirmBeforeDeleteNote:"Confirm before deleting a note",sortCategory:"Sort by Category",sortDefault:"Sort in Default Order",assetNotes:"Asset notes",notesLocal:"Notes are saved locally in this userscript.",backupNow:"Back up now",importBackup:"Import backup",lastBackup:"Last backup: {date}",versionLabel:"Version",updatedLabel:"Updated on this browser",unavailable:"Unavailable",changelog:"Changelog",hideChangelog:"Hide changelog",changelogUnreleased:"Unreleased",changelogV120Notes:"Add notes to assets.",changelogV120NotInterested:"Quick action to mark an asset as not interested when you have tried it and it is not a fit.",changelogV120Panel:"Resize and reposition the note panel.",changelogV120Backup:"Back up and restore notes.",changelogInitialRelease:"Initial release.",preparingBackup:"Preparing backup…",backupCompleted:"Backup completed: {filename}",backupFailed:"Backup failed. Please try again.",backupTimedOut:"Backup timed out. Please try again.",malformedBackup:"Unsupported or malformed backup format",invalidExportDate:"Invalid export date",invalidNoteRecord:"Invalid note record",invalidAssetId:"Invalid asset ID",duplicateAssetId:"Duplicate asset ID: {id}",invalidNoteText:"Invalid note text for asset {id}",invalidCreationDate:"Invalid creation date for asset {id}",invalidUpdateDate:"Invalid update date for asset {id}",importConfirm:"Import {count} notes? Notes with matching asset IDs will be replaced.",importCancelled:"Import cancelled.",importCompleted:"Imported {count} notes.",importFailed:"Import failed: {error}",noteTitle:"Asset note",resizePanel:"Resize note panel",copyNote:"Copy asset URL and note",assetId:"Asset ID: {id}",notePlaceholder:"Write notes about this asset…",delete:"Delete",save:"Save",savedLocally:"Saved locally",copied:"Copied to clipboard",copyFailed:"Copy failed: {error}",clipboardUnavailable:"Clipboard access is unavailable",saving:"Saving…",saveFailed:"Save failed: {error}",exitWithoutSaving:"Exit without saving?",deleteConfirm:"Delete this asset note?",deleteFailed:"Delete failed: {error}",addNote:"Add Notes",editNote:"Edit asset note",markReviewed:"Mark as not interested",markNotReviewed:"Clear not interested",reviewed:"Not interested",copiedAssetUrl:"Asset URL",copiedNotes:"Notes"},de:{settingsTitle:"Skript-Einstellungen",close:"Schließen",settingsButton:"ECSA-Einstellungen",openLinks:"Doppelklicken Sie auf das Asset-Cover, um den Asset-Link in einem neuen Tab zu öffnen",useSystemFont:"Systemschriftart verwenden",enableAssetNotes:"Asset-Notizen und „Nicht interessiert“-Markierungen aktivieren",confirmBeforeDeleteNote:"Vor dem Löschen einer Notiz bestätigen",sortCategory:"Nach Kategorie sortieren",sortDefault:"In Standardreihenfolge sortieren",assetNotes:"Asset-Notizen",notesLocal:"Notizen werden lokal in diesem Benutzerskript gespeichert.",backupNow:"Jetzt sichern",importBackup:"Sicherung importieren",lastBackup:"Letzte Sicherung: {date}",versionLabel:"Version",updatedLabel:"In diesem Browser aktualisiert",unavailable:"Nicht verfügbar",changelog:"Änderungsprotokoll",hideChangelog:"Änderungsprotokoll ausblenden",changelogUnreleased:"Unveröffentlicht",changelogV120Notes:"Notizen zu Assets hinzufügen.",changelogV120NotInterested:"Schnellaktion, um ein Asset als nicht interessiert zu markieren, wenn es nach dem Ausprobieren nicht passt.",changelogV120Panel:"Notizfenster in der Größe ändern und neu positionieren.",changelogV120Backup:"Notizen sichern und wiederherstellen.",changelogInitialRelease:"Erste Veröffentlichung.",preparingBackup:"Sicherung wird vorbereitet…",backupCompleted:"Sicherung abgeschlossen: {filename}",backupFailed:"Sicherung fehlgeschlagen. Bitte erneut versuchen.",backupTimedOut:"Zeitüberschreitung bei der Sicherung. Bitte erneut versuchen.",malformedBackup:"Nicht unterstütztes oder fehlerhaftes Sicherungsformat",invalidExportDate:"Ungültiges Exportdatum",invalidNoteRecord:"Ungültiger Notizeintrag",invalidAssetId:"Ungültige Asset-ID",duplicateAssetId:"Doppelte Asset-ID: {id}",invalidNoteText:"Ungültiger Notiztext für Asset {id}",invalidCreationDate:"Ungültiges Erstellungsdatum für Asset {id}",invalidUpdateDate:"Ungültiges Aktualisierungsdatum für Asset {id}",importConfirm:"{count} Notizen importieren? Notizen mit übereinstimmenden Asset-IDs werden ersetzt.",importCancelled:"Import abgebrochen.",importCompleted:"{count} Notizen importiert.",importFailed:"Import fehlgeschlagen: {error}",noteTitle:"Asset-Notiz",resizePanel:"Notizfenster vergrößern",copyNote:"Asset-URL und Notiz kopieren",assetId:"Asset-ID: {id}",notePlaceholder:"Notizen zu diesem Asset schreiben…",delete:"Löschen",save:"Speichern",savedLocally:"Lokal gespeichert",copied:"In die Zwischenablage kopiert",copyFailed:"Kopieren fehlgeschlagen: {error}",clipboardUnavailable:"Zugriff auf die Zwischenablage ist nicht verfügbar",saving:"Wird gespeichert…",saveFailed:"Speichern fehlgeschlagen: {error}",exitWithoutSaving:"Ohne Speichern beenden?",deleteConfirm:"Diese Asset-Notiz löschen?",deleteFailed:"Löschen fehlgeschlagen: {error}",addNote:"Notizen hinzufügen",editNote:"Asset-Notiz bearbeiten",markReviewed:"Als nicht interessiert markieren",markNotReviewed:"„Nicht interessiert“ entfernen",reviewed:"Nicht interessiert",copiedAssetUrl:"Asset-URL",copiedNotes:"Notizen"},es:{settingsTitle:"Configuración del script",close:"Cerrar",settingsButton:"Configuración de ECSA",openLinks:"Haz doble clic en la portada del recurso para abrir su enlace en una pestaña nueva",useSystemFont:"Usar la fuente del sistema",enableAssetNotes:"Activar notas del recurso y marcas de no me interesa",confirmBeforeDeleteNote:"Confirmar antes de eliminar una nota",sortCategory:"Ordenar por categoría",sortDefault:"Ordenar en el orden predeterminado",assetNotes:"Notas del recurso",notesLocal:"Las notas se guardan localmente en este script de usuario.",backupNow:"Crear copia ahora",importBackup:"Importar copia",lastBackup:"Última copia: {date}",versionLabel:"Versión",updatedLabel:"Actualizado en este navegador",unavailable:"No disponible",changelog:"Registro de cambios",hideChangelog:"Ocultar registro de cambios",changelogUnreleased:"Sin publicar",changelogV120Notes:"Añadir notas a los recursos.",changelogV120NotInterested:"Acción rápida para marcar un recurso como no me interesa cuando lo has probado y no encaja.",changelogV120Panel:"Cambiar el tamaño y la posición del panel de notas.",changelogV120Backup:"Copiar de seguridad y restaurar notas.",changelogInitialRelease:"Versión inicial.",preparingBackup:"Preparando copia de seguridad…",backupCompleted:"Copia de seguridad completada: {filename}",backupFailed:"Error al crear la copia. Inténtalo de nuevo.",backupTimedOut:"La copia agotó el tiempo de espera. Inténtalo de nuevo.",malformedBackup:"Formato de copia no compatible o incorrecto",invalidExportDate:"Fecha de exportación no válida",invalidNoteRecord:"Registro de nota no válido",invalidAssetId:"ID de recurso no válido",duplicateAssetId:"ID de recurso duplicado: {id}",invalidNoteText:"Texto de nota no válido para el recurso {id}",invalidCreationDate:"Fecha de creación no válida para el recurso {id}",invalidUpdateDate:"Fecha de actualización no válida para el recurso {id}",importConfirm:"¿Importar {count} notas? Se reemplazarán las notas con el mismo ID de recurso.",importCancelled:"Importación cancelada.",importCompleted:"Se importaron {count} notas.",importFailed:"Error de importación: {error}",noteTitle:"Nota del recurso",resizePanel:"Redimensionar panel de notas",copyNote:"Copiar URL y nota del recurso",assetId:"ID del recurso: {id}",notePlaceholder:"Escribe notas sobre este recurso…",delete:"Eliminar",save:"Guardar",savedLocally:"Guardado localmente",copied:"Copiado al portapapeles",copyFailed:"Error al copiar: {error}",clipboardUnavailable:"El acceso al portapapeles no está disponible",saving:"Guardando…",saveFailed:"Error al guardar: {error}",exitWithoutSaving:"¿Salir sin guardar?",deleteConfirm:"¿Eliminar esta nota del recurso?",deleteFailed:"Error al eliminar: {error}",addNote:"Añadir notas",editNote:"Editar nota del recurso",markReviewed:"Marcar como no me interesa",markNotReviewed:"Quitar no me interesa",reviewed:"No me interesa",copiedAssetUrl:"URL del recurso",copiedNotes:"Notas"},fr:{settingsTitle:"Paramètres du script",close:"Fermer",settingsButton:"Paramètres ECSA",openLinks:"Double-cliquez sur la couverture de la ressource pour ouvrir son lien dans un nouvel onglet",useSystemFont:"Utiliser la police système",enableAssetNotes:"Activer les notes de ressource et les marques « pas intéressé »",confirmBeforeDeleteNote:"Confirmer avant de supprimer une note",sortCategory:"Trier par catégorie",sortDefault:"Trier dans l’ordre par défaut",assetNotes:"Notes de la ressource",notesLocal:"Les notes sont enregistrées localement dans ce script utilisateur.",backupNow:"Sauvegarder maintenant",importBackup:"Importer une sauvegarde",lastBackup:"Dernière sauvegarde : {date}",versionLabel:"Version",updatedLabel:"Mis à jour dans ce navigateur",unavailable:"Indisponible",changelog:"Journal des modifications",hideChangelog:"Masquer le journal",changelogUnreleased:"Non publié",changelogV120Notes:"Ajouter des notes aux ressources.",changelogV120NotInterested:"Action rapide pour marquer une ressource comme pas intéressée lorsqu’elle ne convient pas après l’avoir essayée.",changelogV120Panel:"Redimensionner et repositionner le panneau de notes.",changelogV120Backup:"Sauvegarder et restaurer les notes.",changelogInitialRelease:"Version initiale.",preparingBackup:"Préparation de la sauvegarde…",backupCompleted:"Sauvegarde terminée : {filename}",backupFailed:"Échec de la sauvegarde. Réessayez.",backupTimedOut:"Délai de sauvegarde dépassé. Réessayez.",malformedBackup:"Format de sauvegarde non pris en charge ou incorrect",invalidExportDate:"Date d’exportation non valide",invalidNoteRecord:"Entrée de note non valide",invalidAssetId:"ID de ressource non valide",duplicateAssetId:"ID de ressource en double : {id}",invalidNoteText:"Texte de note non valide pour la ressource {id}",invalidCreationDate:"Date de création non valide pour la ressource {id}",invalidUpdateDate:"Date de mise à jour non valide pour la ressource {id}",importConfirm:"Importer {count} notes ? Les notes ayant le même ID de ressource seront remplacées.",importCancelled:"Importation annulée.",importCompleted:"{count} notes importées.",importFailed:"Échec de l’importation : {error}",noteTitle:"Note de la ressource",resizePanel:"Redimensionner le panneau de notes",copyNote:"Copier l’URL et la note de la ressource",assetId:"ID de la ressource : {id}",notePlaceholder:"Écrivez des notes sur cette ressource…",delete:"Supprimer",save:"Enregistrer",savedLocally:"Enregistré localement",copied:"Copié dans le presse-papiers",copyFailed:"Échec de la copie : {error}",clipboardUnavailable:"L’accès au presse-papiers est indisponible",saving:"Enregistrement…",saveFailed:"Échec de l’enregistrement : {error}",exitWithoutSaving:"Quitter sans enregistrer ?",deleteConfirm:"Supprimer cette note de ressource ?",deleteFailed:"Échec de la suppression : {error}",addNote:"Ajouter des notes",editNote:"Modifier la note de la ressource",markReviewed:"Marquer comme pas intéressé",markNotReviewed:"Retirer « pas intéressé »",reviewed:"Pas intéressé",copiedAssetUrl:"URL de la ressource",copiedNotes:"Notes"},ja:{settingsTitle:"スクリプト設定",close:"閉じる",settingsButton:"ECSA設定",openLinks:"素材のカバーをダブルクリックすると、新しいタブで素材リンクを開きます",useSystemFont:"システムフォントを使用",enableAssetNotes:"素材メモと「興味なし」マークを有効にする",confirmBeforeDeleteNote:"メモを削除する前に確認する",sortCategory:"素材タイプ別に並べ替え",sortDefault:"デフォルトの順序に戻す",assetNotes:"素材メモ",notesLocal:"メモはこのユーザースクリプト内にローカル保存されます。",backupNow:"今すぐバックアップ",importBackup:"バックアップをインポート",lastBackup:"最終バックアップ:{date}",versionLabel:"バージョン",updatedLabel:"このブラウザーでの更新日時",unavailable:"利用できません",changelog:"変更履歴",hideChangelog:"変更履歴を閉じる",changelogUnreleased:"未リリース",changelogV120Notes:"素材にメモを追加。",changelogV120NotInterested:"試して合わない素材を「興味なし」とすばやくマークする操作。",changelogV120Panel:"メモパネルのサイズ変更と位置の調整。",changelogV120Backup:"メモのバックアップと復元。",changelogInitialRelease:"初回リリース。",preparingBackup:"バックアップを準備しています…",backupCompleted:"バックアップが完了しました:{filename}",backupFailed:"バックアップに失敗しました。もう一度お試しください。",backupTimedOut:"バックアップがタイムアウトしました。もう一度お試しください。",malformedBackup:"未対応または不正なバックアップ形式です",invalidExportDate:"エクスポート日時が無効です",invalidNoteRecord:"メモのデータが無効です",invalidAssetId:"素材IDが無効です",duplicateAssetId:"素材IDが重複しています:{id}",invalidNoteText:"素材 {id} のメモ本文が無効です",invalidCreationDate:"素材 {id} の作成日時が無効です",invalidUpdateDate:"素材 {id} の更新日時が無効です",importConfirm:"{count}件のメモをインポートしますか?同じ素材IDのメモは置き換えられます。",importCancelled:"インポートをキャンセルしました。",importCompleted:"{count}件のメモをインポートしました。",importFailed:"インポートに失敗しました:{error}",noteTitle:"素材メモ",resizePanel:"メモパネルのサイズを変更",copyNote:"素材URLとメモをコピー",assetId:"素材ID:{id}",notePlaceholder:"この素材についてメモを書く…",delete:"削除",save:"保存",savedLocally:"ローカルに保存しました",copied:"クリップボードにコピーしました",copyFailed:"コピーに失敗しました:{error}",clipboardUnavailable:"クリップボードを利用できません",saving:"保存中…",saveFailed:"保存に失敗しました:{error}",exitWithoutSaving:"保存せずに終了しますか?",deleteConfirm:"この素材メモを削除しますか?",deleteFailed:"削除に失敗しました:{error}",addNote:"メモを追加",editNote:"素材メモを編集",markReviewed:"興味なしにする",markNotReviewed:"興味なしを解除",reviewed:"興味なし",copiedAssetUrl:"素材URL",copiedNotes:"メモ"},ko:{settingsTitle:"스크립트 설정",close:"닫기",settingsButton:"ECSA 설정",openLinks:"에셋 표지를 두 번 클릭하면 새 탭에서 에셋 링크를 엽니다",useSystemFont:"시스템 글꼴 사용",enableAssetNotes:"에셋 메모와 관심 없음 표시 사용",confirmBeforeDeleteNote:"메모 삭제 전 확인",sortCategory:"카테고리별 정렬",sortDefault:"기본 순서로 정렬",assetNotes:"에셋 메모",notesLocal:"메모는 이 유저스크립트에 로컬로 저장됩니다.",backupNow:"지금 백업",importBackup:"백업 가져오기",lastBackup:"마지막 백업: {date}",versionLabel:"버전",updatedLabel:"이 브라우저에서 업데이트됨",unavailable:"사용할 수 없음",changelog:"변경 기록",hideChangelog:"변경 기록 숨기기",changelogUnreleased:"출시 예정",changelogV120Notes:"에셋에 메모 추가.",changelogV120NotInterested:"사용해 보았는데 맞지 않을 때 에셋을 관심 없음으로 빠르게 표시.",changelogV120Panel:"메모 패널 크기 조절 및 위치 변경.",changelogV120Backup:"메모 백업 및 복원.",changelogInitialRelease:"최초 릴리스.",preparingBackup:"백업 준비 중…",backupCompleted:"백업 완료: {filename}",backupFailed:"백업에 실패했습니다. 다시 시도하세요.",backupTimedOut:"백업 시간이 초과되었습니다. 다시 시도하세요.",malformedBackup:"지원되지 않거나 잘못된 백업 형식",invalidExportDate:"내보내기 날짜가 잘못되었습니다",invalidNoteRecord:"메모 레코드가 잘못되었습니다",invalidAssetId:"에셋 ID가 잘못되었습니다",duplicateAssetId:"중복된 에셋 ID: {id}",invalidNoteText:"에셋 {id}의 메모 내용이 잘못되었습니다",invalidCreationDate:"에셋 {id}의 생성 날짜가 잘못되었습니다",invalidUpdateDate:"에셋 {id}의 업데이트 날짜가 잘못되었습니다",importConfirm:"메모 {count}개를 가져오시겠습니까? 같은 에셋 ID의 메모는 교체됩니다.",importCancelled:"가져오기가 취소되었습니다.",importCompleted:"메모 {count}개를 가져왔습니다.",importFailed:"가져오기 실패: {error}",noteTitle:"에셋 메모",resizePanel:"메모 패널 크기 조절",copyNote:"에셋 URL과 메모 복사",assetId:"에셋 ID: {id}",notePlaceholder:"이 에셋에 대한 메모를 작성하세요…",delete:"삭제",save:"저장",savedLocally:"로컬에 저장됨",copied:"클립보드에 복사됨",copyFailed:"복사 실패: {error}",clipboardUnavailable:"클립보드에 접근할 수 없습니다",saving:"저장 중…",saveFailed:"저장 실패: {error}",exitWithoutSaving:"저장하지 않고 종료하시겠습니까?",deleteConfirm:"이 에셋 메모를 삭제하시겠습니까?",deleteFailed:"삭제 실패: {error}",addNote:"메모 추가",editNote:"에셋 메모 편집",markReviewed:"관심 없음으로 표시",markNotReviewed:"관심 없음 해제",reviewed:"관심 없음",copiedAssetUrl:"에셋 URL",copiedNotes:"메모"},"zh-CN":{settingsTitle:"脚本设置",close:"关闭",settingsButton:"ECSA 设置",openLinks:"双击素材封面时,在新标签页中打开素材链接",useSystemFont:"使用系统字体",enableAssetNotes:"启用素材笔记和不感兴趣标记",confirmBeforeDeleteNote:"删除笔记前确认",sortCategory:"按素材类型排序",sortDefault:"按默认顺序排序",assetNotes:"素材笔记",notesLocal:"笔记保存在此用户脚本的本地存储中。",backupNow:"立即备份",importBackup:"导入备份",lastBackup:"上次备份:{date}",versionLabel:"版本",updatedLabel:"在此浏览器中的更新时间",unavailable:"不可用",changelog:"更新日志",hideChangelog:"收起更新日志",changelogUnreleased:"尚未发布",changelogV120Notes:"为素材添加笔记。",changelogV120NotInterested:"快捷将素材标为不感兴趣(试用后觉得不合适时)。",changelogV120Panel:"调整笔记面板大小与位置。",changelogV120Backup:"备份与恢复笔记。",changelogInitialRelease:"首次发布。",preparingBackup:"正在准备备份…",backupCompleted:"备份完成:{filename}",backupFailed:"备份失败,请重试。",backupTimedOut:"备份超时,请重试。",malformedBackup:"不支持或格式错误的备份文件",invalidExportDate:"导出日期无效",invalidNoteRecord:"笔记记录无效",invalidAssetId:"素材 ID 无效",duplicateAssetId:"素材 ID 重复:{id}",invalidNoteText:"素材 {id} 的笔记内容无效",invalidCreationDate:"素材 {id} 的创建日期无效",invalidUpdateDate:"素材 {id} 的更新日期无效",importConfirm:"导入 {count} 条笔记?素材 ID 相同的笔记将被替换。",importCancelled:"已取消导入。",importCompleted:"已导入 {count} 条笔记。",importFailed:"导入失败:{error}",noteTitle:"素材笔记",resizePanel:"调整笔记面板大小",copyNote:"复制素材网址和笔记",assetId:"素材 ID:{id}",notePlaceholder:"写下关于此素材的笔记…",delete:"删除",save:"保存",savedLocally:"已保存到本地",copied:"已复制到剪贴板",copyFailed:"复制失败:{error}",clipboardUnavailable:"无法访问剪贴板",saving:"正在保存…",saveFailed:"保存失败:{error}",exitWithoutSaving:"不保存并退出?",deleteConfirm:"删除此素材笔记?",deleteFailed:"删除失败:{error}",addNote:"添加笔记",editNote:"编辑素材笔记",markReviewed:"标记为不感兴趣",markNotReviewed:"取消不感兴趣",reviewed:"不感兴趣",copiedAssetUrl:"素材网址",copiedNotes:"笔记"}},normalizeLocale(e){return V(e)},detectLocale(){let e=window.location.pathname.split("/")[1];return this.normalizeLocale(e)||this.normalizeLocale(document.documentElement.lang)||this.normalizeLocale(navigator.language)||"en"},init(){this.locale=this.detectLocale()},t(e,t={}){return(this.messages[this.locale]?.[e]??this.messages.en[e]??e).replace(/\{(\w+)\}/g,(i,n)=>Object.prototype.hasOwnProperty.call(t,n)?String(t[n]):i)},formatDate(e){let t=new Date(e);if(Number.isNaN(t.getTime()))return this.t("unavailable");try{return new Intl.DateTimeFormat(this.locale,{dateStyle:"medium",timeStyle:"short"}).format(t)}catch{return t.toLocaleString()}}};o.init();var S={init(){let e=typeof GM_info<"u"?GM_info:null;this.version=e?.script?.version||null},getVersion(){return this.version||o.t("unavailable")}};S.init();var E=[{version:"1.20",date:"2026-08-07",items:["changelogV120Notes","changelogV120NotInterested","changelogV120Panel","changelogV120Backup"]},{version:"1.0",date:"2023-10-07",items:["changelogInitialRelease"]}];var y={settings:{openInNewTab:{type:"checkbox",label:"openLinks",default:!1},useSystemFont:{type:"checkbox",label:"useSystemFont",default:!1},enableAssetNotes:{type:"checkbox",label:"enableAssetNotes",default:!0},confirmBeforeDeleteNote:{type:"checkbox",label:"confirmBeforeDeleteNote",default:!0}},init(){this.loadSettings(),this.createPanel(),this.addStyles(),this.setupWrenchButton(),this.applySettings()},loadSettings(){this.values={};for(let[e,t]of Object.entries(this.settings))this.values[e]=GM_getValue(e,t.default)},saveSetting(e,t){GM_setValue(e,t),this.values[e]=t,this.applySettings()},applySettings(){this.values.useSystemFont?document.body.classList.add("ecsa-system-font"):document.body.classList.remove("ecsa-system-font"),document.body.classList.toggle("ecsa-notes-disabled",!this.values.enableAssetNotes),this.backupSection&&(this.backupSection.hidden=!this.values.enableAssetNotes),typeof document<"u"&&typeof document.dispatchEvent=="function"&&typeof CustomEvent=="function"&&document.dispatchEvent(new CustomEvent("ecsa:notes-setting-changed",{detail:{enabled:this.values.enableAssetNotes!==!1}}))},isNotesEnabled(){return this.values?.enableAssetNotes!==!1},isDeleteConfirmEnabled(){return this.values?.confirmBeforeDeleteNote!==!1},createPanel(){this.overlay=document.createElement("div"),this.overlay.className="ecsa-settings-overlay",this.panel=document.createElement("div"),this.panel.className="ecsa-settings-panel",this.panel.innerHTML=`
<div class="ecsa-panel-header">
<h4>${o.t("settingsTitle")}</h4>
<button type="button" class="ecsa-close-btn close" aria-label="${o.t("close")}">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="ecsa-panel-content">
${Object.entries(this.settings).map(([a,i])=>`
<label class="setting-item">
<input type="${i.type}"
data-key="${a}"
${this.values[a]?"checked":""}>
${o.t(i.label)}
</label>
`).join("")}
<section class="ecsa-backup-settings"${this.values.enableAssetNotes?"":" hidden"}>
<h5>${o.t("assetNotes")}</h5>
<p>${o.t("notesLocal")}</p>
<div class="ecsa-backup-actions">
<button type="button" class="btn btn-primary ecsa-backup-export">
${o.t("backupNow")}
</button>
<button type="button" class="btn btn-default ecsa-backup-import">
${o.t("importBackup")}
</button>
<input class="ecsa-backup-file" type="file" accept="application/json,.json" hidden>
</div>
<p class="ecsa-backup-status" role="status" aria-live="polite" hidden></p>
</section>
<div class="ecsa-script-info">
<span>${o.t("versionLabel")}: ${S.getVersion()}</span>
<button type="button" class="btn btn-link btn-xs ecsa-changelog-toggle" aria-expanded="false">
${o.t("changelog")}
</button>
</div>
<section class="ecsa-changelog" hidden>
${E.map(a=>`
<article class="ecsa-changelog-release">
<h6>
${a.titleKey?o.t(a.titleKey):a.version}
${a.date?`<time datetime="${a.date}">${a.date}</time>`:""}
</h6>
<ul>
${a.items.map(i=>`<li>${o.t(i)}</li>`).join("")}
</ul>
</article>
`).join("")}
</section>
</div>
`,this.backupSection=this.panel.querySelector(".ecsa-backup-settings"),this.panel.querySelector(".ecsa-close-btn").addEventListener("click",()=>this.hidePanel()),this.overlay.addEventListener("click",()=>this.hidePanel());let e=this.panel.querySelector(".ecsa-changelog-toggle"),t=this.panel.querySelector(".ecsa-changelog");e.addEventListener("click",()=>{let a=e.getAttribute("aria-expanded")==="true";e.setAttribute("aria-expanded",String(!a)),e.textContent=o.t(a?"changelog":"hideChangelog"),t.hidden=a}),document.body.appendChild(this.overlay),document.body.appendChild(this.panel),this.panel.querySelectorAll("input[data-key]").forEach(a=>{a.addEventListener("change",i=>{let n=i.target.type==="checkbox"?i.target.checked:i.target.value;this.saveSetting(i.target.dataset.key,n)})})},setupWrenchButton(){let e=document.createElement("a");e.className="btn btn-default header__star hidden-xs",e.innerHTML='<i class="fa fa-cog" aria-hidden="true"></i>',e.title=o.t("settingsButton");let t=document.querySelector(".header__star");t&&(t.insertAdjacentElement("beforebegin",e),e.addEventListener("click",a=>{a.preventDefault(),this.togglePanel()}))},togglePanel(){this.panel.style.display=this.panel.style.display==="block"?"none":"block",this.overlay.style.display=this.panel.style.display},hidePanel(){this.panel.style.display="none",this.overlay.style.display="none"},addStyles(){GM_addStyle(`
.translationButtonContainer {
width: fit-content;
}
header .header__notification, header .header__star {
padding: 0px 4px;
}
.customDropdown {
min-width:180px !important;
}
/* System font styles */
.ecsa-system-font {
font-family: system-ui, -apple-system, sans-serif !important;
}
/* Always use system font for settings panel */
.ecsa-settings-panel {
font-family: system-ui, -apple-system, sans-serif !important;
}
.ecsa-close-btn.close {
font-size: 30px;
}
.ecsa-settings-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
z-index: 9999;
display: none;
}
.ecsa-settings-panel {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 8px 16px;
border-radius: 8px;
box-shadow: 0 0 20px rgba(0,0,0,0.2);
z-index: 10000;
width: 80%;
max-width:500px;
max-height: calc(100vh - 40px);
overflow-y: auto;
box-sizing: border-box;
display: none;
}
.ecsa-panel-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
.setting-item {
display: block;
margin: 0;
padding: 0;
cursor: pointer;
}
.header__wrench {
margin-right: 10px;
color: #666;
padding: 6px 12px;
transition: opacity 0.2s;
}
.header__wrench:hover {
color: #333;
background-color: #e6e6e6;
}
body.ecsa-notes-disabled .ecsa-note-button-item,
body.ecsa-notes-disabled .ecsa-review-button-item,
body.ecsa-notes-disabled .ecsa-note-button,
body.ecsa-notes-disabled .ecsa-review-button,
body.ecsa-notes-disabled .ecsa-card-indicators,
body.ecsa-notes-disabled .ecsa-note-overlay {
display: none !important;
}
.ecsa-note-button {
position: relative;
}
.ecsa-note-button.operationButton.inactive:focus,
.ecsa-note-button.operationButton.inactive:active,
.ecsa-review-button.operationButton.inactive:focus,
.ecsa-review-button.operationButton.inactive:active {
background-color: #fff;
background-image: none;
border-color: #ddd;
box-shadow: none;
}
.ecsa-note-button:hover,
.ecsa-note-button:focus {
outline: none;
}
.ecsa-note-button .ecsa-note-icon.is-active {
color: #0072b2;
}
.ecsa-review-button {
position: relative;
}
.ecsa-review-button .ecsa-review-icon.is-active {
color: #e69f00;
}
.ecsa-note-button[data-note-tooltip]:hover::after,
.ecsa-note-button[data-note-tooltip]:focus-visible::after,
.ecsa-review-button[data-note-tooltip]:hover::after,
.ecsa-review-button[data-note-tooltip]:focus-visible::after,
.ecsa-card-indicator[data-note-tooltip]:hover::after,
.ecsa-card-indicator[data-note-tooltip]:focus::after {
position: absolute;
top: calc(100% + 8px);
right: 0;
z-index: 10002;
width: max-content;
max-width: min(320px, calc(100vw - 32px));
padding: 8px 10px;
border-radius: 5px;
background: #333;
color: #fff;
content: attr(data-note-tooltip);
font-size: 13px;
font-weight: normal;
line-height: 1.4;
overflow-wrap: anywhere;
text-align: left;
white-space: pre-wrap;
pointer-events: none;
}
.materialCard__thumbmailBlock {
position: relative;
}
.ecsa-card-indicators {
position: absolute;
top: 6px;
left: 6px;
z-index: 5;
display: flex;
gap: 4px;
pointer-events: none;
}
.ecsa-card-indicator {
position: relative;
display: inline-flex;
width: 24px;
height: 24px;
align-items: center;
justify-content: center;
border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 50%;
background: rgba(255, 255, 255, 0.94);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
color: #0072b2;
font-size: 13px;
pointer-events: auto;
}
.ecsa-card-reviewed-indicator {
color: #e69f00;
}
.ecsa-card-indicator[data-note-tooltip]::after {
right: auto;
left: 0;
}
.ecsa-note-overlay {
position: fixed;
z-index: 10001;
display: none;
pointer-events: none;
}
.ecsa-note-overlay.is-open {
display: flex;
overflow: visible;
}
.ecsa-note-overlay.is-dragging,
.ecsa-note-overlay.is-resizing {
user-select: none;
}
.ecsa-note-dialog {
position: relative;
display: flex;
flex: 1 1 auto;
flex-direction: column;
align-items: stretch;
width: 100%;
height: 100%;
min-width: 0;
min-height: 0;
margin: 0;
overflow: hidden;
padding: 0 18px 22px;
border-radius: 8px;
background: #fff;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.25);
box-sizing: border-box;
color: #333;
font-family: system-ui, -apple-system, sans-serif;
pointer-events: auto;
}
.ecsa-note-header {
display: flex;
flex-shrink: 0;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
margin: 0 -18px 12px;
padding: 18px 18px 0;
cursor: move;
touch-action: none;
}
.ecsa-note-header button {
cursor: pointer;
}
.ecsa-note-resize-handle {
position: absolute;
right: 4px;
bottom: 4px;
width: 14px;
height: 14px;
cursor: nwse-resize;
opacity: 0.45;
}
.ecsa-note-resize-handle::before {
content: '';
position: absolute;
right: 2px;
bottom: 2px;
width: 10px;
height: 10px;
border-right: 2px solid #999;
border-bottom: 2px solid #999;
}
.ecsa-note-resize-handle:hover {
opacity: 0.85;
}
.ecsa-note-dialog h2 {
margin: 0;
font-size: 20px;
}
.ecsa-note-title-group {
display: flex;
align-items: center;
gap: 8px;
}
.ecsa-note-copy {
padding: 2px 7px;
border-radius: 4px;
}
.ecsa-note-close {
display: inline-flex;
width: 32px;
height: 32px;
padding: 0;
align-items: center;
justify-content: center;
border: 0;
border-radius: 50%;
background: transparent;
color: #777;
font-size: 28px;
line-height: 1;
}
.ecsa-note-context {
flex-shrink: 0;
margin: -6px 0 10px;
color: #777;
font-size: 12px;
}
.ecsa-note-textarea {
flex: 1 1 0;
width: 100%;
min-width: 0;
min-height: 0;
max-width: none;
margin: 0;
padding: 10px;
overflow: auto;
resize: none;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
font: inherit;
}
.ecsa-note-actions {
display: flex;
flex-shrink: 0;
gap: 8px;
justify-content: flex-end;
margin-top: 12px;
}
.ecsa-note-delete {
margin-right: auto;
}
.ecsa-note-delete,
.ecsa-note-save {
border-radius: 6px !important;
}
.ecsa-note-status {
flex-shrink: 0;
min-height: 20px;
margin: 6px 0 0;
color: #666;
font-size: 12px;
}
.ecsa-note-status.is-error,
.ecsa-backup-status.is-error {
color: #a94442;
}
.ecsa-backup-settings {
margin-top: 16px;
padding-top: 12px;
border-top: 1px solid #eee;
}
.ecsa-backup-settings h5 {
margin: 0 0 6px;
font-weight: 600;
}
.ecsa-backup-settings p {
margin: 0 0 5px;
color: #666;
font-size: 13px;
}
.ecsa-backup-actions {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.ecsa-backup-file {
display: none !important;
}
.ecsa-backup-status[hidden] {
display: none !important;
}
.ecsa-backup-settings .ecsa-backup-status {
margin-top: 12px;
font-size: 12px;
color: #666;
}
.ecsa-script-info {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px 16px;
margin-top: 16px;
padding-top: 12px;
border-top: 1px solid #eee;
color: #777;
font-size: 12px;
}
.ecsa-changelog-toggle {
padding: 0;
font-size: 12px;
}
.ecsa-changelog[hidden] {
display: none !important;
}
.ecsa-changelog {
margin-top: 10px;
padding: 10px 12px;
border: 1px solid #e5e5e5;
border-radius: 6px;
background: #f8f9fa;
color: #444;
font-size: 12px;
}
.ecsa-changelog-release + .ecsa-changelog-release {
margin-top: 12px;
padding-top: 10px;
border-top: 1px solid #ddd;
}
.ecsa-changelog-release h6 {
margin: 0 0 6px;
font-size: 13px;
font-weight: 600;
}
.ecsa-changelog-release time {
margin-left: 6px;
color: #777;
font-weight: normal;
}
.ecsa-changelog-release ul {
margin: 0;
padding-left: 18px;
}
.ecsa-changelog-release li + li {
margin-top: 3px;
}
@media (max-width: 600px) {
.ecsa-note-dialog {
padding: 0 14px 14px;
}
.ecsa-note-header {
margin: 0 -14px 12px;
padding: 14px 14px 0;
}
}
`)}};var g={storageKey:"ecsa.notes.v1",emptyStore(){return{schemaVersion:1,notes:{}}},normalizeId(e){let t=String(e||"");return/^\d+$/.test(t)?t.replace(/^0+(?=\d)/,""):null},load(){let e=GM_getValue(this.storageKey,this.emptyStore());return!e||e.schemaVersion!==1||!e.notes||typeof e.notes!="object"?this.emptyStore():e},save(e){GM_setValue(this.storageKey,e),typeof document<"u"&&typeof document.dispatchEvent=="function"&&typeof CustomEvent=="function"&&document.dispatchEvent(new CustomEvent("ecsa:notes-changed"))},get(e){let t=this.normalizeId(e);return t&&this.load().notes[t]||null},set(e,t){let a=this.normalizeId(e);if(!a)throw new Error(o.t("invalidAssetId"));let i=this.load(),n=i.notes[a],s=new Date().toISOString(),l=n?.reviewed===!0;t.trim()||l?i.notes[a]={id:a,text:t,reviewed:l,createdAt:n?.createdAt||s,updatedAt:s}:delete i.notes[a],this.save(i)},setReviewed(e,t){let a=this.normalizeId(e);if(!a)throw new Error(o.t("invalidAssetId"));let i=this.load(),n=i.notes[a],s=typeof n?.text=="string"?n.text:"",l=new Date().toISOString();t||s.trim()?i.notes[a]={id:a,text:s,reviewed:t,createdAt:n?.createdAt||l,updatedAt:l}:delete i.notes[a],this.save(i)},migrateLegacy(e){let t=this.normalizeId(e);if(!t||this.get(t))return;let a=`ecsa.assetNote.${t}`,i=GM_getValue(a,"");typeof i=="string"&&i.trim()&&(this.set(t,i),GM_setValue(a,""))},all(){return Object.values(this.load().notes).sort((e,t)=>e.id.localeCompare(t.id))},import(e){let t=this.load();e.forEach(a=>{t.notes[a.id]={...a}}),this.save(t)}},A={init(){this.exportButton=document.querySelector(".ecsa-backup-export"),this.importButton=document.querySelector(".ecsa-backup-import"),this.fileInput=document.querySelector(".ecsa-backup-file"),this.status=document.querySelector(".ecsa-backup-status"),!(!this.exportButton||!this.importButton||!this.fileInput||!this.status)&&(this.exportButton.addEventListener("click",()=>this.export()),this.importButton.addEventListener("click",()=>this.fileInput.click()),this.fileInput.addEventListener("change",()=>this.importFile()),this.refreshLastBackupStatus())},getLastBackupAt(){let e=GM_getValue("ecsa.lastBackupAt",null);return!e||Number.isNaN(Date.parse(e))?null:e},refreshLastBackupStatus(){if(!this.status)return;let e=this.getLastBackupAt();e?(this.status.hidden=!1,this.status.textContent=o.t("lastBackup",{date:o.formatDate(e)}),this.status.classList.remove("is-error")):(this.status.hidden=!0,this.status.textContent="",this.status.classList.remove("is-error"))},setStatus(e,t=!1,a={}){if(this.status){if(clearTimeout(this.statusTimer),!e){this.refreshLastBackupStatus();return}this.status.hidden=!1,this.status.textContent=e,this.status.classList.toggle("is-error",t),a.transient&&(this.statusTimer=setTimeout(()=>this.refreshLastBackupStatus(),4e3))}},downloadViaBrowser(e,t){let a=new Blob([e],{type:"application/json;charset=utf-8"}),i=URL.createObjectURL(a);try{let n=document.createElement("a");n.href=i,n.download=t,n.rel="noopener",document.body.appendChild(n),n.click(),n.remove()}finally{URL.revokeObjectURL(i)}},export(){let e=new Date().toISOString(),t={formatVersion:1,exportedAt:e,notes:g.all()},a=JSON.stringify(t,null,2),i=`ecsa-notes-${e.slice(0,10)}.json`;this.setStatus(o.t("preparingBackup"));let n=()=>{GM_setValue("ecsa.lastBackupAt",e),this.refreshLastBackupStatus()};try{if(typeof GM_download=="function"){let s=`data:application/json;charset=utf-8,${encodeURIComponent(a)}`;GM_download({url:s,name:i,saveAs:!0,onload:n,onerror:()=>this.setStatus(o.t("backupFailed"),!0),ontimeout:()=>this.setStatus(o.t("backupTimedOut"),!0)});return}this.downloadViaBrowser(a,i),n()}catch{this.setStatus(o.t("backupFailed"),!0)}},validate(e){if(!e||e.formatVersion!==1||!Array.isArray(e.notes))throw new Error(o.t("malformedBackup"));if(typeof e.exportedAt!="string"||Number.isNaN(Date.parse(e.exportedAt)))throw new Error(o.t("invalidExportDate"));let t=new Set;return e.notes.map(a=>{if(!a||typeof a!="object")throw new Error(o.t("invalidNoteRecord"));let i=g.normalizeId(a.id);if(!i||i!==String(a.id))throw new Error(o.t("invalidAssetId"));if(t.has(i))throw new Error(o.t("duplicateAssetId",{id:i}));if(typeof a.text!="string")throw new Error(o.t("invalidNoteText",{id:i}));if(a.reviewed!==void 0&&typeof a.reviewed!="boolean")throw new Error(o.t("invalidNoteRecord"));if(typeof a.createdAt!="string"||Number.isNaN(Date.parse(a.createdAt)))throw new Error(o.t("invalidCreationDate",{id:i}));if(typeof a.updatedAt!="string"||Number.isNaN(Date.parse(a.updatedAt)))throw new Error(o.t("invalidUpdateDate",{id:i}));return t.add(i),{id:i,text:a.text,reviewed:a.reviewed===!0,createdAt:a.createdAt,updatedAt:a.updatedAt}})},async importFile(){let e=this.fileInput.files?.[0];if(e)try{let t=JSON.parse(await e.text()),a=this.validate(t);if(!window.confirm(o.t("importConfirm",{count:a.length}))){this.setStatus(o.t("importCancelled"),!1,{transient:!0});return}g.import(a),C.refresh(),this.setStatus(o.t("importCompleted",{count:a.length}),!1,{transient:!0})}catch(t){this.setStatus(o.t("importFailed",{error:t.message}),!0)}finally{this.fileInput.value=""}}},C={init(){let e=g.normalizeId(new URLSearchParams(window.location.search).get("id"));if(!/^\/[^/]+\/detail\/?$/.test(window.location.pathname)||!e)return;this.assetId=e,g.migrateLegacy(e);let a=()=>{if(document.querySelector(".ecsa-note-button"))return;let i=document.querySelector(".favoriteButtons"),n=document.querySelector(".materialHeaderContentsId");!i&&!n||(this.createButton(i,n),this.createDialog())};a(),window.addEventListener("load",a,{once:!0}),window.addEventListener("resize",()=>this.scheduleLayoutClamp()),document.addEventListener("ecsa:notes-setting-changed",i=>{i.detail?.enabled===!1&&this.overlay?.classList.contains("is-open")&&this.closeWithoutPrompt()}),document.addEventListener("keydown",i=>{i.key==="Escape"&&this.overlay?.classList.contains("is-open")&&this.requestClose()})},getNote(){let e=g.get(this.assetId)?.text;return typeof e=="string"?e:""},createButton(e,t){if(this.button=document.createElement("button"),this.button.type="button",this.button.className="btn btn-default operationButton starButton inactive ecsa-note-button",this.button.innerHTML='<i class="fa fa-pencil ecsa-note-icon" aria-hidden="true"></i>',this.button.addEventListener("mousedown",a=>{a.button===0&&a.preventDefault()}),this.button.addEventListener("click",()=>this.open()),this.reviewButton=document.createElement("button"),this.reviewButton.type="button",this.reviewButton.className="btn btn-default operationButton starButton inactive ecsa-review-button",this.reviewButton.innerHTML='<i class="fa fa-ban ecsa-review-icon" aria-hidden="true"></i>',this.reviewButton.addEventListener("mousedown",a=>{a.button===0&&a.preventDefault()}),this.reviewButton.addEventListener("click",()=>this.toggleReviewed()),e){let a=document.createElement("li");a.className="ecsa-note-button-item",a.appendChild(this.button),e.appendChild(a);let i=document.createElement("li");i.className="ecsa-review-button-item",i.appendChild(this.reviewButton),e.appendChild(i)}else t.appendChild(this.button),t.appendChild(this.reviewButton);this.updateButton()},createDialog(){this.overlay=document.createElement("div"),this.overlay.className="ecsa-note-overlay",this.overlay.innerHTML=`
<section class="ecsa-note-dialog" role="dialog" aria-modal="true" aria-labelledby="ecsa-note-title">
<div class="ecsa-note-header">
<div class="ecsa-note-title-group">
<h2 id="ecsa-note-title">${o.t("noteTitle")}</h2>
<button type="button" class="btn btn-default btn-xs ecsa-note-copy" title="${o.t("copyNote")}" aria-label="${o.t("copyNote")}">
<i class="fa fa-copy" aria-hidden="true"></i>
</button>
</div>
<button type="button" class="ecsa-note-close" aria-label="${o.t("close")}">×</button>
</div>
<p class="ecsa-note-context">${o.t("assetId",{id:this.assetId})}</p>
<textarea class="ecsa-note-textarea"></textarea>
<p class="ecsa-note-status" role="status" aria-live="polite"></p>
<div class="ecsa-note-actions">
<button type="button" class="btn btn-danger ecsa-note-delete">${o.t("delete")}</button>
<button type="button" class="btn btn-primary ecsa-note-save">${o.t("save")}</button>
</div>
<div class="ecsa-note-resize-handle" title="${o.t("resizePanel")}" aria-hidden="true"></div>
</section>
`,this.dialog=this.overlay.querySelector(".ecsa-note-dialog"),this.textarea=this.overlay.querySelector(".ecsa-note-textarea"),this.status=this.overlay.querySelector(".ecsa-note-status"),this.deleteButton=this.overlay.querySelector(".ecsa-note-delete"),this.textarea.addEventListener("input",()=>this.scheduleSave()),this.overlay.querySelector(".ecsa-note-copy").addEventListener("click",()=>this.copy()),this.overlay.querySelector(".ecsa-note-close").addEventListener("click",()=>this.closeWithoutPrompt()),this.overlay.querySelector(".ecsa-note-save").addEventListener("click",()=>this.saveAndClose()),this.deleteButton.addEventListener("click",()=>this.delete()),this.overlay.addEventListener("click",e=>{e.target===this.overlay&&this.requestClose()}),this.setupPanelInteraction(),document.body.appendChild(this.overlay)},getPanelDefaultInset(){return window.innerWidth<=600?8:16},clampPanelLayout(e){let i=window.innerWidth,n=window.innerHeight,s=Math.min(Math.max(e.width,280),i),l=Math.min(Math.max(e.height,240),n),m=Math.min(Math.max(e.left,0),window.innerWidth-s),h=Math.min(Math.max(e.top,0),window.innerHeight-l);return{left:m,top:h,width:s,height:l}},loadPanelLayout(){let e=GM_getValue("ecsa.notePanelLayout",null);if(!e){let t=GM_getValue("ecsa.notePanelSize",null);t?.width&&t?.height&&(e={width:t.width,height:t.height})}return e},savePanelLayout(e){GM_setValue("ecsa.notePanelLayout",e)},computeDefaultPanelLayout(){let e=this.getPanelDefaultInset(),t=Math.min(520,window.innerWidth-e*2),a=Math.min(480,window.innerHeight-e*2),i=window.innerWidth-e-t,n=e,s=i+t;return document.querySelectorAll("header, .layout__materialHeader").forEach(m=>{let h=m.getBoundingClientRect(),f=h.width>0&&h.height>0&&h.bottom>e&&h.top<window.innerHeight,c=h.right>i&&h.left<s;f&&c&&(n=Math.max(n,Math.ceil(h.bottom+e)))}),this.clampPanelLayout({left:i,top:n,width:t,height:a})},applyPanelLayout(e){if(!this.overlay)return;let t=this.clampPanelLayout(e);return this.overlay.style.left=`${Math.round(t.left)}px`,this.overlay.style.top=`${Math.round(t.top)}px`,this.overlay.style.width=`${Math.round(t.width)}px`,this.overlay.style.height=`${Math.round(t.height)}px`,this.overlay.style.right="auto",t},getCurrentPanelLayout(){let e=this.overlay.getBoundingClientRect();return{left:e.left,top:e.top,width:e.width,height:e.height}},setupPanelInteraction(){let e=this.overlay.querySelector(".ecsa-note-header"),t=this.overlay.querySelector(".ecsa-note-resize-handle");this.onPanelPointerMove=a=>{if(!this.panelInteraction)return;let i=this.panelInteraction;i.type==="drag"?this.applyPanelLayout({left:i.startLeft+(a.clientX-i.startX),top:i.startTop+(a.clientY-i.startY),width:i.startWidth,height:i.startHeight}):this.applyPanelLayout({left:i.startLeft,top:i.startTop,width:i.startWidth+(a.clientX-i.startX),height:i.startHeight+(a.clientY-i.startY)})},this.onPanelPointerEnd=()=>{this.panelInteraction&&(this.overlay.classList.remove("is-dragging","is-resizing"),document.removeEventListener("mousemove",this.onPanelPointerMove),document.removeEventListener("mouseup",this.onPanelPointerEnd),this.savePanelLayout(this.getCurrentPanelLayout()),this.panelInteraction=null)},e.addEventListener("mousedown",a=>{if(a.button!==0||a.target.closest("button"))return;a.preventDefault(),a.stopPropagation();let i=this.getCurrentPanelLayout();this.panelInteraction={type:"drag",startX:a.clientX,startY:a.clientY,startLeft:i.left,startTop:i.top,startWidth:i.width,startHeight:i.height},this.overlay.classList.add("is-dragging"),document.addEventListener("mousemove",this.onPanelPointerMove),document.addEventListener("mouseup",this.onPanelPointerEnd)}),t.addEventListener("mousedown",a=>{if(a.button!==0)return;a.preventDefault(),a.stopPropagation();let i=this.getCurrentPanelLayout();this.panelInteraction={type:"resize",startX:a.clientX,startY:a.clientY,startLeft:i.left,startTop:i.top,startWidth:i.width,startHeight:i.height},this.overlay.classList.add("is-resizing"),document.addEventListener("mousemove",this.onPanelPointerMove),document.addEventListener("mouseup",this.onPanelPointerEnd)})},applySavedPanelLayout(){let e=this.loadPanelLayout();if(e?.left!=null&&e?.top!=null&&e?.width&&e?.height)this.applyPanelLayout(e);else if(e?.width&&e?.height){let t=this.computeDefaultPanelLayout();this.applyPanelLayout({...t,width:e.width,height:e.height})}else this.applyPanelLayout(this.computeDefaultPanelLayout())},scheduleLayoutClamp(){!this.overlay?.classList.contains("is-open")||this.panelInteraction||this.layoutClampFrame||(this.layoutClampFrame=window.requestAnimationFrame(()=>{this.layoutClampFrame=null,!(!this.overlay?.classList.contains("is-open")||this.panelInteraction)&&this.applyPanelLayout(this.getCurrentPanelLayout())}))},open(){if(!y.isNotesEnabled())return;let e=this.getNote();this.textarea.value=e,this.dirty=!1,this.setStatus(e?o.t("savedLocally"):""),this.deleteButton.hidden=!e,this.overlay.classList.add("is-open"),this.applySavedPanelLayout(),this.textarea.focus(),this.button.blur()},async copy(){let e=`${o.t("copiedAssetUrl")}: ${window.location.href}
${o.t("copiedNotes")}: ${this.textarea.value}`;try{if(typeof GM_setClipboard=="function")GM_setClipboard(e,"text");else if(navigator.clipboard?.writeText)await navigator.clipboard.writeText(e);else{let t=document.createElement("textarea");t.value=e,t.style.position="fixed",t.style.opacity="0",document.body.appendChild(t),t.select();let a=document.execCommand("copy");if(t.remove(),!a)throw new Error(o.t("clipboardUnavailable"))}this.setStatus(o.t("copied"))}catch(t){this.setStatus(o.t("copyFailed",{error:t.message}),!0)}},scheduleSave(){this.dirty=!0,this.setStatus(o.t("saving")),clearTimeout(this.saveTimer),this.saveTimer=setTimeout(()=>this.saveNow(),500)},saveNow(e=!1){if(clearTimeout(this.saveTimer),!this.dirty&&!e)return!0;try{return g.set(this.assetId,this.textarea.value),this.dirty=!1,this.setStatus(o.t("savedLocally")),this.deleteButton.hidden=!this.textarea.value.trim(),this.updateButton(),!0}catch(t){return this.setStatus(o.t("saveFailed",{error:t.message}),!0),!1}},saveAndClose(){this.saveNow(!0)&&(this.overlay.classList.remove("is-open"),this.button.blur())},requestClose(e=!1){(e||this.dirty)&&!window.confirm(o.t("exitWithoutSaving"))||this.closeWithoutPrompt()},closeWithoutPrompt(){clearTimeout(this.saveTimer),this.dirty=!1,this.textarea.value=this.getNote(),this.overlay.classList.remove("is-open"),this.button.blur()},delete(){if(!(y.isDeleteConfirmEnabled()&&!window.confirm(o.t("deleteConfirm"))))try{g.set(this.assetId,""),this.textarea.value="",this.dirty=!1,this.updateButton(),this.overlay.classList.remove("is-open"),this.button.blur()}catch(e){this.setStatus(o.t("deleteFailed",{error:e.message}),!0)}},setStatus(e,t=!1){this.status.textContent=e,this.status.classList.toggle("is-error",t)},refresh(){this.button&&(this.overlay?.classList.contains("is-open")&&(this.textarea.value=this.getNote(),this.dirty=!1,this.setStatus(o.t("savedLocally"))),this.updateButton())},toggleReviewed(){if(!y.isNotesEnabled())return;let e=g.get(this.assetId)?.reviewed===!0;g.setReviewed(this.assetId,!e),this.updateButton(),this.reviewButton.blur()},updateButton(){let e=this.getNote().trim(),t=!!e,a=o.t(t?"editNote":"addNote");this.button.querySelector(".ecsa-note-icon").classList.toggle("is-active",t),this.button.dataset.noteTooltip=t?e:o.t("addNote"),this.button.title="",this.button.setAttribute("aria-label",a);let i=g.get(this.assetId)?.reviewed===!0,n=i?o.t("reviewed"):o.t("markReviewed");this.reviewButton.querySelector(".ecsa-review-icon").classList.toggle("is-active",i),this.reviewButton.dataset.noteTooltip=n,this.reviewButton.title="",this.reviewButton.setAttribute("aria-label",n)}};function D(e){document.addEventListener("click",a,!0);let t=null;function a(i){if(!e.values.openInNewTab||i.metaKey||i.ctrlKey||i.shiftKey||i.button===1)return;let n=i.target.closest(".materialCard__thumbmailBlock"),s=i.target.closest(".materialCard__cardContentBlock");if(n||s){let m=((n||s).querySelector("a[href]")||n||s).href;if(!m||m.includes("javascript:void(0)"))return;i.preventDefault(),i.stopImmediatePropagation(),i.detail===2?(clearTimeout(t),t=null,typeof GM_openInTab<"u"?GM_openInTab(m,{active:!1,insert:!0,setParent:!0}):window.open(m,"_blank")):i.detail===1&&(t=setTimeout(()=>{window.location.href=m},200))}}}function P(e,t){let a=()=>({category:e.t("sortCategory"),time:e.t("sortDefault")}),i=()=>{if(window.location.href.includes("starred")){let u=document.querySelector(".btn-group.selectFilter");if(u){let p=u.querySelector("a");if(p)return p.textContent.trim()}}else{let u=document.querySelector(".dropdown.selectFilter");if(u){let p=u.querySelector("ul.dropdown-menu");if(p){let d=p.querySelector("li:first-child a");if(d)return d.textContent.trim()}}}},n={},s=document.querySelector("ul.layput__cardPanel");if(!s)return;let l=!1,m=s.innerHTML,h=[],f=i(),c=a(),x="";window.location.href.includes("starred")?x="s":x="d";let N=u=>{localStorage.setItem(x+"sorted",u===!0?1:0),l=u;let p=document.getElementById("sortButton");if(p.textContent=l?c.time:c.category,u){s.innerHTML="";let d=Object.keys(n).sort(),r=[];d.forEach(b=>{r.push(...n[b])}),r.forEach(b=>{s.appendChild(b)})}else s.innerHTML=m},T=()=>{document.querySelectorAll("li.materialCard").forEach(r=>{let b=r.querySelector("a[href*='/search?type=']");if(b){let v=b.textContent.trim();h.includes(v)||h.push(v),v&&(n[v]||(n[v]=[]),n[v].push(r))}});let p=document.querySelector(".btn.btn-default.operationButton.favoriteButton");if(p){let r=document.createElement("button");r.type="button",r.className="btn btn-primary ",r.id="sortButton",r.textContent=c.category,r.style.marginLeft="10px",r.style.marginRight="10px",r.addEventListener("click",function(){l=!l,r.textContent=l?c.time:c.category,N(l)}),p.parentNode.insertBefore(r,p.nextSibling);let b=[...h];b.unshift(f);let v=R(b);p.parentNode.insertBefore(v,r.nextSibling)}document.getElementById("filterButton").textContent===i()&&localStorage.getItem(x+"sorted")==1&&N(!0)};function R(u){let p=document.createElement("div");p.className="dropdown selectFilter ",p.style.display="inline-block",p.style.marginTop="10px";let d=document.createElement("button");d.className="btn btn-default dropdown-toggle filterButton customFilterButton",d.id="filterButton",d.type="button",d.style.width="auto",d.style.paddingRight="20px",d.setAttribute("data-toggle","dropdown"),d.setAttribute("aria-haspopup","true"),d.setAttribute("aria-expanded","true");let r=localStorage.getItem(x+"filtered"),b=localStorage.getItem(x+"sorted");if(u.includes(r)&&r!==i()){let w=document.getElementById("sortButton");w.disabled=!0,d.textContent=r,s.innerHTML="",n[r].forEach(k=>{s.appendChild(k)})}else d.textContent=u[0];d.style.borderRadius="0px",d.style.textAlign="left";let v=document.createElement("span");v.className="caret";let L=document.createElement("ul");return L.className="dropdown-menu customDropdown",u.forEach(w=>{let k=document.createElement("li"),I=document.createElement("a");I.textContent=w,k.appendChild(I),L.appendChild(k),k.addEventListener("click",function(U){localStorage.setItem(x+"filtered",w),U.preventDefault(),s.innerHTML="";let M=document.getElementById("sortButton");M.disabled=w!==f,d.firstChild.textContent=w;let $=document.querySelector("h4.text-right");w!==f?(n[w].forEach(B=>{s.appendChild(B)}),localStorage.setItem(x+"filtered",w)):(s.innerHTML=m,localStorage.getItem(x+"sorted")==1&&N(!0))})}),d.appendChild(v),p.appendChild(d),p.appendChild(L),p}function F(){let u=window.location.pathname;return(u.includes("/download-list")||u.includes("/starred"))&&!u.includes("/download-list-monthly-plan-offer-ic-screen")}function _(){return window.location.pathname.includes("/search")}window.addEventListener("load",()=>{F()&&T()}),document.addEventListener("keydown",u=>{u.key==="Escape"&&t.hidePanel()})}function q(e){let t=e.querySelector('a[href*="/detail?id="]');if(!t)return null;try{return g.normalizeId(new URL(t.href,window.location.href).searchParams.get("id"))}catch{return null}}var z={init(){this.render(),window.addEventListener("load",()=>this.render(),{once:!0}),document.addEventListener("ecsa:notes-changed",()=>this.render()),document.addEventListener("ecsa:notes-setting-changed",()=>this.render()),this.observer=new MutationObserver(()=>this.scheduleRender()),this.observer.observe(document.body,{childList:!0,subtree:!0})},scheduleRender(){this.renderFrame||(this.renderFrame=window.requestAnimationFrame(()=>{this.renderFrame=null,this.render()}))},render(){if(!y.isNotesEnabled()){document.querySelectorAll(".ecsa-card-indicators").forEach(t=>t.remove());return}let e=g.load().notes;document.querySelectorAll("li.materialCard").forEach(t=>{let a=q(t),i=a?e[a]:null,n=typeof i?.text=="string"?i.text.trim():"",s=i?.reviewed===!0,l=t.querySelector(".ecsa-card-indicators");if(!n&&!s){l?.remove();return}let m=t.querySelector(".materialCard__thumbmailBlock");if(!m)return;let h=JSON.stringify([n,s]);if(l?.dataset.state===h)return;let f=l||document.createElement("div");if(f.className="ecsa-card-indicators",f.dataset.state=h,f.replaceChildren(),n){let c=document.createElement("span");c.className="ecsa-card-indicator ecsa-card-note-indicator",c.dataset.noteTooltip=n,c.setAttribute("role","img"),c.setAttribute("aria-label",o.t("editNote")),c.innerHTML='<i class="fa fa-pencil" aria-hidden="true"></i>',f.appendChild(c)}else if(s){let c=document.createElement("span");c.className="ecsa-card-indicator ecsa-card-reviewed-indicator",c.dataset.noteTooltip=o.t("reviewed"),c.setAttribute("role","img"),c.setAttribute("aria-label",o.t("reviewed")),c.innerHTML='<i class="fa fa-ban" aria-hidden="true"></i>',f.appendChild(c)}l||m.appendChild(f)})}};y.init();A.init();C.init();z.init();D(y);P(o,y);})();