Conversation
skill / plugin / widget / theme / カスタム CSS の write 確認が編集後の断片 しか出しておらず、承認の判断材料が AI の自己申告だけになっていた。#1040 で 入った CodeDiffView と ConfirmOptions.diff を各 capability へ配線し、 編集前と適用後の全文を並べて見せる。 - 部分編集 (追記・セクション置換・props patch) も適用後の全文を確認時点で 計算して全文 diff で見せる。断片 diff は文脈が消えるため採用しない - 適用後全文の計算は services 層の純関数へ (skills.ts の replaceMarkdownSection もここへ移設) - 承認後は再計算せず、確認に使った全文をそのまま書き込む。確認と書込の間に 元が変わっていたら書かずに中止する (capabilities/stagedEdit.ts) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
編集履歴は capability (`*.history`) からしか読めず、AI が過去に何を変えたか を人間が後から追う手段が無かった。各エディタの「履歴」から開く一時ウィンドウ を追加し、確認ダイアログと同じ CodeDiffView で読めるようにする。 - 選んだ snapshot は「その編集の直前の状態」なので、比較相手は 1 つ新しい snapshot (無ければ現在の内容)。snapshot 間と snapshot vs 現在が同じ 見え方に収まる - 戻す操作は store 直叩きではなく revert capability を dispatcher 経由で 呼ぶ (確認ダイアログと「見せたものを書く」不変条件をそのまま通す) - 種別ごとの差分 (snapshot → 全文テキスト・言語・revert 先) は services/editHistory.ts に集約し、ウィンドウ側は kind で分岐しない - 導線はスキル / ウィジェット / プラグイン / テーマ / カスタム CSS の 5 エディタ Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CSS エディタは開いた瞬間の内容をローカルバッファに持つだけで、store から 編集バッファへ戻す経路が無かった。revert 自体は効いている (適用もファイル 書込も走る) が、エディタの表示は古いままで、タブ切替やプリセット操作で 古い内容が書き戻され、巻き戻しが無言で打ち消される。 - custom.css の外部変更 (履歴からの revert / AI 編集 / 外部エディタ) を 編集バッファへ取り込む watch を追加。取り込み中は書込み側の watch を止め、 待機中のデバウンスも捨てる (再構築した内容を保存し返さないため) - 履歴ウィンドウの「現在の内容」を prop の静的コピーから store 参照に変更。 revert 後も比較相手が実態を指す (種別ごとの取り出しは composable に集約) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
skill / widget / theme の `*.install` は既存インストールがあると上書き更新に なるが、確認はストアの説明文を出すだけで本体の diff を通らなかった。AI 経由の 再インストールでユーザーのローカル編集が無言で消える。プラグインだけは confirmPluginUpdate 経由で塞がっていた (#1040 の権限再同意の副産物) ので、 残り 3 種別を同じ形に揃える。 - confirmSkillUpdate / confirmWidgetUpdate / confirmThemeUpdate を install の既存分岐と update* の共用点として切り出す - install 経路は「中身が実際に変わるとき」だけ確認する。テーマは表示に効く 部分 (name / base / props) で判定し、別アカウントへの installedFor 追加 だけの再インストールでは確認を出さない - 確認を断ったら本体もストア由来メタも書き換えない (更新バッジは残る) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
確認ダイアログや tool 入出力は Shiki を通っているのに、AI メッセージ本文の markdown だけ素の pre/code で出していた。フェンスの言語は data-lang に 記録するだけでハイライタを呼んでいなかった。 - highlight.ts に highlightCodeTokens を追加 (pre/code を持たず span 列だけ 返す)。独自の pre 構造 — コピーボタン同居 — を壊さずにハイライトできる - ハイライトできた塊は背景も shiki 側に譲る。dark-plus のトークン色前提なので ライトテーマの明るい背景だと潰れる - AI が編集内容を ```diff で見せることがあるので diff 文法を遅延ロードに追加 - ハイライタは非同期ロードなので、メッセージ本文も highlighterLoaded を key に 持たせて読み込み完了後に再描画する Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- ```diff は Shiki に渡さず行単位で塗る。dark-plus の diff は淡い前景色 1 色で、 吹き出しの中では増減が読み取れない。確認ダイアログの diff (CodeDiffView) と 同じグローバル変数 (--nd-diffInsertBg / --nd-diffDeleteBg) を使って色の意味を 揃える。行は block の span 1 つに閉じ、pre の改行文字は挟まない (二重にかかると 1 行おきに空行が出る) - highlightRevision を追加し、描画側の再描画キーをこれに載せ替える。 highlighterLoaded (boolean) は初期化でしか変わらないので、遅延ロードの言語 (python / diff 等) は「ロードが終わっても誰も再描画しない」ままだった - コードブロックのコピーボタンをテキストラベルからアイコンに変更し、メッセージ 単位のコピーボタン (ti-copy → ti-check) と見た目を揃える Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CSS エディタと同じ構造が残りの 3 エディタにもあった。開いた瞬間の内容を ローカルバッファに持つだけで store から戻す経路が無く、履歴ウィンドウから revert しても表示は古いまま、次の操作で古い内容が書き戻される。 - ウィジェット / プラグイン / テーマの各エディタに store からの再同期を追加 - 未保存の編集があるときはユーザーのバッファを優先する (revert より本人の 編集が新しい。保存すれば本人の内容で上書きされる = 明示操作) - 自分の書込みで再同期が走らないよう、値の一致とテーマ内容のキーで弾く スキルエディタは store が要素ごと差し替えるため既に追従していた。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
skill / widget / theme を揃えた前コミットでカラムクエリを取りこぼしていた。 updateQuery には diff 確認があるのに installQuery の既存分岐が無確認で applyStoreUpdate を呼んでおり、同じ非対称が残っていた。 confirmQueryUpdate を install の既存分岐と updateQuery の共用点として切り出す (confirmSkillUpdate / confirmWidgetUpdate / confirmThemeUpdate と同型)。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- 履歴一覧を横スクロールのチップから縦リストへ。件数が増えたとき横だと 見えない項目が出るうえ、時系列の向き (上が新しい) が読み取れない。 最新の 1 件に「直前」タグを付けて現在との関係を明示する - 編集履歴ウィンドウに developer の帰属タグを付け、各エディタの「履歴」 ボタンも開発者モードが無効なら出さない (#1034 — 隠すのは入口だけ) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
スキル / ウィジェット / プラグインの保存は、内容が変わったかを見ずに編集前 snapshot を積んでいた (同値ガードがあるのは custom.css だけ)。これらの エディタはデバウンスの自動保存なので、意味のある区切りと無関係に積まれ、 10 件のリングが短時間で埋まる。「AI が編集する前の状態」が押し出されると 履歴の目的そのものが果たせない。 スキルは snapshot が記録する範囲 (本文 / 名前 / バージョン / モード) が 動いたときだけ積む。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
4 エディタがそれぞれ別実装で外部変更を取り込んでおり、壊れるとユーザーの 編集が消える方向のバグになるのに、手作業で確かめるのが難しかった (未保存の編集を守れているかは目視では分からない)。 useExternalEditSync に規則を 1 本化し、ユニットテストで固定する: - バッファと同じ値なら何もしない (自分の書込みの再取り込みを防ぐ) - 未保存の編集があるときは取り込まない (revert より本人の編集が新しい) - 取り込み中は isSyncing() が true。書込み側の watch をここで止めないと、 取り込んだ内容を保存し返す (エディタによっては再構築した内容に化ける) バッファの形も dirty 判定もエディタごとに違うので、判定は呼び出し側から 受け取る。テストは effectScope で回すのでコンポーネントの mount は不要。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
規則は useExternalEditSync のユニットテストで固定したが、どのエディタが何を 「未保存の編集」として渡しているかはそこでは分からない。取り違えても型は 通り、壊れるとユーザーの編集中バッファが無言で消える。 @vue/test-utils を dev 依存に追加し、4 エディタを shallow mount して 「未編集なら外部変更を取り込む」「編集中なら打ちかけを消さない」を検証する。 カスタム CSS は未保存という状態を持たない代わりに、取り込んだ内容が 再構築された内容に化けないこと (書込み側の watch を止められていること) を見る。 各テストが実際に配線の壊れを捕まえることは、isDirty を潰す / 抑止を外す 改変で落ちることを確認済み。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
シンタックスハイライトのトークン色が VS Code Dark+ 準拠でダーク固定なのに、 面の色だけテーマ追従だったり未定義トークンのフォールバックだったりで バラバラだった。ライトテーマでは淡いトークン色が明るい面の上に乗り、 特に diff の増減が読めなかった。 - 面の色を --nd-codeEditorBg / 新設の --nd-codeEditorFg に集約する - 対象: ノート本文の MFM コードブロック / 確認ダイアログ / AI チャットの メッセージ本文とツール入出力 / 診断ログ - shiki-dark-plus.css は色を直書きするので、要素を足した特異度で グローバル側を勝たせ、面の色をトークン 1 箇所から決まるようにする - AI チャットのメッセージ本文では、インライン code の装飾がブロック内の code にも効いていたのを打ち消す (面の中にもう一枚面が乗っていた) AiScript カラムの値インスペクタはハイライト対象ではない (ダークのトークン色 が乗らない) ため、従来どおりテーマ追従のままにする。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
エディタ / 差分表示 / コードブロックはトークン色が暗い面を前提にした一式しか なく、面をダーク固定にするしかなかった。明るい面用のトークン色を用意し、 アピアランス設定から明暗を選べるようにする。 - 設定は 1 つ (appearance.codeScheme: dark / light / auto)。編集と表示で 分ける理由が無いので項目を増やさない。既定は auto (アプリのテーマに追従) - 実効値は root の data-nd-code-scheme に出し、CSS 変数がそれを見る。 CodeMirror のテーマは色の直書きをやめて変数から取るので、設定を変えても エディタを作り直さずに追従する - Shiki はテーマごとに色クラスが変わるため light-plus のパレットを追加し、 切替時は既存の再描画機構 (highlightRevision) で描き直す - #1050 の一本化はしない。明暗対応に必要なのは light パレットだけで、 本家追従の例外も言語カバレッジの退行も伴わない Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
設定項目を増やさず、触れば分かる挙動で完結させる方針に合わせる。アプリが ダークならコード面もダーク、ライトならライト、アプリが OS 追従ならコード面も OS に追従する。明暗を別扱いしたい場合はカスタム CSS で変数を上書きできる。 明るい面用のトークン色・変数化・Shiki のパレット切替はそのまま残す (設定の有無に関係なく、ライトテーマで面だけ明るくすると読めなくなるため)。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
API surface diff外部アプリ向け API 面 ( Full diffdiff --git a/src-tauri/openapi.json b/src-tauri/openapi.json
index 79287a7..2e236d6 100644
--- a/src-tauri/openapi.json
+++ b/src-tauri/openapi.json
@@ -6,7 +6,7 @@
"license": {
"name": "MIT"
},
- "version": "1.47.0"
+ "version": "1.48.0"
},
"paths": {
"/api": {
|
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
Deploying notedeck with
|
| Latest commit: |
b8d6db4
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://5aebd8d6.notedeck-d3a.pages.dev |
| Branch Preview URL: | https://develop.notedeck-d3a.pages.dev |
📝 WalkthroughWalkthroughThe PR adds edit-history viewing and reversion for five content types. It stages confirmed full-file edits and rejects stale writes. It synchronizes external editor changes. It adds reactive light/dark code rendering and updates the application version to 1.48.0. ChangesEdit history and staged edits
Code rendering
Release metadata
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Editor as EditableView
participant History as EditHistoryContent
participant Service as EditHistoryService
participant Dispatcher as CapabilityDispatcher
participant Store as ContentStore
Editor->>History: open edit history
History->>Service: load snapshots and current text
Service-->>History: return paired diff
History->>Dispatcher: request revert
Dispatcher->>Store: apply staged content
Store-->>Editor: update persisted content
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (1)
src/stores/misstore.ts (1)
1202-1206: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMove
themeBodyKeytosrc/services/and make the key order-independent.Two points:
themeBodyKeyis pure comparison logic inside the store setup. The repository guideline requires pure logic such as merge rules and codecs to live insrc/services/and to be unit tested directly, and limits stores to subscription, cache, and UI state.JSON.stringifyoft.propsdepends on key insertion order. Two theme objects with the same properties in a different order produce different keys. The reinstall path then shows a confirmation dialog for a theme that did not change visually.Sort the
propskeys before serialization.♻️ Proposed change (service function)
// src/services/themeIdentity.ts export function themeBodyKey(theme: Record<string, unknown>): string { const props = (theme.props ?? {}) as Record<string, unknown> const sorted = Object.keys(props) .sort() .map((k) => [k, props[k]] as const) return JSON.stringify([theme.name, theme.base, sorted]) }Based on the coding guideline "正規化、マイグレーション、マージ規則、ファイル codec などの純ロジックは store に置かず
src/services/に実装し、直接ユニットテストする。store は購読、キャッシュ、UI 状態に限定する".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/stores/misstore.ts` around lines 1202 - 1206, Move the pure themeBodyKey comparison logic out of the store into a new src/services/themeIdentity.ts service and update callers to import it. Make serialization independent of props insertion order by sorting props keys and serializing the ordered entries, while preserving the name/base values; add direct unit tests for equivalent themes with differently ordered props.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@DEVELOPMENT.md`:
- Line 721: Remove the hard-coded “10件” snapshot count from the root document’s
description and refer readers to src/utils/historyFs.ts as the source of truth.
If retaining the count is required, add docs-lint-disable-next-line with a clear
reason.
In `@src/capabilities/builtins/plugins.ts`:
- Around line 544-550: After store.updateSrc(installId, next) in the
plugins.revert flow, invoke the existing plugins.update relaunch behavior for
the affected plugin so an active plugin restarts with the reverted source;
preserve the current revert and persistence logic for inactive plugins.
In `@src/components/window/EditHistoryContent.vue`:
- Around line 52-57: Update reload and revert to handle rejected listSnapshots
and dispatchCapability calls: wrap each operation in try/finally so loading and
reverting are always reset, and record failures in the existing load-error state
or revertError. Move revertError above reload if reload uses it, while
preserving successful snapshot selection and revert behavior.
In `@src/stores/misstore.ts`:
- Around line 985-1021: Update updateWidget to report success only when
confirmWidgetUpdate returns a value that is neither false nor undefined; replace
the applied !== false condition while preserving cancellation and successful
update behavior.
In `@src/stores/widgets.ts`:
- Around line 371-373: Update applyStoreUpdate to call pushSnapshot only when
patch.src differs from the current widget.src, matching the guard in updateSrc
and preventing unchanged alwaysConfirm reinstalls from adding history entries.
Add a test covering confirmWidgetUpdate/applyStoreUpdate with an unchanged
source and verify no duplicate snapshot is recorded.
In `@src/styles/global.css`:
- Around line 124-128: Remove the duplicate --nd-codeString and --nd-codeNumber
declarations from the shown dark-scheme block in global.css, retaining a single
dark-scheme definition for each and preserving the later light-scheme overrides.
Leave the --nd-codeKeyword declaration unchanged unless it is also duplicated.
---
Nitpick comments:
In `@src/stores/misstore.ts`:
- Around line 1202-1206: Move the pure themeBodyKey comparison logic out of the
store into a new src/services/themeIdentity.ts service and update callers to
import it. Make serialization independent of props insertion order by sorting
props keys and serializing the ordered entries, while preserving the name/base
values; add direct unit tests for equivalent themes with differently ordered
props.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: aac4d0f3-f8d2-458f-8dd9-0d44005530fa
⛔ Files ignored due to path filters (2)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlsrc-tauri/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (60)
DEVELOPMENT.mdpackage.jsonsrc-tauri/Cargo.tomlsrc-tauri/openapi.jsonsrc-tauri/tauri.conf.jsonsrc/App.vuesrc/aiscript/codemirror/theme.tssrc/assets/shiki-light-plus.csssrc/capabilities/builtins/plugins.test.tssrc/capabilities/builtins/plugins.tssrc/capabilities/builtins/skills.test.tssrc/capabilities/builtins/skills.tssrc/capabilities/builtins/styles.test.tssrc/capabilities/builtins/styles.tssrc/capabilities/builtins/theme.test.tssrc/capabilities/builtins/theme.tssrc/capabilities/builtins/widgets.test.tssrc/capabilities/builtins/widgets.tssrc/capabilities/stagedEdit.test.tssrc/capabilities/stagedEdit.tssrc/capabilities/types.tssrc/components/common/AppConfirm.vuesrc/components/common/MkMfm.vuesrc/components/deck/DeckAiColumn.vuesrc/components/window/AboutContent.vuesrc/components/window/CssEditorContent.dom.test.tssrc/components/window/CssEditorContent.vuesrc/components/window/EditHistoryContent.vuesrc/components/window/PluginsContent.dom.test.tssrc/components/window/PluginsContent.vuesrc/components/window/SkillEditContent.vuesrc/components/window/ThemeEditorContent.dom.test.tssrc/components/window/ThemeEditorContent.vuesrc/components/window/WidgetEditContent.dom.test.tssrc/components/window/WidgetEditContent.vuesrc/composables/useCodeScheme.tssrc/composables/useEditHistoryWindow.tssrc/composables/useEditTargetText.test.tssrc/composables/useEditTargetText.tssrc/composables/useExternalEditSync.test.tssrc/composables/useExternalEditSync.tssrc/main.tssrc/services/editHistory.test.tssrc/services/editHistory.tssrc/services/selfEditApply.test.tssrc/services/selfEditApply.tssrc/stores/misstore.test.tssrc/stores/misstore.tssrc/stores/plugins.test.tssrc/stores/plugins.tssrc/stores/skills.tssrc/stores/widgets.test.tssrc/stores/widgets.tssrc/stores/windows.tssrc/styles/global.csssrc/utils/highlight.tssrc/utils/simpleMarkdown.dom.test.tssrc/utils/simpleMarkdown.tssrc/windows/exposure.test.tssrc/windows/registry.ts
| **編集履歴 + revert:** | ||
| - skill / widget / plugin / theme の各カテゴリで `*.history` / `*.revert` capability を提供 | ||
| - skill / widget / plugin / theme / カスタム CSS の各カテゴリで `*.history` / `*.revert` capability を提供 | ||
| - 編集前のスナップショットをリング (10 件) で sidecar 管理 (`src/utils/historyFs.ts`) |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Remove the hard-coded snapshot count from the root document.
Line 721 includes the changeable value 10 件. Reference src/utils/historyFs.ts as the source of truth instead. If the exact count must remain, add docs-lint-disable-next-line with the reason.
As per coding guidelines, リポジトリ直下のドキュメントでは、変化しやすい数値や行番号を書かず、正本ファイルへの参照を記載する。例外には docs-lint-disable-next-line と理由を付ける.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@DEVELOPMENT.md` at line 721, Remove the hard-coded “10件” snapshot count from
the root document’s description and refer readers to src/utils/historyFs.ts as
the source of truth. If retaining the count is required, add
docs-lint-disable-next-line with a clear reason.
Source: Coding guidelines
| const next = takeStagedEdit( | ||
| ctx, | ||
| 'plugins.revert', | ||
| plugin.src, | ||
| () => entry.snapshot.src, | ||
| ) | ||
| store.updateSrc(installId, next) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Restart an active plugin after the revert.
store.updateSrc() persists the reverted source, but the running active plugin keeps its previous source. plugins.update already relaunches active plugins after the same store update. Reload the updated plugin after Line 550.
Proposed fix
)
store.updateSrc(installId, next)
+ const updated = store.getPlugin(installId)
+ if (updated?.active) {
+ await launchPlugin(updated)
+ }
return { installId, reverted: true, at: entry.at }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const next = takeStagedEdit( | |
| ctx, | |
| 'plugins.revert', | |
| plugin.src, | |
| () => entry.snapshot.src, | |
| ) | |
| store.updateSrc(installId, next) | |
| const next = takeStagedEdit( | |
| ctx, | |
| 'plugins.revert', | |
| plugin.src, | |
| () => entry.snapshot.src, | |
| ) | |
| store.updateSrc(installId, next) | |
| const updated = store.getPlugin(installId) | |
| if (updated?.active) { | |
| await launchPlugin(updated) | |
| } | |
| return { installId, reverted: true, at: entry.at } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/capabilities/builtins/plugins.ts` around lines 544 - 550, After
store.updateSrc(installId, next) in the plugins.revert flow, invoke the existing
plugins.update relaunch behavior for the affected plugin so an active plugin
restarts with the reverted source; preserve the current revert and persistence
logic for inactive plugins.
| async function reload() { | ||
| loading.value = true | ||
| entries.value = await listSnapshots(props.kind, props.basename) | ||
| if (selected.value >= entries.value.length) selected.value = 0 | ||
| loading.value = false | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Handle failures of listSnapshots and dispatchCapability.
reload sets loading to true and awaits listSnapshots. If the read fails, the rejection escapes the watch callback, loading stays true, and the window shows "読み込み中…" forever. revert has the same shape: if dispatchCapability throws, reverting stays true and the button stays disabled.
Wrap both bodies in try/finally and show the error in revertError or a load error state.
🛡️ Proposed fix
async function reload() {
loading.value = true
- entries.value = await listSnapshots(props.kind, props.basename)
- if (selected.value >= entries.value.length) selected.value = 0
- loading.value = false
+ try {
+ entries.value = await listSnapshots(props.kind, props.basename)
+ if (selected.value >= entries.value.length) selected.value = 0
+ } catch (e) {
+ entries.value = []
+ revertError.value = e instanceof Error ? e.message : '履歴の読み込みに失敗しました'
+ } finally {
+ loading.value = false
+ }
}
@@
async function revert(index: number) {
reverting.value = true
revertError.value = ''
- const result = await dispatchCapability(
- spec.value.revertCapabilityId,
- spec.value.revertParams(props.itemId ?? '', index),
- { principal: { kind: 'user' } },
- )
- reverting.value = false
+ let result
+ try {
+ result = await dispatchCapability(
+ spec.value.revertCapabilityId,
+ spec.value.revertParams(props.itemId ?? '', index),
+ { principal: { kind: 'user' } },
+ )
+ } catch (e) {
+ revertError.value = e instanceof Error ? e.message : '戻す操作に失敗しました'
+ return
+ } finally {
+ reverting.value = false
+ }
if (!result.ok && result.code !== 'user_cancelled') {Note: revertError is declared after reload in the source order. Move the revertError declaration above reload if you reuse it there.
Also applies to: 79-95
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/window/EditHistoryContent.vue` around lines 52 - 57, Update
reload and revert to handle rejected listSnapshots and dispatchCapability calls:
wrap each operation in try/finally so loading and reverting are always reset,
and record failures in the existing load-error state or revertError. Move
revertError above reload if reload uses it, while preserving successful snapshot
selection and revert behavior.
| async function confirmWidgetUpdate( | ||
| existing: WidgetMeta, | ||
| fetched: { source: string; hash: string; entry: StoreWidgetEntry }, | ||
| opts: { alwaysConfirm: boolean }, | ||
| ): Promise<WidgetMeta | undefined | false> { | ||
| const { source, hash, entry: e } = fetched | ||
| if (opts.alwaysConfirm || source !== existing.src) { | ||
| const ok = await useConfirm().confirm({ | ||
| title: 'ウィジェットを更新', | ||
| message: updateConfirmMessage(existing.name || e.name, e), | ||
| okLabel: '更新', | ||
| diff: { old: existing.src, new: source, language: 'aiscript' }, | ||
| }) | ||
| if (!ok) return false | ||
| widgetsStore.applyStoreUpdate(existing.installId, { | ||
| src: source, | ||
| iconUrl: e.iconUrl, | ||
| storeSha512: hash, | ||
| storeVersion: e.version, | ||
| } | ||
| return useWidgetsStore().applyStoreUpdate(existing.installId, { | ||
| src: source, | ||
| iconUrl: e.iconUrl, | ||
| storeSha512: hash, | ||
| storeVersion: e.version, | ||
| }) | ||
| } | ||
|
|
||
| async function updateWidget(entry: StoreWidgetEntry): Promise<boolean> { | ||
| const widgetsStore = useWidgetsStore() | ||
| const existing = widgetsStore.widgets.find((w) => w.storeId === entry.id) | ||
| if (!existing) return false | ||
| installingWidget.value = entry.id | ||
| try { | ||
| const fetched = await fetchVerifiedSource( | ||
| entry, | ||
| refetchWidgetEntry(entry.id), | ||
| ) | ||
| const applied = await confirmWidgetUpdate(existing, fetched, { | ||
| alwaysConfirm: true, | ||
| }) | ||
| return true | ||
| return applied !== false |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check the return contract of widgets applyStoreUpdate.
ast-grep run --pattern 'function applyStoreUpdate($$$) { $$$ }' --lang typescript src/stores/widgets.ts
rg -n -C8 'applyStoreUpdate' src/stores/widgets.tsRepository: notedeck-dev/notedeck
Length of output: 971
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- applyStoreUpdate implementation ---'
sed -n '380,445p' src/stores/widgets.ts
printf '%s\n' '--- misstore update functions ---'
sed -n '950,1045p' src/stores/misstore.ts
printf '%s\n' '--- all applyStoreUpdate references ---'
rg -n -C5 'applyStoreUpdate' srcRepository: notedeck-dev/notedeck
Length of output: 31806
🏁 Script executed:
#!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
widgets = Path("src/stores/widgets.ts").read_text()
misstore = Path("src/stores/misstore.ts").read_text()
assert "if (!widget) return undefined" in widgets
assert "return applied !== false" in misstore
assert "return applied !== false && applied !== undefined" not in misstore
def current(value):
return value is not False
def proposed(value):
return value is not False and value is not None
for value in (False, None, {"installId": "w1"}):
print(f"value={value!r} current={current(value)} proposed={proposed(value)}")
PYRepository: notedeck-dev/notedeck
Length of output: 292
Treat an unapplied update as failure. If applyStoreUpdate returns undefined for a missing widget, applied !== false returns true. Also check applied !== undefined before reporting success.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/stores/misstore.ts` around lines 985 - 1021, Update updateWidget to
report success only when confirmWidgetUpdate returns a value that is neither
false nor undefined; replace the applied !== false condition while preserving
cancellation and successful update behavior.
| // 履歴キーは対応表の fileBase (未割当 = ファイル未作成なら履歴も無し)。 | ||
| // 内容が同じ保存では積まない (plugins.updateSrc と同じ理由) | ||
| if (widget.fileBase && widget.src !== src) { |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
Prevent duplicate snapshots during unchanged reinstalls.
Line 373 protects only updateSrc. applyStoreUpdate still calls pushSnapshot when patch.src equals widget.src. confirmWidgetUpdate can call that path for an alwaysConfirm reinstall. A no-op reinstall can add duplicate history entries and evict useful entries from the history ring.
Add the same source comparison to applyStoreUpdate and cover that path with a test.
Proposed fix
- if (widget.fileBase && !widget.readOnly) {
+ if (widget.fileBase && !widget.readOnly && widget.src !== patch.src) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/stores/widgets.ts` around lines 371 - 373, Update applyStoreUpdate to
call pushSnapshot only when patch.src differs from the current widget.src,
matching the guard in updateSrc and preventing unchanged alwaysConfirm
reinstalls from adding history entries. Add a test covering
confirmWidgetUpdate/applyStoreUpdate with an unchanged source and verify no
duplicate snapshot is recorded.
| /* シンタックストークン (CodeMirror 側)。Shiki 側は色ごとのクラスを | ||
| assets/shiki-*.css が持つので、こちらはエディタ用 */ | ||
| --nd-codeKeyword: #569cd6; | ||
| --nd-codeString: #ce9178; | ||
| --nd-codeNumber: #b5cea8; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Remove the duplicate custom properties.
Stylelint rejects --nd-codeString and --nd-codeNumber because :root already defines both properties. Keep one dark-scheme declaration for each property, then retain the light-scheme overrides in the later selector.
🧰 Tools
🪛 Stylelint (17.14.0)
[error] 127-127: Duplicate custom property "--nd-codeString" (declaration-block-no-duplicate-custom-properties)
(declaration-block-no-duplicate-custom-properties)
[error] 128-128: Duplicate custom property "--nd-codeNumber" (declaration-block-no-duplicate-custom-properties)
(declaration-block-no-duplicate-custom-properties)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/styles/global.css` around lines 124 - 128, Remove the duplicate
--nd-codeString and --nd-codeNumber declarations from the shown dark-scheme
block in global.css, retaining a single dark-scheme definition for each and
preserving the later light-scheme overrides. Leave the --nd-codeKeyword
declaration unchanged unless it is also duplicated.
Source: Linters/SAST tools
概要
AI の編集を承認する前に差分で見せる (#981) を中心としたリリース。確認ダイアログが編集後の断片しか出しておらず、承認の判断材料が AI の自己申告だけになっていた状態を解消する。あわせて編集履歴を人が読める導線を用意し、コード面の配色をアプリのテーマに追従させる。
Closes #981
主な変更
適用前の差分表示 (#981)
編集履歴
再インストールの保護
表示
変更一覧
確認事項
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes