@kubohiroya/turbowarp-plus-editor-git 0.9.0 → 0.11.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.
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
- import { a as gitHubRepositoryFromOrigin, c as projectPropertyLabel, g as useCostumeImage, h as useAssetImage, i as createGitHubPullRequests, l as ProjectSourceComparison, r as PullRequestError, s as organizeProjectChanges, t as createScratchBlocksDiffRenderer, u as ProjectSourceReview, y as parseProjectSource } from "./scratch-block-diff-renderer-DZR8ep-T.js";
1
+ import { a as gitHubRepositoryFromOrigin, c as projectPropertyLabel, g as useCostumeImage, h as useAssetImage, i as createGitHubPullRequests, l as ProjectSourceComparison, n as createScratchBlocksDiffLocator, r as PullRequestError, s as organizeProjectChanges, t as createScratchBlocksDiffRenderer, u as ProjectSourceReview, y as parseProjectSource } from "./scratch-block-diff-renderer-CgAGfmAc.js";
2
2
  import { n as init_objectSpread2, r as __commonJSMin, t as _objectSpread2$1 } from "./objectSpread2-Dkd68nJH.js";
3
- import { n as GitPanelError, r as createReadOnlyFs, t as openRepository } from "./repository-VBp-UNWc.js";
3
+ import { i as createReadOnlyFs, r as GitPanelError, t as openRepository } from "./repository-CtvWYPQ8.js";
4
4
  import * as m from "react";
5
5
  import React, { createContext, memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
6
- import { render, unmountComponentAtNode } from "react-dom";
6
+ import { createPortal, render, unmountComponentAtNode } from "react-dom";
7
7
  // builtin:esm-external-require-react
8
8
  var require_builtin_esm_external_require_react = /* @__PURE__ */ __commonJSMin(((exports, module) => {
9
9
  init_objectSpread2();
@@ -2684,12 +2684,12 @@ var isProjectSource = (path) => path.split("/").pop() === "project.source.json";
2684
2684
  /**
2685
2685
  * Shows one changed file. Project sources are compared as blocks by default, with their text diff one switch away.
2686
2686
  */
2687
- function FileDiffView({ change, diff, labels, locale, renderBlock, loadAsset }) {
2687
+ function FileDiffView({ change, diff, labels, locale, renderBlock, loadAsset, locateBlock }) {
2688
2688
  var _change$oldMode, _change$newMode;
2689
2689
  const [view, setView] = useState("blocks");
2690
2690
  const names = locale.startsWith("ja") ? ja$3 : en$3;
2691
2691
  const parsed = (diff === null || diff === void 0 ? void 0 : diff.patch) ? parseDiff(diff.patch)[0] : null;
2692
- const text = /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("p", null, labels.mode, " · ", ((_change$oldMode = change.oldMode) === null || _change$oldMode === void 0 ? void 0 : _change$oldMode.toString(8)) || "—", " → ", ((_change$newMode = change.newMode) === null || _change$newMode === void 0 ? void 0 : _change$newMode.toString(8)) || "—"), diff && /* @__PURE__ */ React.createElement("p", null, diff.beforeSize, " → ", diff.afterSize, " ", labels.bytes), (diff === null || diff === void 0 ? void 0 : diff.binary) && /* @__PURE__ */ React.createElement("p", null, labels.binary), (diff === null || diff === void 0 ? void 0 : diff.tooLarge) && /* @__PURE__ */ React.createElement("p", null, labels.large), parsed && /* @__PURE__ */ React.createElement(index, {
2692
+ const text = /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("details", { className: "twgit-file-details" }, /* @__PURE__ */ React.createElement("summary", null, labels.details), /* @__PURE__ */ React.createElement("p", null, labels.mode, " · ", ((_change$oldMode = change.oldMode) === null || _change$oldMode === void 0 ? void 0 : _change$oldMode.toString(8)) || "—", " → ", ((_change$newMode = change.newMode) === null || _change$newMode === void 0 ? void 0 : _change$newMode.toString(8)) || "—"), diff && /* @__PURE__ */ React.createElement("p", null, diff.beforeSize, " → ", diff.afterSize, " ", labels.bytes)), (diff === null || diff === void 0 ? void 0 : diff.binary) && /* @__PURE__ */ React.createElement("p", null, labels.binary), (diff === null || diff === void 0 ? void 0 : diff.tooLarge) && /* @__PURE__ */ React.createElement("p", null, labels.large), parsed && /* @__PURE__ */ React.createElement(index, {
2693
2693
  viewType: "unified",
2694
2694
  diffType: parsed.type,
2695
2695
  hunks: parsed.hunks
@@ -2713,6 +2713,7 @@ function FileDiffView({ change, diff, labels, locale, renderBlock, loadAsset })
2713
2713
  locale,
2714
2714
  fallback: text,
2715
2715
  loadAsset,
2716
+ locateBlock,
2716
2717
  before: change.before === null ? null : sources.before,
2717
2718
  after: change.after === null ? null : sources.after
2718
2719
  }) : text);
@@ -2746,10 +2747,10 @@ var en$2 = {
2746
2747
  file: "File name",
2747
2748
  noImage: "No image",
2748
2749
  groups: {
2749
- staged: "Staged",
2750
- unstaged: "Unstaged",
2751
- untracked: "Untracked",
2752
- commit: "Commit"
2750
+ staged: "in the next record",
2751
+ unstaged: "not yet recorded",
2752
+ untracked: "new file",
2753
+ commit: "in this record"
2753
2754
  }
2754
2755
  };
2755
2756
  var ja$2 = {
@@ -2780,10 +2781,10 @@ var ja$2 = {
2780
2781
  file: "ファイル名",
2781
2782
  noImage: "画像なし",
2782
2783
  groups: {
2783
- staged: "ステージ済み",
2784
- unstaged: "未ステージ",
2785
- untracked: "未追跡",
2786
- commit: "コミット"
2784
+ staged: "次の記録に入る",
2785
+ unstaged: "まだ記録していない",
2786
+ untracked: "新しいファイル",
2787
+ commit: "この記録"
2787
2788
  }
2788
2789
  };
2789
2790
  var projectChangeLabels = (locale) => locale.startsWith("ja") ? ja$2 : en$2;
@@ -2965,7 +2966,7 @@ var show = (value) => {
2965
2966
  return text.length > 400 ? text.slice(0, 400) + "…" : text;
2966
2967
  };
2967
2968
  /** The details of one selected row: its code, pictures or values in both versions, and the files behind it. */
2968
- function ProjectElementView({ changes, selection, locale, renderBlock, onSelectFile }) {
2969
+ function ProjectElementView({ changes, selection, locale, renderBlock, onSelectFile, locateBlock }) {
2969
2970
  var _element$properties2;
2970
2971
  const labels = projectChangeLabels(locale);
2971
2972
  const { element, target } = selection;
@@ -3000,7 +3001,8 @@ function ProjectElementView({ changes, selection, locale, renderBlock, onSelectF
3000
3001
  target: {
3001
3002
  isStage: target.isStage,
3002
3003
  name: target.name
3003
- }
3004
+ },
3005
+ locateBlock
3004
3006
  }), element.kind === "costumes" && /* @__PURE__ */ React.createElement("div", { className: "twgit-element-pictures" }, ["before", "after"].map((version) => {
3005
3007
  var _element$md5ext3, _element$version;
3006
3008
  return /* @__PURE__ */ React.createElement(Picture, {
@@ -3039,15 +3041,15 @@ var messages = {
3039
3041
  replace: "Replace the open project and discard its unsaved changes?",
3040
3042
  dirty: "The Editor has unsaved changes.",
3041
3043
  saved: "The Editor has no unsaved changes.",
3042
- staged: "Staged",
3043
- unstaged: "Unstaged",
3044
- untracked: "Untracked",
3045
- commit: "Commit changes",
3046
- history: "History",
3047
- worktree: "Working tree",
3048
- clean: "No changes in the selected folder.",
3044
+ staged: "Changes in the next record",
3045
+ unstaged: "Saved, not yet recorded",
3046
+ untracked: "New files never recorded",
3047
+ commit: "Changes in this record",
3048
+ history: "Records (commits)",
3049
+ worktree: "Saved files",
3050
+ clean: "No saved changes in the selected folder.",
3049
3051
  branches: "Local branches",
3050
- unborn: "No commits yet",
3052
+ unborn: "No records yet",
3051
3053
  detached: "Detached HEAD",
3052
3054
  select: "Select a file to view its diff.",
3053
3055
  binary: "Binary asset",
@@ -3061,16 +3063,33 @@ var messages = {
3061
3063
  changed: "Files or Git state changed while reading. Refresh to read the current state.",
3062
3064
  outdated: "The repository changed outside the Editor. Refresh to read the current state.",
3063
3065
  unsupported: "This repository contains an entry the browser cannot represent.",
3064
- pullUnsafe: "Stop the project and clear unsaved or local Git changes before pulling.",
3065
- diverged: "Pull requires a fast-forward. Your local branch and files are intact.",
3066
+ pullUnsafe: "Stop the project, and save or undo unsaved edits and unrecorded changes, before getting changes from GitHub (pull).",
3067
+ diverged: "This branch and GitHub's have both gained records, so they cannot be combined here (pull only fast-forwards). Your branch and files are unchanged.",
3066
3068
  session: "Reconnect GitHub for this repository and requested permission.",
3067
- pushUnknown: "The push result is unknown. Fetch and check the remote branch before retrying.",
3068
- pushRefresh: "The push succeeded. Fetch to refresh the local tracking branch.",
3069
- rollbackFailed: "Git could not restore all local changes. Check the working tree and Git status.",
3070
- remoteFailed: "The Git operation failed. Check the selected connection and retry.",
3071
- remoteDone: "Git operation completed.",
3072
- pulledProject: "Pull completed. Use Reload Project to open the updated files.",
3073
- confirmPush: "Push the current branch to its GitHub origin?",
3069
+ pushUnknown: "It is unknown whether GitHub received the records. Check GitHub (fetch) before sending again.",
3070
+ pushRefresh: "GitHub received the records. Check GitHub (fetch) to update the status.",
3071
+ rollbackFailed: "Some local changes could not be restored. Check the files and the Git details.",
3072
+ remoteFailed: "Could not complete the GitHub operation. Check the connection and try again.",
3073
+ remoteDone: "The GitHub operation finished.",
3074
+ pulledProject: "Got the changes from GitHub. Use Reload Project to open the updated files.",
3075
+ confirmPush: "Send the records on this branch to GitHub (push)?",
3076
+ details: "Git details",
3077
+ branch: "Branch",
3078
+ head: "HEAD",
3079
+ groups: "Git groups",
3080
+ added: "Added",
3081
+ modified: "Changed",
3082
+ deleted: "Deleted",
3083
+ gitStaged: "Staged",
3084
+ gitUnstaged: "Unstaged",
3085
+ gitUntracked: "Untracked",
3086
+ upstreamSame: "Same as GitHub",
3087
+ upstreamAhead: "Ahead of GitHub",
3088
+ upstreamBehind: "Behind GitHub",
3089
+ upstreamDiverged: "Diverged from GitHub",
3090
+ upstreamUnknown: "Not checked with GitHub yet",
3091
+ records: "records",
3092
+ checkedAt: "as of the last check",
3074
3093
  failed: "Could not read the repository. Check folder access and refresh."
3075
3094
  },
3076
3095
  ja: {
@@ -3090,18 +3109,18 @@ var messages = {
3090
3109
  reload: "作品を再読込",
3091
3110
  up: "上のフォルダ",
3092
3111
  browse: "リポジトリ内の作品を選択",
3093
- replace: "未保存の変更を破棄して、別の作品を開きますか?",
3094
- dirty: "Editor に未保存の変更があります。",
3095
- saved: "Editor に未保存の変更はありません。",
3096
- staged: "ステージ済み",
3097
- unstaged: "未ステージ",
3098
- untracked: "未追跡",
3099
- commit: "コミットの変更",
3100
- history: "履歴",
3101
- worktree: "作業ツリー",
3102
- clean: "対象フォルダに変更はありません。",
3112
+ replace: "保存していない編集を破棄して、別の作品を開きますか?",
3113
+ dirty: "Editor に保存していない編集があります。",
3114
+ saved: "Editor に保存していない編集はありません。",
3115
+ staged: "次の記録に入る変更",
3116
+ unstaged: "保存したが、まだ記録していない変更",
3117
+ untracked: "まだ記録したことのない新しいファイル",
3118
+ commit: "この記録の変更",
3119
+ history: "記録(コミット)の履歴",
3120
+ worktree: "保存したファイル",
3121
+ clean: "対象フォルダに、保存した変更はありません。",
3103
3122
  branches: "ローカルブランチ",
3104
- unborn: "コミットはまだありません",
3123
+ unborn: "まだ記録がありません",
3105
3124
  detached: "ブランチ未選択の HEAD",
3106
3125
  select: "ファイルを選ぶと差分を表示します。",
3107
3126
  binary: "バイナリアセット",
@@ -3115,16 +3134,33 @@ var messages = {
3115
3134
  changed: "読込中にファイルまたは Git の状態が変わりました。再取得してください。",
3116
3135
  outdated: "Editor の外でリポジトリが変更されました。再取得すると最新の状態を読み込みます。",
3117
3136
  unsupported: "ブラウザーで扱えない種類の Git エントリーがあります。",
3118
- pullUnsafe: "pull の前に実行を止め、未保存編集とローカルの Git 変更を解消してください。",
3119
- diverged: "fast-forward できません。ローカルブランチとファイルは保持されています。",
3137
+ pullUnsafe: "GitHub から取り込む前に、実行を止め、保存していない編集とまだ記録していない変更をなくしてください。",
3138
+ diverged: "このブランチと GitHub の両方に新しい記録があるため、ここでは取り込めません(pull は fast-forward のみ)。ブランチとファイルはそのままです。",
3120
3139
  session: "このリポジトリと操作に必要な権限で GitHub に接続してください。",
3121
- pushUnknown: "push の結果が不明です。再試行する前に fetch でリモートブランチを確認してください。",
3122
- pushRefresh: "push は成功しました。fetch でローカルの追跡ブランチを更新してください。",
3123
- rollbackFailed: "Git がローカルの変更をすべて復元できませんでした。作業ツリーと Git の状態を確認してください。",
3124
- remoteFailed: "Git 操作に失敗しました。選択中の接続を確認して再試行してください。",
3125
- remoteDone: "Git 操作が完了しました。",
3126
- pulledProject: "pull が完了しました。「作品を再読込」で更新済みのファイルを開けます。",
3127
- confirmPush: "現在のブランチを GitHub の origin に push しますか?",
3140
+ pushUnknown: "GitHub が記録を受け取ったか分かりません。送り直す前に「GitHub と確かめる」を使ってください。",
3141
+ pushRefresh: "GitHub が記録を受け取りました。「GitHub と確かめる」で状態を更新してください。",
3142
+ rollbackFailed: "一部のローカルの変更を元に戻せませんでした。ファイルと Git の詳細を確認してください。",
3143
+ remoteFailed: "GitHub との操作を完了できませんでした。接続を確認して、もう一度試してください。",
3144
+ remoteDone: "GitHub との操作が終わりました。",
3145
+ pulledProject: "GitHub から取り込みました。「作品を再読込」で更新したファイルを開けます。",
3146
+ confirmPush: "このブランチの記録を GitHub に送りますか(push)?",
3147
+ details: "Git の詳細",
3148
+ branch: "ブランチ",
3149
+ head: "HEAD",
3150
+ groups: "Git の区分",
3151
+ added: "追加",
3152
+ modified: "変更",
3153
+ deleted: "削除",
3154
+ gitStaged: "ステージ済み (staged)",
3155
+ gitUnstaged: "未ステージ (unstaged)",
3156
+ gitUntracked: "未追跡 (untracked)",
3157
+ upstreamSame: "GitHub と同じ",
3158
+ upstreamAhead: "GitHub より進んでいる",
3159
+ upstreamBehind: "GitHub より遅れている",
3160
+ upstreamDiverged: "GitHub と分かれている",
3161
+ upstreamUnknown: "まだ GitHub と確かめていない",
3162
+ records: "件",
3163
+ checkedAt: "最後に確かめた時点",
3128
3164
  failed: "リポジトリを読み込めませんでした。フォルダへのアクセスを確認し、再取得してください。"
3129
3165
  }
3130
3166
  };
@@ -3151,10 +3187,10 @@ var en$1 = {
3151
3187
  changed: "Switching disconnects the current session.",
3152
3188
  error: "GitHub connection failed. Retry using the selected connection.",
3153
3189
  invalid: "The selected repository needs a GitHub HTTPS origin.",
3154
- allowPush: "Authorize push",
3155
- fetch: "Fetch",
3156
- pull: "Pull (fast-forward only)",
3157
- push: "Push"
3190
+ allowPush: "Allow sending to GitHub (push)",
3191
+ fetch: "Check GitHub (fetch)",
3192
+ pull: "Get from GitHub (pull)",
3193
+ push: "Send to GitHub (push)"
3158
3194
  };
3159
3195
  var ja$1 = {
3160
3196
  title: "GitHub 接続",
@@ -3177,10 +3213,10 @@ var ja$1 = {
3177
3213
  changed: "方式を切り替えると現在のセッションを切断します。",
3178
3214
  error: "GitHub 接続に失敗しました。選択中の方式で再試行してください。",
3179
3215
  invalid: "選択したリポジトリには GitHub の HTTPS リモート origin が必要です。",
3180
- allowPush: "push を許可する",
3181
- fetch: "Fetch",
3182
- pull: "Pull(fast-forward のみ)",
3183
- push: "Push"
3216
+ allowPush: "GitHub に送ることを許可する(push)",
3217
+ fetch: "GitHub と確かめる(fetch)",
3218
+ pull: "GitHub から取り込む(pull)",
3219
+ push: "GitHub に送る(push)"
3184
3220
  };
3185
3221
  function ConnectionSettings({ settings, locale, readOrigin, runOperation }) {
3186
3222
  const labels = locale.startsWith("ja") ? ja$1 : en$1;
@@ -3216,7 +3252,16 @@ function ConnectionSettings({ settings, locale, readOrigin, runOperation }) {
3216
3252
  if (!remote) throw new Error("INVALID_REMOTE");
3217
3253
  await settings.connect(remote, allowPush);
3218
3254
  });
3219
- return /* @__PURE__ */ React.createElement("details", { className: "twgit-connection" }, /* @__PURE__ */ React.createElement("summary", null, labels.title), !settings.modes.length ? /* @__PURE__ */ React.createElement("p", null, labels.unavailable) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("label", null, labels.mode, " ", /* @__PURE__ */ React.createElement("select", {
3255
+ const connected = !!mode && state.status === "connected";
3256
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, runOperation && connected && /* @__PURE__ */ React.createElement("span", { className: "twgit-sync" }, [
3257
+ "fetch",
3258
+ "pull",
3259
+ "push"
3260
+ ].map((operation) => /* @__PURE__ */ React.createElement("button", {
3261
+ key: operation,
3262
+ disabled: busy || !readOrigin || operation === "push" && !write,
3263
+ onClick: () => void run(() => runOperation(operation))
3264
+ }, labels[operation]))), /* @__PURE__ */ React.createElement("details", { className: "twgit-connection" }, /* @__PURE__ */ React.createElement("summary", null, labels.title), /* @__PURE__ */ React.createElement("div", { className: "twgit-connection-body" }, !settings.modes.length ? /* @__PURE__ */ React.createElement("p", null, labels.unavailable) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("label", null, labels.mode, " ", /* @__PURE__ */ React.createElement("select", {
3220
3265
  value: mode !== null && mode !== void 0 ? mode : "",
3221
3266
  disabled: busy,
3222
3267
  onChange: (event) => {
@@ -3250,20 +3295,13 @@ function ConnectionSettings({ settings, locale, readOrigin, runOperation }) {
3250
3295
  }, labels.check), /* @__PURE__ */ React.createElement("button", {
3251
3296
  disabled: busy,
3252
3297
  onClick: () => void run(settings.disconnect)
3253
- }, labels.disconnect), runOperation && state.status === "connected" && [
3254
- "fetch",
3255
- "pull",
3256
- "push"
3257
- ].map((operation) => /* @__PURE__ */ React.createElement("button", {
3258
- key: operation,
3259
- disabled: busy || !readOrigin || operation === "push" && !write,
3260
- onClick: () => void run(() => runOperation(operation))
3261
- }, labels[operation]))), error && /* @__PURE__ */ React.createElement("p", {
3298
+ }, labels.disconnect))))), error && /* @__PURE__ */ React.createElement("p", {
3262
3299
  className: "twgit-error",
3263
3300
  role: "alert"
3264
- }, labels[error])));
3301
+ }, labels[error]));
3265
3302
  }
3266
3303
  // src/pull-request-panel.tsx
3304
+ init_objectSpread2();
3267
3305
  var en = {
3268
3306
  title: "Pull requests",
3269
3307
  number: "Pull request number",
@@ -3309,7 +3347,7 @@ var ja = {
3309
3347
  function errorMessage(error, labels) {
3310
3348
  return error instanceof PullRequestError ? labels[error.code] : labels.failed;
3311
3349
  }
3312
- function PullRequests({ name, labels, locale, renderBlock }) {
3350
+ function PullRequests({ name, labels, locale, renderBlock, locateFor }) {
3313
3351
  const client = useMemo(() => createGitHubPullRequests(name), [name]);
3314
3352
  const [page, setPage] = useState(1);
3315
3353
  const [listing, setListing] = useState();
@@ -3397,12 +3435,12 @@ function PullRequests({ name, labels, locale, renderBlock }) {
3397
3435
  }, labels.onGitHub)), /* @__PURE__ */ React.createElement(ProjectSourceReview, {
3398
3436
  title: pr.title,
3399
3437
  description: pr.description,
3400
- files: pr.files,
3438
+ files: locateFor ? pr.files.map((file) => "patch" in file ? _objectSpread2$1(_objectSpread2$1({}, file), {}, { locateBlock: locateFor(file.path, pr.head.sha) }) : file) : pr.files,
3401
3439
  renderBlock,
3402
3440
  locale
3403
3441
  }))));
3404
3442
  }
3405
- function PullRequestReviewPanel({ repository, locale, renderBlock }) {
3443
+ function PullRequestReviewPanel({ repository, locale, renderBlock, locateFor }) {
3406
3444
  const labels = locale.startsWith("ja") ? ja : en;
3407
3445
  const [origin, setOrigin] = useState();
3408
3446
  useEffect(() => {
@@ -3436,7 +3474,44 @@ function PullRequestReviewPanel({ repository, locale, renderBlock }) {
3436
3474
  name: origin.name,
3437
3475
  labels,
3438
3476
  locale,
3439
- renderBlock
3477
+ renderBlock,
3478
+ locateFor
3479
+ });
3480
+ }
3481
+ // src/editor-block-locator.ts
3482
+ /**
3483
+ * Finds the stage or sprite of a compared version in the open project, opens its code and waits for the block,
3484
+ * then centers and outlines it. The caller decides which version matches the open project.
3485
+ */
3486
+ function createEditorBlockLocator({ vm, openTargetCode, getWorkspace }) {
3487
+ return createScratchBlocksDiffLocator(async ({ target, id: blockId, signal }) => {
3488
+ const found = vm.runtime.targets.find((item) => target.isStage ? item.isStage : !item.isStage && item.isOriginal !== false && item.getName() === target.name);
3489
+ if (!found || signal.aborted) return null;
3490
+ await openTargetCode(found.id);
3491
+ const deadline = Date.now() + waitLimit;
3492
+ while (!signal.aborted) {
3493
+ var _vm$editingTarget;
3494
+ if (((_vm$editingTarget = vm.editingTarget) === null || _vm$editingTarget === void 0 ? void 0 : _vm$editingTarget.id) === found.id) {
3495
+ const workspace = await getWorkspace();
3496
+ if (signal.aborted) break;
3497
+ if (workspace === null || workspace === void 0 ? void 0 : workspace.getBlockById(blockId)) return workspace;
3498
+ }
3499
+ if (Date.now() >= deadline) break;
3500
+ await nextFrame(signal);
3501
+ }
3502
+ return null;
3503
+ });
3504
+ }
3505
+ var waitLimit = 2e3;
3506
+ function nextFrame(signal) {
3507
+ return new Promise((resolve) => {
3508
+ const done = () => {
3509
+ signal.removeEventListener("abort", done);
3510
+ resolve();
3511
+ };
3512
+ signal.addEventListener("abort", done, { once: true });
3513
+ if (typeof requestAnimationFrame === "function") requestAnimationFrame(done);
3514
+ else setTimeout(done, 16);
3440
3515
  });
3441
3516
  }
3442
3517
  // src/index.tsx
@@ -3455,9 +3530,20 @@ var initial = {
3455
3530
  };
3456
3531
  var controllers = /* @__PURE__ */ new WeakMap();
3457
3532
  var watchInterval = 2e3;
3458
- function GitPanel({ document, host, controller }) {
3459
- var _state$repository, _state$snapshot, _state$snapshot2, _state$snapshot3, _state$snapshot4, _state$snapshot5;
3533
+ function GitPanel({ vm, document, host, controller }) {
3534
+ var _state$repository, _state$snapshot$head$, _state$snapshot$head, _state$snapshot2;
3460
3535
  const [visible, setVisible] = useState(false);
3536
+ const [slot, setSlot] = useState(null);
3537
+ const shown = visible || slot !== null;
3538
+ const locator = useMemo(() => slot && host.openTargetCode && host.getWorkspace ? createEditorBlockLocator({
3539
+ vm,
3540
+ openTargetCode: host.openTargetCode,
3541
+ getWorkspace: host.getWorkspace
3542
+ }) : void 0, [
3543
+ slot !== null,
3544
+ host,
3545
+ vm
3546
+ ]);
3461
3547
  const [review, setReview] = useState(false);
3462
3548
  const [locale, setLocale] = useState(document.documentElement.lang || "en");
3463
3549
  const [dirty, setDirty] = useState(host.isProjectDirty());
@@ -3515,8 +3601,34 @@ function GitPanel({ document, host, controller }) {
3515
3601
  }
3516
3602
  };
3517
3603
  controller.open = (language) => {
3604
+ var _host$openPanel;
3518
3605
  setLocale(language);
3519
- setVisible(true);
3606
+ if (!((_host$openPanel = host.openPanel) === null || _host$openPanel === void 0 ? void 0 : _host$openPanel.call(host, {
3607
+ reference: {
3608
+ providerId: "turbowarp-plus-git",
3609
+ documentId: "local-git"
3610
+ },
3611
+ filename: "Git",
3612
+ title: labelsFor(language).title,
3613
+ transient: true,
3614
+ mount(element, close) {
3615
+ setVisible(false);
3616
+ setSlot({
3617
+ element,
3618
+ close
3619
+ });
3620
+ return {
3621
+ requestClose: close,
3622
+ dispose() {
3623
+ setSlot((previous) => (previous === null || previous === void 0 ? void 0 : previous.element) === element ? null : previous);
3624
+ }
3625
+ };
3626
+ }
3627
+ }))) setVisible(true);
3628
+ };
3629
+ const closePanel = () => {
3630
+ if (slot) slot.close();
3631
+ else setVisible(false);
3520
3632
  };
3521
3633
  controller.refresh = async (automatic = false) => {
3522
3634
  if (!alive.current || !current.current.repository) return;
@@ -3565,7 +3677,7 @@ function GitPanel({ document, host, controller }) {
3565
3677
  }, [host]);
3566
3678
  useEffect(() => {
3567
3679
  const repository = state.repository;
3568
- if (!visible || !repository) return;
3680
+ if (!shown || !repository) return;
3569
3681
  const view = document.defaultView;
3570
3682
  let stopped = false;
3571
3683
  let checking = false;
@@ -3612,19 +3724,19 @@ function GitPanel({ document, host, controller }) {
3612
3724
  observer === null || observer === void 0 || observer.disconnect();
3613
3725
  };
3614
3726
  }, [
3615
- visible,
3727
+ shown,
3616
3728
  state.repository,
3617
3729
  locale
3618
3730
  ]);
3619
3731
  useEffect(() => {
3620
3732
  var _dialog$current;
3621
- if (!visible) return;
3733
+ if (!visible || slot) return;
3622
3734
  const previous = document.activeElement;
3623
3735
  (_dialog$current = dialog.current) === null || _dialog$current === void 0 || _dialog$current.focus();
3624
3736
  return () => {
3625
3737
  if (previous === null || previous === void 0 ? void 0 : previous.isConnected) previous.focus();
3626
3738
  };
3627
- }, [visible]);
3739
+ }, [visible, slot]);
3628
3740
  const chooseRoot = async () => {
3629
3741
  const view = document.defaultView;
3630
3742
  await run(async () => {
@@ -3661,6 +3773,7 @@ function GitPanel({ document, host, controller }) {
3661
3773
  } finally {
3662
3774
  opening.current = false;
3663
3775
  }
3776
+ if (slot) controller.open(locale);
3664
3777
  if (!alive.current) return {};
3665
3778
  setDirty(host.isProjectDirty());
3666
3779
  setState((previous) => _objectSpread2$1(_objectSpread2$1({}, previous), {}, {
@@ -3741,7 +3854,7 @@ function GitPanel({ document, host, controller }) {
3741
3854
  const focusTrap = (event) => {
3742
3855
  if (event.key === "Escape") {
3743
3856
  event.stopPropagation();
3744
- setVisible(false);
3857
+ closePanel();
3745
3858
  return;
3746
3859
  }
3747
3860
  if (event.key !== "Tab") return;
@@ -3760,23 +3873,21 @@ function GitPanel({ document, host, controller }) {
3760
3873
  state.snapshot,
3761
3874
  state.selected
3762
3875
  ]);
3763
- if (!visible) return null;
3876
+ if (!shown) return null;
3764
3877
  const groups = state.commit ? ["commit"] : [
3765
3878
  "staged",
3766
3879
  "unstaged",
3767
3880
  "untracked"
3768
3881
  ];
3769
3882
  const projectList = typeof projectChanges === "object" ? projectChanges : null;
3883
+ const projectFile = state.project === null ? null : [state.project, "project.source.json"].filter(Boolean).join("/");
3884
+ const fileLocator = (change) => locator && change.path === projectFile && (change.group === "unstaged" || change.group === "untracked" || change.group === "staged" && !state.changes.some((item) => item.path === change.path && item.group === "unstaged")) ? locator : void 0;
3885
+ const reviewLocator = (path, headSha) => {
3886
+ var _state$snapshot;
3887
+ return locator && path === projectFile && ((_state$snapshot = state.snapshot) === null || _state$snapshot === void 0 ? void 0 : _state$snapshot.head) === headSha && !state.changes.length ? locator : void 0;
3888
+ };
3770
3889
  const selection = projectList && !state.selected ? findElement(projectList.list, element) : null;
3771
- return /* @__PURE__ */ React.createElement("div", { className: "twgit-backdrop" }, /* @__PURE__ */ React.createElement("div", {
3772
- className: "twgit-panel",
3773
- ref: dialog,
3774
- tabIndex: -1,
3775
- role: "dialog",
3776
- "aria-modal": "true",
3777
- "aria-label": labels.title,
3778
- onKeyDown: focusTrap
3779
- }, /* @__PURE__ */ React.createElement("header", null, /* @__PURE__ */ React.createElement("h2", null, labels.title), /* @__PURE__ */ React.createElement("button", {
3890
+ const content = /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("header", null, /* @__PURE__ */ React.createElement("h2", null, labels.title), /* @__PURE__ */ React.createElement("button", {
3780
3891
  onClick: chooseRoot,
3781
3892
  disabled: state.busy
3782
3893
  }, labels.choose), !review && /* @__PURE__ */ React.createElement("button", {
@@ -3789,7 +3900,10 @@ function GitPanel({ document, host, controller }) {
3789
3900
  type: "button",
3790
3901
  disabled: state.busy,
3791
3902
  onClick: () => setReview(!review)
3792
- }, review ? labels.localChanges : labels.pullRequests), /* @__PURE__ */ React.createElement("button", { onClick: () => setVisible(false) }, labels.close)), /* @__PURE__ */ React.createElement("div", { className: "twgit-context" }, /* @__PURE__ */ React.createElement("span", null, labels.repository, " · ", ((_state$repository = state.repository) === null || _state$repository === void 0 ? void 0 : _state$repository.root.name) || "—"), /* @__PURE__ */ React.createElement("span", null, labels.project, " · ", state.project === null ? labels.none : state.project || labels.root), /* @__PURE__ */ React.createElement("strong", null, ((_state$snapshot = state.snapshot) === null || _state$snapshot === void 0 ? void 0 : _state$snapshot.branch) || (((_state$snapshot2 = state.snapshot) === null || _state$snapshot2 === void 0 ? void 0 : _state$snapshot2.head) ? labels.detached : labels.unborn)), /* @__PURE__ */ React.createElement("code", null, (_state$snapshot3 = state.snapshot) === null || _state$snapshot3 === void 0 || (_state$snapshot3 = _state$snapshot3.head) === null || _state$snapshot3 === void 0 ? void 0 : _state$snapshot3.slice(0, 12)), /* @__PURE__ */ React.createElement("span", { "data-git-dirty": "" }, dirty ? labels.dirty : labels.saved)), !review && host.connectionSettings && /* @__PURE__ */ React.createElement(ConnectionSettings, {
3903
+ }, review ? labels.localChanges : labels.pullRequests), /* @__PURE__ */ React.createElement("button", { onClick: closePanel }, labels.close)), /* @__PURE__ */ React.createElement("div", { className: "twgit-context" }, /* @__PURE__ */ React.createElement("span", null, labels.repository, " · ", ((_state$repository = state.repository) === null || _state$repository === void 0 ? void 0 : _state$repository.root.name) || "—"), /* @__PURE__ */ React.createElement("span", null, labels.project, " · ", state.project === null ? labels.none : state.project || labels.root), /* @__PURE__ */ React.createElement("span", { "data-git-dirty": "" }, dirty ? labels.dirty : labels.saved), state.snapshot && /* @__PURE__ */ React.createElement("span", {
3904
+ "data-git-upstream": state.snapshot.upstream.state,
3905
+ title: labels.checkedAt
3906
+ }, upstreamText(state.snapshot, labels)), !review && host.connectionSettings && /* @__PURE__ */ React.createElement(ConnectionSettings, {
3793
3907
  settings: host.connectionSettings,
3794
3908
  locale,
3795
3909
  runOperation: host.connectionSettings.getSession ? remoteOperation : void 0,
@@ -3798,7 +3912,7 @@ function GitPanel({ document, host, controller }) {
3798
3912
  const remote = await state.repository.readOrigin();
3799
3913
  return epoch.current === ticket ? remote : void 0;
3800
3914
  } : void 0
3801
- }), state.error && /* @__PURE__ */ React.createElement("p", {
3915
+ }), state.snapshot && /* @__PURE__ */ React.createElement("details", { className: "twgit-git-details" }, /* @__PURE__ */ React.createElement("summary", null, labels.details), /* @__PURE__ */ React.createElement("div", { className: "twgit-connection-body" }, /* @__PURE__ */ React.createElement("p", null, labels.branch, " · ", /* @__PURE__ */ React.createElement("strong", null, state.snapshot.branch || (state.snapshot.head ? labels.detached : labels.unborn))), /* @__PURE__ */ React.createElement("p", null, labels.head, " · ", /* @__PURE__ */ React.createElement("code", null, (_state$snapshot$head$ = (_state$snapshot$head = state.snapshot.head) === null || _state$snapshot$head === void 0 ? void 0 : _state$snapshot$head.slice(0, 12)) !== null && _state$snapshot$head$ !== void 0 ? _state$snapshot$head$ : "—")), /* @__PURE__ */ React.createElement("p", null, labels.branches, " · ", state.snapshot.branches.join(", ") || "—"), /* @__PURE__ */ React.createElement("p", null, labels.groups, " · ", labels.gitStaged, " = ", labels.staged, " / ", labels.gitUnstaged, " = ", labels.unstaged, " / ", labels.gitUntracked, " = ", labels.untracked), /* @__PURE__ */ React.createElement("p", null, labels.permissions)))), state.error && /* @__PURE__ */ React.createElement("p", {
3802
3916
  className: "twgit-error",
3803
3917
  role: "alert"
3804
3918
  }, state.error), state.notice && /* @__PURE__ */ React.createElement("p", {
@@ -3807,7 +3921,8 @@ function GitPanel({ document, host, controller }) {
3807
3921
  }, state.notice), state.busy && /* @__PURE__ */ React.createElement("p", { role: "status" }, labels.loading), !state.repository && /* @__PURE__ */ React.createElement("p", null, labels.empty), review && state.repository && host.renderReviewBlock && /* @__PURE__ */ React.createElement(PullRequestReviewPanel, {
3808
3922
  repository: state.repository,
3809
3923
  locale,
3810
- renderBlock: host.renderReviewBlock
3924
+ renderBlock: host.renderReviewBlock,
3925
+ locateFor: reviewLocator
3811
3926
  }), !review && state.repository && /* @__PURE__ */ React.createElement("div", { className: "twgit-content" }, /* @__PURE__ */ React.createElement("aside", null, state.browser && /* @__PURE__ */ React.createElement("details", null, /* @__PURE__ */ React.createElement("summary", null, labels.browse), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("code", null, state.browser.path || "/")), /* @__PURE__ */ React.createElement("button", {
3812
3927
  disabled: state.busy || !state.browser.path,
3813
3928
  onClick: () => void browse(state.browser.path.split("/").slice(0, -1).join("/"))
@@ -3828,7 +3943,7 @@ function GitPanel({ document, host, controller }) {
3828
3943
  disabled: state.busy,
3829
3944
  "aria-pressed": state.selected === change,
3830
3945
  onClick: () => openFile(change)
3831
- }, /* @__PURE__ */ React.createElement("span", null, !change.after ? "D" : !change.before ? "A" : "M"), " ", change.path))))));
3946
+ }, /* @__PURE__ */ React.createElement("small", { className: "twgit-change-kind" }, !change.after ? labels.deleted : !change.before ? labels.added : labels.modified), " ", change.path))))));
3832
3947
  return projectList ? /* @__PURE__ */ React.createElement(ProjectChangeListView, {
3833
3948
  changes: projectList,
3834
3949
  selected: selection ? element : null,
@@ -3840,15 +3955,16 @@ function GitPanel({ document, host, controller }) {
3840
3955
  fileListOpen,
3841
3956
  setFileListOpen
3842
3957
  }) : fileList;
3843
- })(), !state.changes.length && /* @__PURE__ */ React.createElement("p", null, labels.clean), /* @__PURE__ */ React.createElement("details", null, /* @__PURE__ */ React.createElement("summary", null, labels.branches), /* @__PURE__ */ React.createElement("ul", null, (_state$snapshot4 = state.snapshot) === null || _state$snapshot4 === void 0 ? void 0 : _state$snapshot4.branches.map((branch) => /* @__PURE__ */ React.createElement("li", { key: branch }, branch)))), /* @__PURE__ */ React.createElement("h3", null, labels.history), /* @__PURE__ */ React.createElement("ol", { className: "twgit-history" }, (_state$snapshot5 = state.snapshot) === null || _state$snapshot5 === void 0 ? void 0 : _state$snapshot5.commits.map((commit) => /* @__PURE__ */ React.createElement("li", { key: commit.oid }, /* @__PURE__ */ React.createElement("button", {
3958
+ })(), !state.changes.length && /* @__PURE__ */ React.createElement("p", null, labels.clean), /* @__PURE__ */ React.createElement("h3", null, labels.history), /* @__PURE__ */ React.createElement("ol", { className: "twgit-history" }, (_state$snapshot2 = state.snapshot) === null || _state$snapshot2 === void 0 ? void 0 : _state$snapshot2.commits.map((commit) => /* @__PURE__ */ React.createElement("li", { key: commit.oid }, /* @__PURE__ */ React.createElement("button", {
3844
3959
  disabled: state.busy,
3845
3960
  "aria-pressed": state.commit === commit.oid,
3846
3961
  title: commit.oid,
3847
3962
  onClick: () => void selectCommit(commit.oid)
3848
- }, commit.message.split("\n")[0]), /* @__PURE__ */ React.createElement("small", null, /* @__PURE__ */ React.createElement("code", null, commit.oid.slice(0, 8)), " · ", commit.author, " · ", (/* @__PURE__ */ new Date(commit.timestamp * 1e3)).toLocaleString(locale)))))), /* @__PURE__ */ React.createElement("main", { className: "twgit-diff" }, selection && projectList ? /* @__PURE__ */ React.createElement(ProjectElementView, {
3963
+ }, commit.message.split("\n")[0]), /* @__PURE__ */ React.createElement("small", null, commit.author, " · ", (/* @__PURE__ */ new Date(commit.timestamp * 1e3)).toLocaleString(locale)))))), /* @__PURE__ */ React.createElement("main", { className: "twgit-diff" }, selection && projectList ? /* @__PURE__ */ React.createElement(ProjectElementView, {
3849
3964
  changes: projectList,
3850
3965
  selection,
3851
3966
  locale,
3967
+ locateBlock: state.commit ? void 0 : locator,
3852
3968
  renderBlock: host.renderReviewBlock,
3853
3969
  onSelectFile: openFile
3854
3970
  }) : state.selected ? /* @__PURE__ */ React.createElement(FileDiffView, {
@@ -3856,9 +3972,34 @@ function GitPanel({ document, host, controller }) {
3856
3972
  diff: state.diff,
3857
3973
  labels,
3858
3974
  locale,
3975
+ locateBlock: fileLocator(state.selected),
3859
3976
  renderBlock: host.renderReviewBlock,
3860
3977
  loadAsset
3861
- }) : /* @__PURE__ */ React.createElement("p", null, labels.select))), /* @__PURE__ */ React.createElement("footer", null, labels.permissions)));
3978
+ }) : /* @__PURE__ */ React.createElement("p", null, labels.select))));
3979
+ if (slot) return createPortal(/* @__PURE__ */ React.createElement("div", {
3980
+ className: "twgit-panel twgit-panel-tab",
3981
+ role: "region",
3982
+ "aria-label": labels.title
3983
+ }, content), slot.element);
3984
+ return /* @__PURE__ */ React.createElement("div", { className: "twgit-backdrop" }, /* @__PURE__ */ React.createElement("div", {
3985
+ className: "twgit-panel",
3986
+ ref: dialog,
3987
+ tabIndex: -1,
3988
+ role: "dialog",
3989
+ "aria-modal": "true",
3990
+ "aria-label": labels.title,
3991
+ onKeyDown: focusTrap
3992
+ }, content));
3993
+ }
3994
+ /** The GitHub relation in plain words, with the number of records where it helps. */
3995
+ function upstreamText({ upstream }, labels) {
3996
+ switch (upstream.state) {
3997
+ case "same": return labels.upstreamSame;
3998
+ case "ahead": return `${labels.upstreamAhead} · ${upstream.ahead} ${labels.records}`;
3999
+ case "behind": return `${labels.upstreamBehind} · ${upstream.behind} ${labels.records}`;
4000
+ case "diverged": return `${labels.upstreamDiverged} · +${upstream.ahead} / −${upstream.behind}`;
4001
+ default: return labels.upstreamUnknown;
4002
+ }
3862
4003
  }
3863
4004
  function mountGitPanel({ vm, document, host }) {
3864
4005
  var _document$defaultView2;
@@ -3871,6 +4012,7 @@ function mountGitPanel({ vm, document, host }) {
3871
4012
  };
3872
4013
  controllers.set(vm, controller);
3873
4014
  render(/* @__PURE__ */ React.createElement(GitPanel, {
4015
+ vm,
3874
4016
  document,
3875
4017
  host,
3876
4018
  controller
@@ -3897,4 +4039,4 @@ function renderGitMenu(createElement, { vm, locale = "en", MenuItem, MenuSection
3897
4039
  }
3898
4040
  }, labelsFor(locale).menu));
3899
4041
  }
3900
- export { GitPanelError, createReadOnlyFs, createScratchBlocksDiffRenderer, mountGitPanel, openRepository, renderGitMenu };
4042
+ export { GitPanelError, createEditorBlockLocator, createReadOnlyFs, createScratchBlocksDiffLocator, createScratchBlocksDiffRenderer, mountGitPanel, openRepository, renderGitMenu };