@kubohiroya/turbowarp-plus-editor-git 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.ja.md +13 -3
  2. package/README.md +12 -2
  3. package/dist/branch-button.d.ts +47 -0
  4. package/dist/change-marks.d.ts +8 -0
  5. package/dist/connection-settings.d.ts +3 -1
  6. package/dist/github-pull-requests.d.ts +2 -2
  7. package/dist/github-review-api.d.ts +112 -0
  8. package/dist/index.d.ts +29 -5
  9. package/dist/index.js +830 -303
  10. package/dist/messages.d.ts +109 -2
  11. package/dist/{project-asset-paths-nL5sV35A.js → project-asset-paths-DNaoWs3E.js} +2 -2
  12. package/dist/{project-comparison-DjMlwyp8.js → project-comparison-B_3QZ6M-.js} +1414 -33
  13. package/dist/project-source-diff.d.ts +5 -0
  14. package/dist/project-source-diff.js +4 -4
  15. package/dist/project-source-review.d.ts +6 -1
  16. package/dist/pull-request-panel.d.ts +2 -0
  17. package/dist/remote-operations.d.ts +1 -1
  18. package/dist/{repository-xIv0iZkj.js → repository-DXRZEtYk.js} +26 -10
  19. package/dist/repository.d.ts +7 -0
  20. package/dist/repository.js +1 -1
  21. package/dist/review-composer.d.ts +23 -0
  22. package/dist/review-submit.d.ts +144 -0
  23. package/dist/{smart-http-BmHEkE7K.js → smart-http-eARzlXHO.js} +101 -2
  24. package/dist/smart-http.d.ts +2 -0
  25. package/dist/smart-http.js +1 -1
  26. package/dist/style.css +51 -0
  27. package/package.json +1 -1
  28. package/src/branch-button.tsx +289 -0
  29. package/src/change-marks.ts +22 -0
  30. package/src/connection-settings.tsx +9 -2
  31. package/src/github-review-api.ts +298 -0
  32. package/src/index.tsx +322 -66
  33. package/src/messages.ts +80 -4
  34. package/src/project-source-diff.css +10 -0
  35. package/src/project-source-diff.tsx +6 -0
  36. package/src/project-source-review.tsx +33 -5
  37. package/src/pull-request-panel.tsx +27 -6
  38. package/src/remote-operations.ts +1 -1
  39. package/src/repository.ts +22 -8
  40. package/src/review-composer.tsx +166 -0
  41. package/src/review-submit.ts +519 -0
  42. package/src/smart-http.ts +2 -2
  43. package/src/style.css +41 -0
  44. package/dist/objectSpread2-Dkd68nJH.js +0 -101
package/README.ja.md CHANGED
@@ -2,7 +2,11 @@
2
2
 
3
3
  [English](README.md)
4
4
 
5
- TurboWarp Plus Editor のファイルメニューに「ローカル Git」を追加します。リポジトリルートを一度選び、配下の sb3-toolchain v1 作品フォルダをパネルから開きます。作品の編集と保存には Editor の project-folder 接続を使います。
5
+ TurboWarp Plus Editor に「ローカル Git」パネルを追加します。リポジトリルートを一度選び、配下の sb3-toolchain v1 作品フォルダをパネルから開きます。作品の編集と保存には Editor の project-folder 接続を使います。
6
+
7
+ パネルは、Editor のメニューバーのブランチのボタンから開きます。`mountGitPanel({vm, document, host})` はパネルを組み込み、`{open(locale), refresh(), dispose()}` を返します。`renderBranchButton(createElement, {vm, locale})` は、`vm` に組み込んだパネルのボタンを描きます。パネルがないとき(ホストがローカル Git を出さないときなど)は何も描きません。ボタンはブランチのアイコン、現在のブランチ名、▾ を表示します。リポジトリを選ぶ前は **Git** と表示し、**リポジトリを選択…** に進みます。detached HEAD ではコミットの短い ID を、記録がまだないブランチではそのことを表示します。メニュー(`role="menu"`。矢印キー・Home・End・Enter で操作し、Escape でボタンに戻ります)には、ローカルブランチ(現在のブランチに印)、`origin` にあってローカルにないブランチの **… を作って切り替える**、**新しいブランチ…**(HEAD からブランチを作る小さな欄。既定で作ったら切り替えます)、**ローカル Git を開く** を並べます。ボタンとパネルは同じリポジトリと同じ操作を使うので、メニューからの切り替えも、パネルのボタンと同じく確かめ、同じ理由で断ります。結果はパネルを開かずに、画面下のスナックバーで示します。切り替えたときは「ブランチを切り替えました。」(パネルで作品フォルダを選んでいれば **作品を再読込** のボタン付き)、作っただけのときは「ブランチを作りました。」、断ったときはパネルと同じ理由を警告として **ローカル Git を開く** のボタン付きで示します。確認で取りやめたときは何も出しません。スナックバーは 6 秒で消えますが、ポインターを置いているときとフォーカスがあるときは残ります。Escape か閉じるボタンで閉じ、新しい結果は前のものを置き換えます。フォーカスは移さず、Tab でボタンに移れます。`localWrite` がなければ、メニューはブランチを示すだけです。ボタンは、パネルやほかのツールでのブランチの変更に追従します。パネルが見えていない間は、下のパネルの確認と同じ契機で、HEAD とブランチの参照だけを読みます。`BranchButton` は、状態をホストが持つ場合のためのボタン単体です。
8
+
9
+ ホストの横のパネル向けに、`renderHistoryView(createElement, {vm, locale})` は同じリポジトリの履歴のグラフを描きます。ブランチが動くたびに履歴を読み直し、ホストが書き込みを許すときはグラフのブランチの操作を出し、その結果を同じスナックバーで示します。リポジトリを選ぶ前は **リポジトリを選択…** を出し、`vm` にパネルがないときは何も描きません。`followChangeMarks(vm, directory, listener)` は、パネルが最後に読んだ `directory` 配下のファイルの Git の状態(`added`・`modified`・`deleted`・`untracked`、`directory` からの相対パスごと)を、フォルダのツリー向けに `listener` へ渡し、止める関数を返します。`changeMarks(changes, folder)` はその対応づけ単体です。
6
10
 
7
11
  ステージ済み・未ステージ・未追跡の変更、現在のブランチ、HEAD、ローカルブランチと履歴を表示します。ファイルを選ぶとテキスト差分またはバイナリのサイズを確認できます。作品データ(`project.source.json`)は、ホストがブロックの描画関数を渡していればステージ・スプライト別、コード別のブロックで比べ、「テキスト」に切り替えると行単位の差分を表示します。ステージ・スプライトの見出しには、その版の現在のコスチューム(ステージは背景)のサムネイルを添えます。素材は比較する版から読みます(ステージ済みは HEAD とインデックス、未ステージはインデックスと作業ツリー、未追跡は作業ツリー、コミットは第一親とそのコミット)。素材のフォルダはその版の `sb3-source.json` の `assetsDirectory`(既定は `assets`)です。読めない版では名前だけを示します。コミットを選ぶと第一親との差分を表示します。作品を選んでいるときは、変更一覧と差分をそのフォルダに絞ります。
8
12
 
@@ -14,7 +18,7 @@ TurboWarp Plus Editor のファイルメニューに「ローカル Git」を追
14
18
 
15
19
  File System Access に対応する Chromium 系ブラウザーが必要です。初版は通常の `.git` ディレクトリとファイル内容の変更を扱います。linked worktree、サブモジュール、シンボリックリンクは対象外です。作業ツリーの実行権限の変更はブラウザーから検出できません。Git に記録済みのファイルモードは表示します。バイナリアセットはサイズを示し、テキスト差分の生成には上限があります。
16
20
 
17
- Node.js 26 で `npm ci` と `npm test` を実行します。Vite でライブラリとスタイルシート、TypeScript で型宣言を生成します。`npm pack` で Editor 用の配布物を作成します。`npm run test:browser` は、最小 editor runtime(`@kubohiroya/turbowarp-plus-editor-test-runtime`)の上で、履歴グラフのキーボード操作と操作メニュー、比較ダイアログのブロックの描画を Chromium で試します。Editor が固定する VM と Scratch Blocks を使い、Editor のビルドは要りません。Editor では既定で有効です。起動 URL に `twPlusLocalGit=0` を指定するとパネルの接続を解除できます。
21
+ Node.js 26 で `npm ci` と `npm test` を実行します。Vite でライブラリとスタイルシート、TypeScript で型宣言を生成します。`npm pack` で Editor 用の配布物を作成します。`npm run test:browser` は、最小 editor runtime(`@kubohiroya/turbowarp-plus-editor-test-runtime`)の上で、ブランチのボタンのメニューとキーボード操作(実際のリポジトリでパネルと並べた場合も)、履歴グラフのキーボード操作と操作メニュー、比較ダイアログのブロックの描画を Chromium で試します。Editor が固定する VM と Scratch Blocks を使い、Editor のビルドは要りません。Editor では既定で有効です。起動 URL に `twPlusLocalGit=0` を指定するとパネルの接続を解除でき、ブランチのボタンも出ません。
18
22
 
19
23
  パネルの構成とローカル Git の扱いは [duff](https://github.com/chigichan24/duff) を参考にしました。実装には [isomorphic-git](https://github.com/isomorphic-git/isomorphic-git) と [react-diff-view](https://github.com/otakustay/react-diff-view) を使用し、同梱依存のライセンスはビルド時の第三者ライセンス一覧に収録します。
20
24
 
@@ -53,6 +57,12 @@ GitHub 接続が接続済みのとき、PR レビューは PR の中身を GitHu
53
57
 
54
58
  `localWrite` があるとき、接続で読んだ PR に「pr/&lt;番号&gt; として手元で開く」が出ます。取得した head にローカルブランチ `pr/<番号>` を作り(`pullRequestBranch(number)`。`refs/pull/origin/<番号>/head` はそのまま残します)、ブランチ一覧と同じ確認付きの経路で切り替え、選んでいる作品フォルダをそのブランチから開き直します。これで PR の版を Editor で実行できます。作品の実行中、Editor に保存していない編集があるとき、作業ツリーに変更(ステージ済み・未ステージ・未追跡)があるときは行わず、何も書きません。開いている作品フォルダが PR にないときは、先に確かめます。`pr/<番号>` がすでに同じコミットにあるときは、それに切り替えます。別のコミットを指しているとき(そのブランチで記録した、または PR が更新された)は、ブランチを動かさずに断ります。「ローカルの変更」でそのブランチに切り替えるか、Git で名前を変えるか消して(`git branch -m pr/<番号> <名前>`、`git branch -D pr/<番号>`)、もう一度開いてください。公開 API で読んだ PR は Git のオブジェクトが手元にないため、ボタンを出しません。`localWrite` がないときも出しません。
55
59
 
60
+ #### レビューを送る
61
+
62
+ 「レビューの送信を許可する」を付けて接続したとき(auth パッケージの任意の `review` の許可。push とは別)、接続で読んだ PR では、各コードの変更の下にコメント欄が、末尾に「レビューを送る」が出ます。全体のコメントと「コメント」「承認」「修正を依頼」を選べます。ホストは `connection` に `reviewGranted()` を渡します。レビューはプロキシの GitHub API の経路(`/v1/github/<owner>/<repo>/`、`createGitHubReviewApi`)で送り、GitHub のトークンとノード ID はプロキシの中に留まります。クライアントが送るのは、プロキシのセッションの Bearer、`Review-Submit-Id`、PR 番号、取得した head、書いた内容だけです。GraphQL の経路では、1 つずつ順に送ります。取得した head への保留中のレビュー、コードのコメントごとに `project.source.json` へのファイル単位のスレッド(50 件まで)、最後に公開です。PR の変更に入っていないファイル、51 件目以降、GitHub が拒んだファイルへのコメントは、ファイルとコードの見出しを付けてレビュー本文に入れます。送る前に、どちらに何件入るかを表示します。GraphQL の経路が `404 GITHUB_API_UNAVAILABLE` のときは、全部を本文に入れて REST のレビュー 1 件で送ります。どちらの経路もなければ、送信は出しません。
63
+
64
+ 公開の前に失敗したときは保留中のレビューを破棄し、何も公開しません。破棄に失敗したら「GitHub に保留中のレビューが残っています」と示し、破棄し直せます。公開を拒まれたときは、スレッド付きの保留中のレビューを残し、直して送り直すか破棄できます。結果がわからないとき(応答が届かない、重複、`502`・`504`)は自動で再送せず、「送信されたか確かめる」でプロキシの記録を読みます。利用上限では待ち時間を示し、その間ボタンを止めます。下書き(内容、結果、送信中の送信 ID)は GitHub がレビューを受け取るまで、失敗や再読み込みをまたいで `turbowarp-plus-git:review-draft:<owner/repo#番号>` に保存します。トークン、プロキシのアドレス、GitHub の ID は入れません。`sendReview`・`checkReview`・`discardReview`・`planReview` と下書きの関数は、React から分けて `./project-source-diff` から使えます。
65
+
56
66
  元のブランチはそのまま残ります。戻るときは「ローカルの変更」でそのブランチに切り替えます(開いた後に、パネルがブランチ名を示します)。`pr/<番号>` は追跡の設定を持たない通常のローカルブランチです。パネルは消さず、pull と push もほかのブランチと同じに扱うので、意図しない限り push しないでください。要らなくなったら `git branch -D pr/<番号>` で消せます。
57
67
 
58
68
  ## 作品ソースのブロック差分
@@ -119,7 +129,7 @@ const diffFile = new Blob([patch], {type: 'text/x-diff'});
119
129
 
120
130
  `loadAsset` は `({version, md5ext, signal}) => Promise<Blob | null>` の形で、`version`(`before`/`after`)の作品フォルダの素材を読みます。渡すと、ステージ・スプライトの見出しにその版の現在のコスチューム(ステージは背景)のサムネイルを添えます。変更前後でコスチュームが違えば「変更前 → 変更後」と並べます。関数がないとき、または画像を読めないときは名前だけを示します。`createGitHubPullRequests` が返す作品ファイルには、変更前(マージベース)と変更後(head)の素材を raw.githubusercontent.com から読む `loadAsset` が付きます。この読み込みは GitHub API の利用上限を消費せず、1 ファイル 10 MiB までです。
121
131
 
122
- 確認済みの印は、`rememberAs`(`owner/repo#42` など)を渡すと、ブラウザーのストレージにコードの内容と組で保存します。パネルを閉じても Editor を再読み込みしても残り、PR の head が変わると、内容が変わったコードの印だけが外れます。作品ファイルと Git には書きません。`rememberAs` がないときは、表示中のレビューだけの状態です。ローカル Git パネルの PR 一覧は、リポジトリ名と PR 番号を渡します。PR 本文・ファイル差分の取得と GitHub へのレビュー送信は、呼び出し側が担当します。
132
+ 確認済みの印は、`rememberAs`(`owner/repo#42` など)を渡すと、ブラウザーのストレージにコードの内容と組で保存します。パネルを閉じても Editor を再読み込みしても残り、PR の head が変わると、内容が変わったコードの印だけが外れます。作品ファイルと Git には書きません。`rememberAs` がないときは、表示中のレビューだけの状態です。ローカル Git パネルの PR 一覧は、リポジトリ名と PR 番号を渡します。PR 本文・ファイル差分の取得は呼び出し側が担当します。`commentAs`(`owner/repo#42` など)を渡すと、コードごとにレビューの下書きへ入るコメント欄を出します。
123
133
 
124
134
  ### 2 つの版を直接比べる
125
135
 
package/README.md CHANGED
@@ -2,7 +2,11 @@
2
2
 
3
3
  [日本語](README.ja.md)
4
4
 
5
- Local Git adds a panel to the TurboWarp Plus Editor File menu. Select the repository root once, then browse to an sb3-toolchain v1 project folder inside it. Project editing and saving are supplied by the Editor's project-folder integration.
5
+ Local Git adds a panel to the TurboWarp Plus Editor. Select the repository root once, then browse to an sb3-toolchain v1 project folder inside it. Project editing and saving are supplied by the Editor's project-folder integration.
6
+
7
+ The panel opens from a branch button in the Editor's menu bar. `mountGitPanel({vm, document, host})` mounts the panel and returns `{open(locale), refresh(), dispose()}`; `renderBranchButton(createElement, {vm, locale})` renders the button for the panel mounted for `vm`, and renders nothing while there is none (such as when the host does not offer local Git). The button shows a branch icon, the current branch and ▾. Before a repository is chosen it reads **Git** and offers **Choose Repository…**; a detached HEAD shows its short commit ID, and a branch without records yet says so. Its menu (`role="menu"`; arrow keys, Home and End, Enter, Escape back to the button) lists the local branches with the current one checked, then **Create and switch to …** for `origin` branches without a local one, **New Branch…** (a small form that creates a branch at HEAD, switching to it by default) and **Open Local Git**. The button and the panel share one repository and the same operations, so switching from the menu asks and refuses exactly as the panel's own buttons do. The result shows in a snackbar at the bottom of the window instead of opening the panel: **Switched branches.** with **Reload Project** when a project folder is selected in the panel, **Created the branch.**, or the panel's reason for a refusal (as an alert) with **Open Local Git**. A declined confirmation shows nothing. The snackbar hides after six seconds unless it is hovered or focused, Escape or its close button dismisses it, a new one replaces the old, and focus is not moved into it (Tab reaches its buttons). Without `localWrite` the menu lists the branches only. The button follows branch changes made in the panel or by other tools: while the panel is out of view, the panel's checks below read only HEAD and the branch refs. `BranchButton` is the button on its own, for hosts that keep the state themselves.
8
+
9
+ For a side panel of the host, `renderHistoryView(createElement, {vm, locale})` draws the history graph of the same repository. It reads the history again whenever the branches move, offers the graph's branch actions when the host allows writes and tells their results in the same snackbar; before a repository is chosen it offers **Choose Repository…**, and it renders nothing while no panel is mounted for `vm`. `followChangeMarks(vm, directory, listener)` calls `listener` with the Git status of the files under `directory` (`added`, `modified`, `deleted` or `untracked`, by path relative to `directory`) as the panel last read them, for a folder explorer, and returns the function that stops it. `changeMarks(changes, folder)` is the mapping on its own.
6
10
 
7
11
  The panel separates staged, unstaged, and untracked changes. It shows the current branch, HEAD, local branches, and the history. Select a file to inspect its text diff or binary size. When the host supplies a block renderer, project data (`project.source.json`) is compared as blocks by stage, sprite and code, and **Text** switches to the line diff. Each stage and sprite heading shows a thumbnail of its current costume (the backdrop for the stage) in each version. Assets are read from the compared versions: HEAD and the index for staged changes, the index and the working tree for unstaged changes, the working tree for untracked files, and the first parent and the commit for history. The assets folder is the `assetsDirectory` of that version's `sb3-source.json` (`assets` by default). When an asset cannot be read, only the name is shown. Pull request reviews read the same thumbnails from raw.githubusercontent.com at the merge base and head, without using the GitHub API rate limit. A review's **Reviewed** marks are kept in browser storage per repository and pull request, together with each script's content: they survive closing the panel and reloading the Editor, and only the marks of scripts whose content changed are cleared when the pull request head moves. They are never written to project files or Git. Select a commit to compare its files with its first parent. The selected project limits the file list and diffs to that folder.
8
12
 
@@ -14,7 +18,7 @@ The history is a graph of the commits reachable from the local branches, `origin
14
18
 
15
19
  A Chromium-based browser with File System Access support is required. The first version supports ordinary `.git` directories and content changes. Linked worktrees, submodules, and symbolic links are outside this scope. The browser cannot detect working-tree executable-bit changes. Recorded index and tree modes remain visible. Binary assets show sizes, and text diff generation is bounded.
16
20
 
17
- Use Node.js 26, run `npm ci`, then `npm test`. Vite builds the library and stylesheet, and TypeScript emits declarations. `npm pack` prepares the artifact used by the Editor build. `npm run test:browser` runs the history graph's keyboard and actions, and the comparison dialog's block drawing, in Chromium on the minimal editor runtime (`@kubohiroya/turbowarp-plus-editor-test-runtime`), with the Editor's pinned VM and Scratch Blocks and without building the Editor. The Editor enables the panel by default. Start it with `twPlusLocalGit=0` in its URL to disable the integration.
21
+ Use Node.js 26, run `npm ci`, then `npm test`. Vite builds the library and stylesheet, and TypeScript emits declarations. `npm pack` prepares the artifact used by the Editor build. `npm run test:browser` runs the branch button's menu and keyboard (also beside the panel on a real repository), the history graph's keyboard and actions, and the comparison dialog's block drawing, in Chromium on the minimal editor runtime (`@kubohiroya/turbowarp-plus-editor-test-runtime`), with the Editor's pinned VM and Scratch Blocks and without building the Editor. The Editor enables the panel by default. Start it with `twPlusLocalGit=0` in its URL to disable the integration; the branch button is then not shown.
18
22
 
19
23
  The panel structure and local Git approach were informed by [duff](https://github.com/chigichan24/duff). The implementation uses [isomorphic-git](https://github.com/isomorphic-git/isomorphic-git) and [react-diff-view](https://github.com/otakustay/react-diff-view). Bundled licenses are listed in the build's third-party notices.
20
24
 
@@ -53,6 +57,12 @@ The public API still provides the list, and the title, description and base of p
53
57
 
54
58
  With `localWrite`, a pull request read over the connection offers **Open here as pr/&lt;number&gt;**. It creates the local branch `pr/<number>` at the fetched head (`pullRequestBranch(number)`; `refs/pull/origin/<number>/head` stays as it is), switches to it through the same checked path as the branch list, and opens the selected project folder again from it, so the pull request's version can be run in the Editor. It is refused, and nothing is written, while the project runs, while the Editor has unsaved edits, or while the work tree has changes (staged, unstaged or untracked). It asks first when the open project folder is not in the pull request. If `pr/<number>` already exists at the same commit, the panel switches to it; if it points at another commit (you recorded on it, or the pull request moved), the panel refuses and leaves the branch unchanged. Switch to it from **Local changes**, or rename or delete it with Git (`git branch -m pr/<number> <name>`, `git branch -D pr/<number>`), and open the pull request again. The button is not shown for pull requests read from the public API, which brings no Git objects, nor without `localWrite`.
55
59
 
60
+ #### Sending a review
61
+
62
+ When the connection was made with **Allow sending reviews** (the auth package's optional `review` grant, separate from push), a pull request read over the connection offers a comment field under each code change and a **Send a review** box: an overall comment and **Comment**, **Approve** or **Request changes**. The host's `connection` passes `reviewGranted()`. The review goes through the proxy's GitHub API routes (`/v1/github/<owner>/<repo>/`, `createGitHubReviewApi`), which keep the GitHub token and node IDs; the client sends the Bearer of the proxy session, a `Review-Submit-Id`, the pull request number, the fetched head and the words. With the GraphQL routes, one review is sent in steps one at a time: a pending review on the fetched head, one file-level thread on the `project.source.json` per code comment (up to 50), then the submission. Comments on files outside the pull request's changes, beyond 50, or on a file GitHub refused go in the review body under their file and script; the box shows how many go where before sending. When the proxy answers `404 GITHUB_API_UNAVAILABLE` for the GraphQL routes, the whole review goes in the body of one REST review instead; when neither route is available, sending is not offered.
63
+
64
+ A failure before the submission discards the pending review, so nothing is published. If that fails, the box says a pending review remains on GitHub and offers to discard it. A refused submission keeps the pending review with its threads, to change and send again or discard. When the outcome is unknown (a lost answer, a duplicate, `502`/`504`), nothing is sent again on its own: **Check Whether It Was Sent** reads the proxy's record. A rate limit shows its wait and disables the buttons for it. The draft (words, result, and an in-progress submission ID) is kept in browser storage under `turbowarp-plus-git:review-draft:<owner/repo#number>` until GitHub has the review, through failures and reloads; it never holds tokens, the proxy address or GitHub IDs. `sendReview`, `checkReview`, `discardReview`, `planReview` and the draft functions are exported from `./project-source-diff` apart from React.
65
+
56
66
  The branch you were on stays as it was. To go back, switch to it under **Local changes** (the panel names it after opening). `pr/<number>` is an ordinary local branch without tracking settings: the panel does not delete it, and pull and push treat it like any other branch, so do not push it unless you mean to. Delete it with `git branch -D pr/<number>` when it is no longer needed.
57
67
 
58
68
  ## Comparing before choosing
@@ -0,0 +1,47 @@
1
+ import React from 'react';
2
+ import type { HistoryActionResult } from './history-graph-view.js';
3
+ import type { BranchRefs } from './repository.js';
4
+ /** What the branch button shows: the branches of the chosen repository as the Git panel last read them. */
5
+ export interface BranchButtonState extends BranchRefs {
6
+ /** Whether a repository has been chosen. */
7
+ repository: boolean;
8
+ /** Whether the host lets the panel switch and create branches. */
9
+ writable: boolean;
10
+ /** Whether the panel is running an operation. */
11
+ busy: boolean;
12
+ /** Whether a project folder is selected in the panel, so a switch can offer to reload it. */
13
+ project: boolean;
14
+ }
15
+ /** What the branch button asks the Git panel to do. Branch operations resolve as the panel's own buttons do. */
16
+ export interface BranchButtonActions {
17
+ open(): void;
18
+ choose(): void;
19
+ /** The panel's Reload Project, which asks before replacing unsaved edits. */
20
+ reload(): void;
21
+ switchBranch(name: string): Promise<HistoryActionResult>;
22
+ /** Creates a branch at HEAD. */
23
+ createBranch(name: string, checkout: boolean): Promise<HistoryActionResult>;
24
+ /** Creates a local branch from `origin/<name>` and switches to it. */
25
+ createFromRemote(name: string): Promise<HistoryActionResult>;
26
+ }
27
+ export interface BranchButtonProps extends BranchButtonActions {
28
+ state: BranchButtonState;
29
+ locale?: string;
30
+ }
31
+ /** How long a snackbar stays when it is neither hovered nor focused. */
32
+ export declare const snackbarDuration = 6000;
33
+ /**
34
+ * The results of branch operations in a snackbar, as the branch button and the history view tell them: a switch with
35
+ * Reload Project while a project folder is selected, a new branch, or the reason for a refusal with Open Local Git.
36
+ * A declined confirmation tells nothing. `restoreFocus` takes the focus back when the snackbar closes from inside.
37
+ */
38
+ export declare function useBranchSnackbar(locale: string, project: boolean, actions: Pick<BranchButtonActions, 'open' | 'reload'>, restoreFocus?: () => void): {
39
+ report: (result: HistoryActionResult, switched: boolean) => void;
40
+ element: React.JSX.Element | null;
41
+ };
42
+ /**
43
+ * The current branch as a menu button for the host's menu bar, with the menu of branches. Before a repository is
44
+ * chosen it reads "Git" and offers to choose one. A detached HEAD shows its short commit ID. The menu moves with the
45
+ * arrow keys, Home and End; Escape closes it and returns to the button. Without `writable` it lists the branches only.
46
+ */
47
+ export declare function BranchButton({ state, locale, ...actions }: BranchButtonProps): React.JSX.Element;
@@ -0,0 +1,8 @@
1
+ import type { FileChange } from './repository.js';
2
+ /** How a file stands in Git, as a folder explorer marks it. */
3
+ export type ChangeMark = 'added' | 'modified' | 'deleted' | 'untracked';
4
+ /**
5
+ * The marks of the working tree's changes under `folder` (a path inside the repository, '' for its root), by path
6
+ * relative to `folder`. A file with both staged and unstaged changes is marked as its working tree stands.
7
+ */
8
+ export declare function changeMarks(changes: readonly FileChange[], folder: string): Map<string, ChangeMark>;
@@ -8,13 +8,15 @@ export interface GitConnectionSettings {
8
8
  state: GitAuthState;
9
9
  proxyOrigin?: string;
10
10
  write?: boolean;
11
+ /** Whether the connection was made with the grant to send pull request reviews. */
12
+ review?: boolean;
11
13
  authorization?: {
12
14
  url: string;
13
15
  userCode?: string;
14
16
  };
15
17
  };
16
18
  select(mode: Mode): Promise<void>;
17
- connect(remote: string, write?: boolean): Promise<void>;
19
+ connect(remote: string, write?: boolean, review?: boolean): Promise<void>;
18
20
  refresh(): Promise<void>;
19
21
  disconnect(): Promise<void>;
20
22
  getSession?: GetRemoteSession;
@@ -21,7 +21,7 @@ export declare function createGitHubPullRequests(repository: string, send?: type
21
21
  ref: string;
22
22
  repository: string | null;
23
23
  };
24
- state: "open" | "closed" | "merged" | "draft";
24
+ state: "closed" | "open" | "merged" | "draft";
25
25
  changedFiles: number;
26
26
  number: number;
27
27
  title: string;
@@ -48,7 +48,7 @@ export declare function createGitHubPullRequests(repository: string, send?: type
48
48
  ref: string;
49
49
  repository: string | null;
50
50
  };
51
- state: "open" | "closed" | "merged" | "draft";
51
+ state: "closed" | "open" | "merged" | "draft";
52
52
  changedFiles: number;
53
53
  number: number;
54
54
  title: string;
@@ -0,0 +1,112 @@
1
+ import type { GitProxySession } from '@kubohiroya/turbowarp-plus-editor-auth';
2
+ /**
3
+ * The pull request review routes of the GitHub connection's proxy, under `/v1/github/<owner>/<repo>/`. The proxy keeps
4
+ * the GitHub token and the GitHub node IDs; this client sends only a pull request number, the compared commit, a
5
+ * Review-Submit-Id and the review's words, and reads back the few fields the proxy returns.
6
+ */
7
+ export type ReviewEvent = 'COMMENT' | 'APPROVE' | 'REQUEST_CHANGES';
8
+ export declare const reviewEvents: readonly ReviewEvent[];
9
+ declare const proxyCodes: {
10
+ readonly 400: readonly ["BAD_REQUEST"];
11
+ readonly 401: readonly ["SESSION_REQUIRED", "SESSION_EXPIRED", "REAUTH_REQUIRED"];
12
+ readonly 403: readonly ["FORBIDDEN"];
13
+ readonly 404: readonly ["GITHUB_API_UNAVAILABLE", "PULL_REQUEST_NOT_FOUND"];
14
+ readonly 409: readonly ["REVIEW_STEP_CONFLICT", "REVIEW_DUPLICATE", "PENDING_REVIEW_EXISTS", "REDIRECT_REJECTED"];
15
+ readonly 411: readonly ["LENGTH_REQUIRED"];
16
+ readonly 413: readonly ["PAYLOAD_TOO_LARGE"];
17
+ readonly 422: readonly ["REVIEW_REJECTED", "REVIEW_THREAD_REJECTED"];
18
+ readonly 429: readonly ["RATE_LIMITED"];
19
+ readonly 500: readonly ["GITHUB_API_MISCONFIGURED"];
20
+ readonly 502: readonly ["UPSTREAM_FAILURE"];
21
+ readonly 504: readonly ["UPSTREAM_TIMEOUT"];
22
+ };
23
+ type ProxyCode = (typeof proxyCodes)[keyof typeof proxyCodes][number];
24
+ export type GitHubReviewErrorCode = ProxyCode | 'INVALID_REMOTE' | 'INVALID_CONNECTION' | 'INVALID_SESSION' | 'SESSION_UNAVAILABLE' | 'NETWORK_ERROR' | 'INVALID_RESPONSE' | 'ABORTED';
25
+ export declare class GitHubReviewError extends Error {
26
+ readonly code: GitHubReviewErrorCode;
27
+ readonly options: {
28
+ status?: number;
29
+ requestId?: string;
30
+ retryAfter?: number;
31
+ /** A mutation was sent and GitHub may have applied it. */
32
+ unknown?: boolean;
33
+ };
34
+ constructor(code: GitHubReviewErrorCode, options?: {
35
+ status?: number;
36
+ requestId?: string;
37
+ retryAfter?: number;
38
+ /** A mutation was sent and GitHub may have applied it. */
39
+ unknown?: boolean;
40
+ });
41
+ get unknown(): boolean;
42
+ get retryAfter(): number | undefined;
43
+ }
44
+ /** A thread path the proxy accepts: a relative path whose last element is exactly project.source.json. */
45
+ export declare function isReviewThreadPath(path: string): boolean;
46
+ export declare function newReviewSubmitId(): `${string}-${string}-${string}-${string}-${string}`;
47
+ export interface ReviewContext {
48
+ headRefOid: string;
49
+ viewer: string;
50
+ pendingReview: {
51
+ ours: boolean;
52
+ commitOid: string | null;
53
+ createdAt: string;
54
+ } | null;
55
+ reviews: {
56
+ state: string;
57
+ commitOid: string | null;
58
+ submittedAt: string | null;
59
+ ours: boolean;
60
+ }[];
61
+ /** The proxy's record of the submission, when the request named one. */
62
+ submit?: {
63
+ state: string;
64
+ threads: number;
65
+ };
66
+ }
67
+ export interface SubmittedReview {
68
+ state: string;
69
+ commitOid: string;
70
+ submittedAt: string;
71
+ url: string;
72
+ }
73
+ export interface RestReview {
74
+ id: number;
75
+ login: string | null;
76
+ commitId: string | null;
77
+ state: string;
78
+ body: string;
79
+ }
80
+ export interface GitHubReviewApiOptions {
81
+ /** The repository's GitHub HTTPS remote, as the connection was made for it. */
82
+ readonly remote: string;
83
+ /** The connection's session for `remote`; review calls ask for the review grant. */
84
+ readonly getSession: (remote: string, write: boolean, review?: boolean) => Promise<GitProxySession | null>;
85
+ readonly fetch?: typeof fetch;
86
+ }
87
+ export declare function createGitHubReviewApi({ remote, getSession, fetch: send }: GitHubReviewApiOptions): {
88
+ available: {
89
+ threads?: boolean;
90
+ body?: boolean;
91
+ };
92
+ /** The GitHub login of the last session this client used. */
93
+ account: () => string | null;
94
+ /** Reads the viewer's pending and recent reviews; with `submitId`, resolves that submission's unknown outcome. */
95
+ context(number: number, submitId?: string, signal?: AbortSignal): Promise<ReviewContext>;
96
+ /** Step 1: a pending review on `commitOid`. */
97
+ start(number: number, submitId: string, commitOid: string, signal?: AbortSignal): Promise<void>;
98
+ /** Step 2: one file-level thread on a project.source.json; resolves the number of threads the proxy has accepted. */
99
+ thread(number: number, submitId: string, threadIndex: number, threadPath: string, body: string, signal?: AbortSignal): Promise<number>;
100
+ /** Step 3: publishes the pending review. */
101
+ submit(number: number, submitId: string, threadCount: number, event: ReviewEvent, body: string, signal?: AbortSignal): Promise<SubmittedReview>;
102
+ /** Removes the pending review of the submission. */
103
+ discard(number: number, submitId: string, signal?: AbortSignal): Promise<void>;
104
+ /** The review-body route: one page of the pull request's reviews, oldest first. */
105
+ reviews(number: number, page: number, signal?: AbortSignal): Promise<RestReview[]>;
106
+ /** The review-body route: creates and publishes a review with a body only. */
107
+ create(number: number, submitId: string, commitOid: string, event: ReviewEvent, body: string, signal?: AbortSignal): Promise<{
108
+ url: string;
109
+ }>;
110
+ };
111
+ export type GitHubReviewApi = ReturnType<typeof createGitHubReviewApi>;
112
+ export {};
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { ChangeMark } from './change-marks.js';
1
2
  import type { GitConnectionSettings } from './connection-settings.js';
2
3
  import type { GitAuthor } from './local-operations.js';
3
4
  import type { ProjectBlockRenderer } from './scratch-block-diff-renderer.js';
@@ -11,11 +12,15 @@ export { openProjectComparison } from './project-comparison.js';
11
12
  export { openProjectSourceTabs } from './project-source-tabs.js';
12
13
  export type { ProjectSourceTabsFile, ProjectSourceTabsOptions } from './project-source-tabs.js';
13
14
  export type { ProjectComparisonAction, ProjectComparisonOptions, ProjectComparisonSection } from './project-comparison.js';
14
- export type { PullRequestBase, PullRequestFetch, PullRequestFiles, Repository, Snapshot, FileChange, FileDiff, HistoryCommit, HistoryCursor, HistoryPage, HistoryRef } from './repository.js';
15
+ export type { BranchRefs, PullRequestBase, PullRequestFetch, PullRequestFiles, Repository, Snapshot, FileChange, FileDiff, HistoryCommit, HistoryCursor, HistoryPage, HistoryRef } from './repository.js';
16
+ export { BranchButton } from './branch-button.js';
17
+ export type { BranchButtonActions, BranchButtonProps, BranchButtonState } from './branch-button.js';
15
18
  export { HistoryGraph, laneColors } from './history-graph-view.js';
16
19
  export type { HistoryActionResult, HistoryGraphProps } from './history-graph-view.js';
17
20
  export { layoutHistory } from './history-graph.js';
18
21
  export type { GraphCommit, GraphLayout, GraphRow, GraphSegment } from './history-graph.js';
22
+ export { changeMarks } from './change-marks.js';
23
+ export type { ChangeMark } from './change-marks.js';
19
24
  export type { GitAuthor, LocalOperation } from './local-operations.js';
20
25
  export { pullRequestBranch } from './local-operations.js';
21
26
  export interface GitHost {
@@ -72,12 +77,31 @@ export declare function mountGitPanel({ vm, document, host }: {
72
77
  host: GitHost;
73
78
  }): {
74
79
  refresh: () => Promise<void>;
80
+ open: (locale: string) => void;
75
81
  dispose(): void;
76
82
  } | null;
77
- export declare function renderGitMenu(createElement: (component: any, props: any, ...children: any[]) => any, { vm, locale, MenuItem, MenuSection, onRequestClose }: {
83
+ /**
84
+ * Renders the button for the host's menu bar that shows the current branch of the panel mounted for `vm` and opens the
85
+ * menu of branches, the panel and the repository choice. It renders nothing while no panel is mounted for `vm`, such as
86
+ * when the host does not offer local Git, and shares the panel's repository, operations and checks.
87
+ */
88
+ export declare function renderBranchButton(createElement: (component: any, props: any, ...children: any[]) => any, { vm, locale }: {
78
89
  vm: object;
79
90
  locale?: string;
80
- MenuItem: any;
81
- MenuSection: any;
82
- onRequestClose(): void;
83
91
  }): any;
92
+ /**
93
+ * Renders the history graph of the repository chosen in the panel mounted for `vm`, for a side panel of the host. It
94
+ * follows branch changes made anywhere, offers the graph's branch actions when the panel allows writes and tells their
95
+ * results in the branch button's snackbar. Before a repository is chosen it offers to choose one; it renders nothing
96
+ * while no panel is mounted for `vm`.
97
+ */
98
+ export declare function renderHistoryView(createElement: (component: any, props: any, ...children: any[]) => any, { vm, locale }: {
99
+ vm: object;
100
+ locale?: string;
101
+ }): any;
102
+ /**
103
+ * Follows the Git status of the files under `directory`, as the panel mounted for `vm` last read it, and calls
104
+ * `listener` with their marks by path relative to `directory`. The marks are empty while no repository is chosen or
105
+ * `directory` is outside it. Returns the function that stops following.
106
+ */
107
+ export declare function followChangeMarks(vm: object, directory: FileSystemDirectoryHandle, listener: (marks: ReadonlyMap<string, ChangeMark>) => void): () => void;