paperclip-github-plugin 0.4.9 → 0.5.1

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/ui/index.js CHANGED
@@ -22411,6 +22411,7 @@ function normalizeCompanyAssigneeOptionsResponse(response) {
22411
22411
  return null;
22412
22412
  }
22413
22413
  const record = entry;
22414
+ const kind = record.kind === "user" ? "user" : "agent";
22414
22415
  const id = typeof record.id === "string" ? record.id.trim() : "";
22415
22416
  const name2 = typeof record.name === "string" ? record.name.trim() : "";
22416
22417
  const status = typeof record.status === "string" ? record.status.trim() : "";
@@ -22419,12 +22420,18 @@ function normalizeCompanyAssigneeOptionsResponse(response) {
22419
22420
  return null;
22420
22421
  }
22421
22422
  return {
22423
+ kind,
22422
22424
  id,
22423
22425
  name: name2,
22424
22426
  ...title ? { title } : {},
22425
22427
  ...status ? { status } : {}
22426
22428
  };
22427
- }).filter((entry) => entry !== null).sort((left, right) => left.name.localeCompare(right.name));
22429
+ }).filter((entry) => entry !== null).sort((left, right) => {
22430
+ if (left.kind !== right.kind) {
22431
+ return left.kind === "user" ? -1 : 1;
22432
+ }
22433
+ return left.name.localeCompare(right.name);
22434
+ });
22428
22435
  }
22429
22436
 
22430
22437
  // src/ui/http.ts
@@ -22810,6 +22817,14 @@ var HOST_DESTRUCTIVE_BUTTON_CLASSNAME = [
22810
22817
  "bg-destructive text-white hover:bg-destructive/90",
22811
22818
  "focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40"
22812
22819
  ].join(" ");
22820
+ var HOST_SUCCESS_BUTTON_CLASSNAME = [
22821
+ HOST_BUTTON_BASE_CLASSNAME,
22822
+ "ghsync__button-tone ghsync__button-tone--success"
22823
+ ].join(" ");
22824
+ var HOST_WARNING_BUTTON_CLASSNAME = [
22825
+ HOST_BUTTON_BASE_CLASSNAME,
22826
+ "ghsync__button-tone ghsync__button-tone--warning"
22827
+ ].join(" ");
22813
22828
  var HOST_TOOLBAR_BUTTON_SIZE_CLASSNAME = "px-3 has-[>svg]:px-2.5";
22814
22829
  var HOST_ACTION_BUTTON_SIZE_CLASSNAME = "h-9 px-4 py-2 has-[>svg]:px-3";
22815
22830
  var HOST_INLINE_BUTTON_SIZE_CLASSNAME = "h-8 px-3 has-[>svg]:px-2.5";
@@ -22838,7 +22853,7 @@ var PREVIEW_MARKDOWN_REHYPE_PLUGINS = [rehypeRaw, PREVIEW_MARKDOWN_SANITIZE_REHY
22838
22853
  function getPluginActionClassName(options) {
22839
22854
  const variant = options?.variant ?? "secondary";
22840
22855
  const size = options?.size ?? "default";
22841
- const variantClassName = variant === "primary" ? HOST_DEFAULT_BUTTON_CLASSNAME : variant === "danger" ? HOST_DESTRUCTIVE_BUTTON_CLASSNAME : HOST_OUTLINE_BUTTON_CLASSNAME;
22856
+ const variantClassName = variant === "primary" ? HOST_DEFAULT_BUTTON_CLASSNAME : variant === "success" ? HOST_SUCCESS_BUTTON_CLASSNAME : variant === "warning" ? HOST_WARNING_BUTTON_CLASSNAME : variant === "danger" ? HOST_DESTRUCTIVE_BUTTON_CLASSNAME : HOST_OUTLINE_BUTTON_CLASSNAME;
22842
22857
  const sizeClassName = size === "sm" ? HOST_INLINE_BUTTON_SIZE_CLASSNAME : HOST_ACTION_BUTTON_SIZE_CLASSNAME;
22843
22858
  return ["ghsync__button", variantClassName, sizeClassName, options?.extraClassName].filter(Boolean).join(" ");
22844
22859
  }
@@ -22875,6 +22890,7 @@ function GitHubButtonLabel(props) {
22875
22890
  ] });
22876
22891
  }
22877
22892
  var PROJECT_PULL_REQUESTS_PAGE_ROUTE_PATH = "github-pull-requests";
22893
+ var QUICK_REQUEST_CHANGES_REVIEW_SUMMARY = "Requested changes.";
22878
22894
  var LIGHT_PALETTE = {
22879
22895
  text: "#18181b",
22880
22896
  title: "#09090b",
@@ -23349,6 +23365,32 @@ var SHARED_LOADING_STYLES = `
23349
23365
  min-width: 0;
23350
23366
  }
23351
23367
 
23368
+ .ghsync__button-tone {
23369
+ border: 1px solid transparent;
23370
+ }
23371
+
23372
+ .ghsync__button-tone--success {
23373
+ border-color: var(--ghsync-success-border);
23374
+ background: var(--ghsync-success-bg);
23375
+ color: var(--ghsync-success-text);
23376
+ }
23377
+
23378
+ .ghsync__button-tone--success:hover {
23379
+ background: color-mix(in srgb, var(--ghsync-success-bg) 72%, var(--ghsync-success-border));
23380
+ color: var(--ghsync-success-text);
23381
+ }
23382
+
23383
+ .ghsync__button-tone--warning {
23384
+ border-color: var(--ghsync-warning-border);
23385
+ background: var(--ghsync-warning-bg);
23386
+ color: var(--ghsync-warning-text);
23387
+ }
23388
+
23389
+ .ghsync__button-tone--warning:hover {
23390
+ background: color-mix(in srgb, var(--ghsync-warning-bg) 72%, var(--ghsync-warning-border));
23391
+ color: var(--ghsync-warning-text);
23392
+ }
23393
+
23352
23394
  .ghsync__loading-inline {
23353
23395
  display: inline-flex;
23354
23396
  align-items: center;
@@ -23927,9 +23969,15 @@ var PAGE_STYLES = `
23927
23969
 
23928
23970
  .ghsync__picker {
23929
23971
  position: relative;
23972
+ min-width: 0;
23973
+ }
23974
+
23975
+ .ghsync__picker--full {
23976
+ width: 100%;
23930
23977
  }
23931
23978
 
23932
23979
  .ghsync__picker-trigger {
23980
+ box-sizing: border-box;
23933
23981
  width: fit-content;
23934
23982
  max-width: 100%;
23935
23983
  min-height: 0;
@@ -23962,6 +24010,10 @@ var PAGE_STYLES = `
23962
24010
  background: var(--ghsync-surfaceRaised);
23963
24011
  }
23964
24012
 
24013
+ .ghsync__picker--full .ghsync__picker-trigger {
24014
+ width: 100%;
24015
+ }
24016
+
23965
24017
  .ghsync__picker-trigger--assignee {
23966
24018
  min-width: 10rem;
23967
24019
  font-size: 14px;
@@ -24012,6 +24064,7 @@ var PAGE_STYLES = `
24012
24064
  }
24013
24065
 
24014
24066
  .ghsync__picker-panel {
24067
+ box-sizing: border-box;
24015
24068
  position: absolute;
24016
24069
  top: calc(100% + 8px);
24017
24070
  left: 0;
@@ -24024,7 +24077,8 @@ var PAGE_STYLES = `
24024
24077
  }
24025
24078
 
24026
24079
  .ghsync__picker-panel--assignee {
24027
- width: min(20rem, calc(100vw - 2rem));
24080
+ width: min(20rem, 100%);
24081
+ max-width: calc(100vw - 2rem);
24028
24082
  }
24029
24083
 
24030
24084
  .ghsync__picker-panel--status {
@@ -24976,6 +25030,7 @@ var PROJECT_PULL_REQUESTS_PAGE_STYLES = `
24976
25030
  font-weight: 700;
24977
25031
  letter-spacing: 0.02em;
24978
25032
  flex: 0 0 auto;
25033
+ overflow: hidden;
24979
25034
  }
24980
25035
 
24981
25036
  .ghsync-prs-avatar img,
@@ -25043,6 +25098,36 @@ var PROJECT_PULL_REQUESTS_PAGE_STYLES = `
25043
25098
  opacity: 0.42;
25044
25099
  }
25045
25100
 
25101
+ .ghsync-prs-table__icon-button--success {
25102
+ color: var(--ghsync-success-text);
25103
+ }
25104
+
25105
+ .ghsync-prs-table__icon-button--warning {
25106
+ color: var(--ghsync-warning-text);
25107
+ }
25108
+
25109
+ .ghsync-prs-table__icon-button--danger {
25110
+ color: var(--ghsync-danger-text);
25111
+ }
25112
+
25113
+ .ghsync-prs-table__icon-button--success:hover {
25114
+ color: var(--ghsync-success-text);
25115
+ border-color: var(--ghsync-success-border);
25116
+ background: color-mix(in srgb, var(--ghsync-success-bg) 68%, var(--ghsync-surfaceRaised));
25117
+ }
25118
+
25119
+ .ghsync-prs-table__icon-button--warning:hover {
25120
+ color: var(--ghsync-warning-text);
25121
+ border-color: var(--ghsync-warning-border);
25122
+ background: color-mix(in srgb, var(--ghsync-warning-bg) 68%, var(--ghsync-surfaceRaised));
25123
+ }
25124
+
25125
+ .ghsync-prs-table__icon-button--danger:hover {
25126
+ color: var(--ghsync-danger-text);
25127
+ border-color: var(--ghsync-danger-border);
25128
+ background: color-mix(in srgb, var(--ghsync-danger-bg) 68%, var(--ghsync-surfaceRaised));
25129
+ }
25130
+
25046
25131
  .ghsync-prs-table__status--passed {
25047
25132
  color: var(--ghsync-success-text);
25048
25133
  }
@@ -26630,14 +26715,45 @@ function normalizeAgentIds(value) {
26630
26715
  rawEntries.map((entry) => typeof entry === "string" && entry.trim() ? entry.trim() : null).filter((entry) => Boolean(entry))
26631
26716
  )].sort((left, right) => left.localeCompare(right));
26632
26717
  }
26718
+ function normalizeAdvancedSettingsAssigneeOverride(record, keys2) {
26719
+ const rawUserId = record[keys2.userId];
26720
+ const userId = typeof rawUserId === "string" && rawUserId.trim() ? rawUserId.trim() : void 0;
26721
+ if (userId) {
26722
+ return {
26723
+ [keys2.userId]: userId
26724
+ };
26725
+ }
26726
+ const rawAgentId = record[keys2.agentId];
26727
+ const agentId = typeof rawAgentId === "string" && rawAgentId.trim() ? rawAgentId.trim() : void 0;
26728
+ if (agentId) {
26729
+ return {
26730
+ [keys2.agentId]: agentId
26731
+ };
26732
+ }
26733
+ return {};
26734
+ }
26633
26735
  function normalizeAdvancedSettings(value) {
26634
26736
  if (!value || typeof value !== "object") {
26635
26737
  return DEFAULT_ADVANCED_SETTINGS;
26636
26738
  }
26637
26739
  const record = value;
26638
- const defaultAssigneeAgentId = typeof record.defaultAssigneeAgentId === "string" && record.defaultAssigneeAgentId.trim() ? record.defaultAssigneeAgentId.trim() : void 0;
26639
26740
  return {
26640
- ...defaultAssigneeAgentId ? { defaultAssigneeAgentId } : {},
26741
+ ...normalizeAdvancedSettingsAssigneeOverride(record, {
26742
+ agentId: "defaultAssigneeAgentId",
26743
+ userId: "defaultAssigneeUserId"
26744
+ }),
26745
+ ...normalizeAdvancedSettingsAssigneeOverride(record, {
26746
+ agentId: "executorAssigneeAgentId",
26747
+ userId: "executorAssigneeUserId"
26748
+ }),
26749
+ ...normalizeAdvancedSettingsAssigneeOverride(record, {
26750
+ agentId: "reviewerAssigneeAgentId",
26751
+ userId: "reviewerAssigneeUserId"
26752
+ }),
26753
+ ...normalizeAdvancedSettingsAssigneeOverride(record, {
26754
+ agentId: "approverAssigneeAgentId",
26755
+ userId: "approverAssigneeUserId"
26756
+ }),
26641
26757
  defaultStatus: normalizePaperclipIssueStatus(record.defaultStatus),
26642
26758
  ignoredIssueAuthorUsernames: "ignoredIssueAuthorUsernames" in record ? normalizeIgnoredIssueAuthorUsernames(record.ignoredIssueAuthorUsernames) : DEFAULT_ADVANCED_SETTINGS.ignoredIssueAuthorUsernames,
26643
26759
  ...normalizeAgentIds(record.githubTokenPropagationAgentIds).length > 0 ? { githubTokenPropagationAgentIds: normalizeAgentIds(record.githubTokenPropagationAgentIds) } : {}
@@ -26656,6 +26772,13 @@ function getComparableAdvancedSettings(value) {
26656
26772
  const settings = normalizeAdvancedSettings(value);
26657
26773
  return {
26658
26774
  ...settings.defaultAssigneeAgentId ? { defaultAssigneeAgentId: settings.defaultAssigneeAgentId } : {},
26775
+ ...settings.defaultAssigneeUserId ? { defaultAssigneeUserId: settings.defaultAssigneeUserId } : {},
26776
+ ...settings.executorAssigneeAgentId ? { executorAssigneeAgentId: settings.executorAssigneeAgentId } : {},
26777
+ ...settings.executorAssigneeUserId ? { executorAssigneeUserId: settings.executorAssigneeUserId } : {},
26778
+ ...settings.reviewerAssigneeAgentId ? { reviewerAssigneeAgentId: settings.reviewerAssigneeAgentId } : {},
26779
+ ...settings.reviewerAssigneeUserId ? { reviewerAssigneeUserId: settings.reviewerAssigneeUserId } : {},
26780
+ ...settings.approverAssigneeAgentId ? { approverAssigneeAgentId: settings.approverAssigneeAgentId } : {},
26781
+ ...settings.approverAssigneeUserId ? { approverAssigneeUserId: settings.approverAssigneeUserId } : {},
26659
26782
  defaultStatus: settings.defaultStatus,
26660
26783
  ignoredIssueAuthorUsernames: [...settings.ignoredIssueAuthorUsernames].sort((left, right) => left.localeCompare(right)),
26661
26784
  ...settings.githubTokenPropagationAgentIds?.length ? { githubTokenPropagationAgentIds: [...settings.githubTokenPropagationAgentIds].sort((left, right) => left.localeCompare(right)) } : {}
@@ -26664,43 +26787,127 @@ function getComparableAdvancedSettings(value) {
26664
26787
  function formatAssigneeOptionLabel(option) {
26665
26788
  return option.title?.trim() ? `${option.name} (${option.title.trim()})` : option.name;
26666
26789
  }
26667
- function getAvailableAssigneeOptions(options, selectedAgentId) {
26790
+ function getAssigneeOptionKey(option) {
26791
+ return `${option.kind}:${option.id}`;
26792
+ }
26793
+ function getAssigneeOptionValue(option) {
26794
+ return getAssigneeOptionKey(option);
26795
+ }
26796
+ function parseAssigneeOptionValue(value) {
26797
+ const trimmedValue = typeof value === "string" ? value.trim() : "";
26798
+ if (!trimmedValue) {
26799
+ return null;
26800
+ }
26801
+ if (trimmedValue.startsWith("agent:")) {
26802
+ const id = trimmedValue.slice("agent:".length).trim();
26803
+ return id ? { kind: "agent", id } : null;
26804
+ }
26805
+ if (trimmedValue.startsWith("user:")) {
26806
+ const id = trimmedValue.slice("user:".length).trim();
26807
+ return id ? { kind: "user", id } : null;
26808
+ }
26809
+ return { kind: "agent", id: trimmedValue };
26810
+ }
26811
+ function getAdvancedSettingsAssigneePrincipal(advancedSettings, role) {
26812
+ switch (role) {
26813
+ case "default":
26814
+ return advancedSettings.defaultAssigneeUserId ? { kind: "user", id: advancedSettings.defaultAssigneeUserId } : advancedSettings.defaultAssigneeAgentId ? { kind: "agent", id: advancedSettings.defaultAssigneeAgentId } : null;
26815
+ case "executor":
26816
+ return advancedSettings.executorAssigneeUserId ? { kind: "user", id: advancedSettings.executorAssigneeUserId } : advancedSettings.executorAssigneeAgentId ? { kind: "agent", id: advancedSettings.executorAssigneeAgentId } : null;
26817
+ case "reviewer":
26818
+ return advancedSettings.reviewerAssigneeUserId ? { kind: "user", id: advancedSettings.reviewerAssigneeUserId } : advancedSettings.reviewerAssigneeAgentId ? { kind: "agent", id: advancedSettings.reviewerAssigneeAgentId } : null;
26819
+ case "approver":
26820
+ return advancedSettings.approverAssigneeUserId ? { kind: "user", id: advancedSettings.approverAssigneeUserId } : advancedSettings.approverAssigneeAgentId ? { kind: "agent", id: advancedSettings.approverAssigneeAgentId } : null;
26821
+ }
26822
+ }
26823
+ function setAdvancedSettingsAssigneePrincipal(advancedSettings, role, principal) {
26824
+ switch (role) {
26825
+ case "default":
26826
+ return {
26827
+ ...advancedSettings,
26828
+ defaultAssigneeAgentId: principal?.kind === "agent" ? principal.id : void 0,
26829
+ defaultAssigneeUserId: principal?.kind === "user" ? principal.id : void 0
26830
+ };
26831
+ case "executor":
26832
+ return {
26833
+ ...advancedSettings,
26834
+ executorAssigneeAgentId: principal?.kind === "agent" ? principal.id : void 0,
26835
+ executorAssigneeUserId: principal?.kind === "user" ? principal.id : void 0
26836
+ };
26837
+ case "reviewer":
26838
+ return {
26839
+ ...advancedSettings,
26840
+ reviewerAssigneeAgentId: principal?.kind === "agent" ? principal.id : void 0,
26841
+ reviewerAssigneeUserId: principal?.kind === "user" ? principal.id : void 0
26842
+ };
26843
+ case "approver":
26844
+ return {
26845
+ ...advancedSettings,
26846
+ approverAssigneeAgentId: principal?.kind === "agent" ? principal.id : void 0,
26847
+ approverAssigneeUserId: principal?.kind === "user" ? principal.id : void 0
26848
+ };
26849
+ }
26850
+ }
26851
+ function getSelectedAssigneeOptionValue(advancedSettings, role) {
26852
+ const principal = getAdvancedSettingsAssigneePrincipal(advancedSettings, role);
26853
+ return principal ? getAssigneeOptionValue(principal) : "";
26854
+ }
26855
+ function compareAssigneeOptions(left, right) {
26856
+ if (left.kind !== right.kind) {
26857
+ return left.kind === "user" ? -1 : 1;
26858
+ }
26859
+ return left.name.localeCompare(right.name);
26860
+ }
26861
+ function getAvailableAssigneeOptions(options, selectedAssignees) {
26668
26862
  const normalizedOptions = [...options ?? []];
26669
- if (selectedAgentId && !normalizedOptions.some((option) => option.id === selectedAgentId)) {
26670
- normalizedOptions.push({
26671
- id: selectedAgentId,
26672
- name: "Unavailable agent"
26673
- });
26863
+ const selectedPrincipals = Array.isArray(selectedAssignees) ? selectedAssignees.filter((selectedAssignee) => Boolean(selectedAssignee)) : selectedAssignees ? [selectedAssignees] : [];
26864
+ for (const selectedPrincipal of selectedPrincipals) {
26865
+ if (!normalizedOptions.some((option) => getAssigneeOptionKey(option) === getAssigneeOptionKey(selectedPrincipal))) {
26866
+ normalizedOptions.push({
26867
+ kind: selectedPrincipal.kind,
26868
+ id: selectedPrincipal.id,
26869
+ name: selectedPrincipal.kind === "user" ? "Unavailable user" : "Unavailable agent"
26870
+ });
26871
+ }
26674
26872
  }
26675
- return normalizedOptions;
26873
+ return normalizedOptions.sort(compareAssigneeOptions);
26676
26874
  }
26677
26875
  function getAvailablePropagationAgentOptions(options, selectedAgentIds) {
26678
- const normalizedOptions = [...options ?? []];
26876
+ const normalizedOptions = [...(options ?? []).filter((option) => option.kind === "agent")];
26679
26877
  const selectedIds = normalizeAgentIds(selectedAgentIds);
26680
26878
  for (const selectedAgentId of selectedIds) {
26681
26879
  if (!normalizedOptions.some((option) => option.id === selectedAgentId)) {
26682
26880
  normalizedOptions.push({
26881
+ kind: "agent",
26683
26882
  id: selectedAgentId,
26684
26883
  name: "Unavailable agent"
26685
26884
  });
26686
26885
  }
26687
26886
  }
26688
- return normalizedOptions.sort((left, right) => left.name.localeCompare(right.name));
26887
+ return normalizedOptions.sort(compareAssigneeOptions);
26689
26888
  }
26690
26889
  function formatAdvancedSettingsSummary(advancedSettings, availableAssignees, options) {
26691
- const assigneeLabel = advancedSettings.defaultAssigneeAgentId ? formatAssigneeOptionLabel(
26692
- availableAssignees.find((option) => option.id === advancedSettings.defaultAssigneeAgentId) ?? {
26693
- id: advancedSettings.defaultAssigneeAgentId,
26694
- name: "Unavailable agent"
26695
- }
26696
- ) : "Unassigned";
26890
+ const resolveAssigneeLabel = (principal, fallbackLabel = "None") => {
26891
+ return principal ? formatAssigneeOptionLabel(
26892
+ availableAssignees.find((option) => getAssigneeOptionKey(option) === getAssigneeOptionKey(principal)) ?? {
26893
+ kind: principal.kind,
26894
+ id: principal.id,
26895
+ name: principal.kind === "user" ? "Unavailable user" : "Unavailable agent"
26896
+ }
26897
+ ) : fallbackLabel;
26898
+ };
26899
+ const assigneeLabel = resolveAssigneeLabel(getAdvancedSettingsAssigneePrincipal(advancedSettings, "default"), "Unassigned");
26900
+ const executorLabel = resolveAssigneeLabel(getAdvancedSettingsAssigneePrincipal(advancedSettings, "executor"), "Automatic routing");
26901
+ const reviewerLabel = resolveAssigneeLabel(getAdvancedSettingsAssigneePrincipal(advancedSettings, "reviewer"), "Automatic routing");
26902
+ const approverLabel = resolveAssigneeLabel(getAdvancedSettingsAssigneePrincipal(advancedSettings, "approver"), "Automatic routing");
26697
26903
  const statusLabel = PAPERCLIP_STATUS_OPTIONS.find((option) => option.value === advancedSettings.defaultStatus)?.label ?? "Backlog";
26698
26904
  const ignoredAuthorsLabel = advancedSettings.ignoredIssueAuthorUsernames.length > 0 ? advancedSettings.ignoredIssueAuthorUsernames.join(", ") : "none";
26905
+ const handoffLabel = `Back to work: ${executorLabel} \xB7 Review: ${reviewerLabel} \xB7 Approval: ${approverLabel}`;
26699
26906
  if (options?.includePropagation) {
26700
26907
  const propagatedAgentsLabel = advancedSettings.githubTokenPropagationAgentIds?.length ? `${advancedSettings.githubTokenPropagationAgentIds.length} selected` : "none";
26701
- return `Assignee: ${assigneeLabel} \xB7 Status: ${statusLabel} \xB7 Ignore: ${ignoredAuthorsLabel} \xB7 Propagate: ${propagatedAgentsLabel}`;
26908
+ return `Import: ${assigneeLabel} \xB7 ${handoffLabel} \xB7 Status: ${statusLabel} \xB7 Ignore: ${ignoredAuthorsLabel} \xB7 Propagate: ${propagatedAgentsLabel}`;
26702
26909
  }
26703
- return `Assignee: ${assigneeLabel} \xB7 Status: ${statusLabel} \xB7 Ignore: ${ignoredAuthorsLabel}`;
26910
+ return `Import: ${assigneeLabel} \xB7 ${handoffLabel} \xB7 Status: ${statusLabel} \xB7 Ignore: ${ignoredAuthorsLabel}`;
26704
26911
  }
26705
26912
  function resolveSavedTokenUiState(params) {
26706
26913
  if (params.githubTokenConfigured) {
@@ -26771,6 +26978,29 @@ function AgentIcon() {
26771
26978
  )
26772
26979
  ] });
26773
26980
  }
26981
+ function UserIcon() {
26982
+ return /* @__PURE__ */ jsxs2("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
26983
+ /* @__PURE__ */ jsx2("circle", { cx: "8", cy: "5.1", r: "2.3", stroke: "currentColor", strokeWidth: "1.2" }),
26984
+ /* @__PURE__ */ jsx2(
26985
+ "path",
26986
+ {
26987
+ d: "M3.25 12.85C3.78 10.83 5.62 9.4 7.82 9.4H8.18C10.38 9.4 12.22 10.83 12.75 12.85",
26988
+ stroke: "currentColor",
26989
+ strokeWidth: "1.2",
26990
+ strokeLinecap: "round"
26991
+ }
26992
+ )
26993
+ ] });
26994
+ }
26995
+ function SettingsSelectIcon(props) {
26996
+ if (props.icon === "agent") {
26997
+ return /* @__PURE__ */ jsx2(AgentIcon, {});
26998
+ }
26999
+ if (props.icon === "user") {
27000
+ return /* @__PURE__ */ jsx2(UserIcon, {});
27001
+ }
27002
+ return null;
27003
+ }
26774
27004
  function SettingsAssigneePicker(props) {
26775
27005
  const { id, value, options, disabled, onChange } = props;
26776
27006
  const [open, setOpen] = useState2(false);
@@ -26813,7 +27043,7 @@ function SettingsAssigneePicker(props) {
26813
27043
  setOpen(false);
26814
27044
  }
26815
27045
  }, [disabled, open]);
26816
- return /* @__PURE__ */ jsxs2("div", { className: "ghsync__picker", ref: rootRef, children: [
27046
+ return /* @__PURE__ */ jsxs2("div", { className: "ghsync__picker ghsync__picker--full", ref: rootRef, children: [
26817
27047
  /* @__PURE__ */ jsxs2(
26818
27048
  "button",
26819
27049
  {
@@ -26831,7 +27061,7 @@ function SettingsAssigneePicker(props) {
26831
27061
  },
26832
27062
  children: [
26833
27063
  /* @__PURE__ */ jsxs2("span", { className: "ghsync__picker-trigger-main", children: [
26834
- selectedOption?.icon === "agent" ? /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-agent-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(AgentIcon, {}) }) : null,
27064
+ selectedOption?.icon ? /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-agent-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(SettingsSelectIcon, { icon: selectedOption.icon }) }) : null,
26835
27065
  /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-trigger-label", children: selectedOption?.label ?? "No assignee" })
26836
27066
  ] }),
26837
27067
  /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-trigger-icon", children: /* @__PURE__ */ jsx2(PickerChevronIcon, {}) })
@@ -26873,7 +27103,7 @@ function SettingsAssigneePicker(props) {
26873
27103
  },
26874
27104
  children: [
26875
27105
  /* @__PURE__ */ jsxs2("span", { className: "ghsync__picker-trigger-main", children: [
26876
- option.icon === "agent" ? /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-agent-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(AgentIcon, {}) }) : null,
27106
+ option.icon ? /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-agent-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(SettingsSelectIcon, { icon: option.icon }) }) : null,
26877
27107
  /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-option-label", children: option.label })
26878
27108
  ] }),
26879
27109
  /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-option-check", "aria-hidden": "true", children: selected ? /* @__PURE__ */ jsx2(PickerCheckIcon, {}) : null })
@@ -26929,7 +27159,7 @@ function SettingsAgentMultiPicker(props) {
26929
27159
  setOpen(false);
26930
27160
  }
26931
27161
  }, [disabled, open]);
26932
- return /* @__PURE__ */ jsxs2("div", { className: "ghsync__picker", ref: rootRef, children: [
27162
+ return /* @__PURE__ */ jsxs2("div", { className: "ghsync__picker ghsync__picker--full", ref: rootRef, children: [
26933
27163
  /* @__PURE__ */ jsxs2(
26934
27164
  "button",
26935
27165
  {
@@ -26989,7 +27219,7 @@ function SettingsAgentMultiPicker(props) {
26989
27219
  },
26990
27220
  children: [
26991
27221
  /* @__PURE__ */ jsxs2("span", { className: "ghsync__picker-trigger-main", children: [
26992
- option.icon === "agent" ? /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-agent-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(AgentIcon, {}) }) : null,
27222
+ option.icon ? /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-agent-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(SettingsSelectIcon, { icon: option.icon }) }) : null,
26993
27223
  /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-option-label", children: option.label })
26994
27224
  ] }),
26995
27225
  /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-option-check", "aria-hidden": "true", children: selected ? /* @__PURE__ */ jsx2(PickerCheckIcon, {}) : null })
@@ -27395,6 +27625,16 @@ function getPreviewPullRequestCheckToneClass(status) {
27395
27625
  return "ghsync-prs-table__status--pending";
27396
27626
  }
27397
27627
  }
27628
+ function getPreviewPullRequestInlineActionToneClass(tone) {
27629
+ switch (tone) {
27630
+ case "success":
27631
+ return "ghsync-prs-table__icon-button--success";
27632
+ case "warning":
27633
+ return "ghsync-prs-table__icon-button--warning";
27634
+ default:
27635
+ return "ghsync-prs-table__icon-button--danger";
27636
+ }
27637
+ }
27398
27638
  function getPreviewPullRequestUpToDateMeta(status) {
27399
27639
  switch (status) {
27400
27640
  case "up_to_date":
@@ -28092,6 +28332,20 @@ function getCliAuthIdentityLabel(identity) {
28092
28332
  }
28093
28333
  return null;
28094
28334
  }
28335
+ function getCliAuthIdentityUserId(identity) {
28336
+ const candidates = [
28337
+ identity.user?.id,
28338
+ identity.user?.userId,
28339
+ identity.id,
28340
+ identity.userId
28341
+ ];
28342
+ for (const candidate of candidates) {
28343
+ if (typeof candidate === "string" && candidate.trim()) {
28344
+ return candidate.trim();
28345
+ }
28346
+ }
28347
+ return null;
28348
+ }
28095
28349
  function waitForDuration(durationMs) {
28096
28350
  return new Promise((resolve) => {
28097
28351
  globalThis.setTimeout(resolve, durationMs);
@@ -28146,7 +28400,10 @@ async function fetchBoardAccessIdentity(boardApiToken) {
28146
28400
  authorization: `Bearer ${boardApiToken.trim()}`
28147
28401
  }
28148
28402
  });
28149
- return getCliAuthIdentityLabel(identity);
28403
+ return {
28404
+ label: getCliAuthIdentityLabel(identity),
28405
+ userId: getCliAuthIdentityUserId(identity)
28406
+ };
28150
28407
  }
28151
28408
  function getSyncStatus(syncState, runningSync, syncUnlocked) {
28152
28409
  if (!syncUnlocked) {
@@ -28782,24 +29039,43 @@ function SyncDiagnosticsPanel(props) {
28782
29039
  function PreviewAvatar(props) {
28783
29040
  const backgroundColor = getPreviewAvatarColor(props.person.handle);
28784
29041
  const className = props.stacked ? "ghsync-prs-avatar-stack__item" : "ghsync-prs-avatar";
28785
- const avatarSizePx = props.size === "sm" ? 20 : 28;
28786
- const fontSizePx = props.size === "sm" ? 10 : 11;
28787
29042
  const labels = resolvePreviewPersonLabels(props.person);
28788
29043
  const initialsSource = props.person.name.trim() || props.person.handle.replace(/^@/, "").trim();
28789
29044
  const title = labels.secondary ? `${labels.primary} (${labels.secondary})` : labels.primary;
29045
+ const avatarStyle = {
29046
+ backgroundColor
29047
+ };
29048
+ const imageStyle = props.size === "sm" ? {
29049
+ width: "100%",
29050
+ height: "100%",
29051
+ borderRadius: "inherit",
29052
+ objectFit: "cover",
29053
+ display: "block"
29054
+ } : void 0;
29055
+ if (props.size === "sm") {
29056
+ Object.assign(avatarStyle, {
29057
+ width: 20,
29058
+ height: 20,
29059
+ fontSize: 10,
29060
+ display: "inline-flex",
29061
+ alignItems: "center",
29062
+ justifyContent: "center",
29063
+ borderRadius: 999,
29064
+ color: "white",
29065
+ fontWeight: 700,
29066
+ letterSpacing: "0.02em",
29067
+ flex: "0 0 auto",
29068
+ overflow: "hidden"
29069
+ });
29070
+ }
28790
29071
  return /* @__PURE__ */ jsx2(
28791
29072
  "span",
28792
29073
  {
28793
29074
  className,
28794
- style: {
28795
- backgroundColor,
28796
- width: avatarSizePx,
28797
- height: avatarSizePx,
28798
- fontSize: fontSizePx
28799
- },
29075
+ style: avatarStyle,
28800
29076
  title,
28801
29077
  "aria-hidden": "true",
28802
- children: props.person.avatarUrl ? /* @__PURE__ */ jsx2("img", { src: props.person.avatarUrl, alt: "", loading: "lazy" }) : getInitials(initialsSource)
29078
+ children: props.person.avatarUrl ? /* @__PURE__ */ jsx2("img", { src: props.person.avatarUrl, alt: "", loading: "lazy", style: imageStyle }) : getInitials(initialsSource)
28803
29079
  }
28804
29080
  );
28805
29081
  }
@@ -29152,6 +29428,7 @@ function GitHubSyncProjectPullRequestsPage() {
29152
29428
  const issueModalPending = issueModalPullRequest ? pendingActionKey === `create-issue:${issueModalPullRequest.id}` : false;
29153
29429
  const commentModalPending = commentModalPullRequest ? pendingActionKey === `comment-modal:${commentModalPullRequest.id}` : false;
29154
29430
  const reviewModalApprovePending = reviewModalPullRequest ? pendingActionKey === `review:${reviewModalPullRequest.id}:approve` : false;
29431
+ const reviewModalCommentPending = reviewModalPullRequest ? pendingActionKey === `review:${reviewModalPullRequest.id}:comment` : false;
29155
29432
  const reviewModalRequestChangesPending = reviewModalPullRequest ? pendingActionKey === `review:${reviewModalPullRequest.id}:request_changes` : false;
29156
29433
  const reviewModalPending = reviewModalPullRequest ? Boolean(pendingActionKey?.startsWith(`review:${reviewModalPullRequest.id}:`)) : false;
29157
29434
  const rerunCiModalPending = rerunCiModalPullRequest ? pendingActionKey === `rerun-ci:${rerunCiModalPullRequest.id}` : false;
@@ -29314,6 +29591,10 @@ function GitHubSyncProjectPullRequestsPage() {
29314
29591
  setCommentModalPullRequestId(pullRequestId);
29315
29592
  setCommentModalDraft("");
29316
29593
  }
29594
+ function openReviewModal(pullRequestId) {
29595
+ setReviewModalPullRequestId(pullRequestId);
29596
+ setReviewModalDraft("");
29597
+ }
29317
29598
  function openClosePullRequestModal(pullRequest) {
29318
29599
  setCloseModalPullRequest(pullRequest);
29319
29600
  }
@@ -29600,23 +29881,42 @@ function GitHubSyncProjectPullRequestsPage() {
29600
29881
  setPendingActionKey((current) => current === actionKey ? null : current);
29601
29882
  }
29602
29883
  }
29603
- async function handleReviewPullRequest(review) {
29604
- if (!reviewModalPullRequest || !hostContext.companyId || !projectId) {
29884
+ function getSubmittedReviewToast(review, pullRequestNumber) {
29885
+ switch (review) {
29886
+ case "approved":
29887
+ return {
29888
+ title: `Approved #${pullRequestNumber}`,
29889
+ body: "GitHub review submitted."
29890
+ };
29891
+ case "changes_requested":
29892
+ return {
29893
+ title: `Requested changes on #${pullRequestNumber}`,
29894
+ body: "GitHub change request submitted."
29895
+ };
29896
+ default:
29897
+ return {
29898
+ title: `Commented on #${pullRequestNumber}`,
29899
+ body: "GitHub review comment submitted."
29900
+ };
29901
+ }
29902
+ }
29903
+ async function submitPullRequestReview(pullRequest, review, body, options) {
29904
+ if (!hostContext.companyId || !projectId) {
29605
29905
  return;
29606
29906
  }
29607
- const actionKey = `review:${reviewModalPullRequest.id}:${review}`;
29907
+ const actionKey = `review:${pullRequest.id}:${review}`;
29608
29908
  setPendingActionKey(actionKey);
29609
29909
  try {
29610
29910
  const result = await reviewPullRequest({
29611
29911
  companyId: hostContext.companyId,
29612
29912
  projectId,
29613
29913
  repositoryUrl: pageData.repositoryUrl,
29614
- pullRequestNumber: reviewModalPullRequest.number,
29914
+ pullRequestNumber: pullRequest.number,
29615
29915
  review,
29616
- body: reviewModalDraft.trim()
29916
+ body
29617
29917
  });
29618
29918
  const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
29619
- patchPullRequestRow(reviewModalPullRequest.id, (current) => {
29919
+ patchPullRequestRow(pullRequest.id, (current) => {
29620
29920
  const nextReviewApprovals = current.reviewApprovals + (result.review === "approved" ? 1 : 0);
29621
29921
  const nextReviewChangesRequested = current.reviewChangesRequested + (result.review === "changes_requested" ? 1 : 0);
29622
29922
  const nextRecord = {
@@ -29631,13 +29931,16 @@ function GitHubSyncProjectPullRequestsPage() {
29631
29931
  mergeable: resolvePreviewPullRequestMergeable(nextRecord)
29632
29932
  };
29633
29933
  });
29634
- closeReviewModal();
29635
- refreshSelectedPullRequestDetails(reviewModalPullRequest.id);
29934
+ if (options?.closeModal) {
29935
+ closeReviewModal();
29936
+ }
29937
+ refreshSelectedPullRequestDetails(pullRequest.id);
29636
29938
  refreshPullRequestMetricsPanel();
29637
29939
  notifyGitHubSyncPullRequestsChanged();
29940
+ const submittedReviewToast = getSubmittedReviewToast(result.review, pullRequest.number);
29638
29941
  toast({
29639
- title: result.review === "approved" ? `Approved #${reviewModalPullRequest.number}` : `Requested changes on #${reviewModalPullRequest.number}`,
29640
- body: result.review === "approved" ? "GitHub review submitted." : "GitHub change request submitted.",
29942
+ title: submittedReviewToast.title,
29943
+ body: submittedReviewToast.body,
29641
29944
  tone: "success"
29642
29945
  });
29643
29946
  } catch (error) {
@@ -29650,6 +29953,19 @@ function GitHubSyncProjectPullRequestsPage() {
29650
29953
  setPendingActionKey((current) => current === actionKey ? null : current);
29651
29954
  }
29652
29955
  }
29956
+ async function handleReviewPullRequest(review) {
29957
+ if (!reviewModalPullRequest) {
29958
+ return;
29959
+ }
29960
+ await submitPullRequestReview(reviewModalPullRequest, review, reviewModalDraft.trim(), { closeModal: true });
29961
+ }
29962
+ async function handleQuickPullRequestReview(pullRequest, review) {
29963
+ await submitPullRequestReview(
29964
+ pullRequest,
29965
+ review,
29966
+ review === "request_changes" ? QUICK_REQUEST_CHANGES_REVIEW_SUMMARY : ""
29967
+ );
29968
+ }
29653
29969
  async function handleRerunCi() {
29654
29970
  if (!rerunCiModalPullRequest || !hostContext.companyId || !projectId) {
29655
29971
  return;
@@ -29707,7 +30023,7 @@ function GitHubSyncProjectPullRequestsPage() {
29707
30023
  /* @__PURE__ */ jsx2("th", { scope: "col", className: "ghsync-prs-table__cell--center", children: "Checks" }),
29708
30024
  /* @__PURE__ */ jsx2("th", { scope: "col", className: "ghsync-prs-table__cell--center", children: "Up to date" }),
29709
30025
  /* @__PURE__ */ jsx2("th", { scope: "col", className: "ghsync-prs-table__cell--center", children: "Target branch" }),
29710
- /* @__PURE__ */ jsx2("th", { scope: "col", className: "ghsync-prs-table__cell--center", children: "Reviews" }),
30026
+ /* @__PURE__ */ jsx2("th", { scope: "col", className: "ghsync-prs-table__cell--center", children: "Approvals" }),
29711
30027
  /* @__PURE__ */ jsx2("th", { scope: "col", className: "ghsync-prs-table__cell--center", children: "Review threads" }),
29712
30028
  /* @__PURE__ */ jsx2("th", { scope: "col", className: "ghsync-prs-table__cell--center", children: "Comments" }),
29713
30029
  /* @__PURE__ */ jsx2("th", { scope: "col", className: "ghsync-prs-table__cell--center", children: "Last updated" }),
@@ -29929,12 +30245,16 @@ function GitHubSyncProjectPullRequestsPage() {
29929
30245
  const mergeActionKey = `merge:${pullRequest.id}`;
29930
30246
  const commentModalActionKey = `comment-modal:${pullRequest.id}`;
29931
30247
  const reviewActionPrefix = `review:${pullRequest.id}:`;
30248
+ const reviewApproveActionKey = `review:${pullRequest.id}:approve`;
30249
+ const reviewRequestChangesActionKey = `review:${pullRequest.id}:request_changes`;
29932
30250
  const rerunCiActionKey = `rerun-ci:${pullRequest.id}`;
29933
30251
  const updateBranchActionKey = `update-branch:${pullRequest.id}`;
29934
30252
  const closeActionKey = `close:${pullRequest.id}`;
29935
30253
  const copilotPending = Boolean(pendingActionKey?.startsWith(copilotActionPrefix));
29936
30254
  const commentModalPending2 = pendingActionKey === commentModalActionKey;
29937
30255
  const reviewPending = Boolean(pendingActionKey?.startsWith(reviewActionPrefix));
30256
+ const reviewApprovePending = pendingActionKey === reviewApproveActionKey;
30257
+ const reviewRequestChangesPending = pendingActionKey === reviewRequestChangesActionKey;
29938
30258
  const rerunCiPending = pendingActionKey === rerunCiActionKey;
29939
30259
  const updateBranchPending = pendingActionKey === updateBranchActionKey;
29940
30260
  const mergePending = pendingActionKey === mergeActionKey;
@@ -30015,7 +30335,7 @@ function GitHubSyncProjectPullRequestsPage() {
30015
30335
  "button",
30016
30336
  {
30017
30337
  type: "button",
30018
- className: "ghsync-prs-table__icon-button",
30338
+ className: `ghsync-prs-table__icon-button ${getPreviewPullRequestInlineActionToneClass("warning")}`,
30019
30339
  title: `Re-run CI for #${pullRequest.number}`,
30020
30340
  onClick: () => setRerunCiPullRequestId(pullRequest.id),
30021
30341
  disabled: rerunCiPending,
@@ -30071,40 +30391,82 @@ function GitHubSyncProjectPullRequestsPage() {
30071
30391
  /* @__PURE__ */ jsx2("td", { className: "ghsync-prs-table__cell--center", children: /* @__PURE__ */ jsxs2("div", { className: "ghsync-prs-table__metric-group", children: [
30072
30392
  hasReviewSummary ? /* @__PURE__ */ jsxs2("a", { className: "ghsync-prs-table__metric-link", href: pullRequest.reviewsUrl, target: "_blank", rel: "noreferrer", children: [
30073
30393
  pullRequest.reviewApprovals > 0 ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
30074
- /* @__PURE__ */ jsx2(CheckPassedIcon, { className: "ghsync-prs-icon" }),
30394
+ /* @__PURE__ */ jsx2(CheckPassedIcon, { className: "ghsync-prs-icon ghsync-prs-table__status--passed" }),
30075
30395
  /* @__PURE__ */ jsx2("span", { children: pullRequest.reviewApprovals })
30076
30396
  ] }) : null,
30077
30397
  pullRequest.reviewChangesRequested > 0 ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
30078
- /* @__PURE__ */ jsx2(CheckFailedIcon, { className: "ghsync-prs-icon" }),
30398
+ /* @__PURE__ */ jsx2(CheckFailedIcon, { className: "ghsync-prs-icon ghsync-prs-table__status--failed" }),
30079
30399
  /* @__PURE__ */ jsx2("span", { children: pullRequest.reviewChangesRequested })
30080
30400
  ] }) : null
30081
30401
  ] }) : null,
30082
- pullRequest.reviewable && canReviewPullRequests ? /* @__PURE__ */ jsx2(
30083
- "button",
30084
- {
30085
- type: "button",
30086
- className: "ghsync-prs-table__icon-button",
30087
- title: `Review #${pullRequest.number}`,
30088
- onClick: () => {
30089
- setReviewModalPullRequestId(pullRequest.id);
30090
- setReviewModalDraft("");
30091
- },
30092
- disabled: reviewPending,
30093
- children: /* @__PURE__ */ jsx2(
30094
- LoadingIconButtonContent,
30095
- {
30096
- busy: reviewPending,
30097
- busyLabel: `Reviewing #${pullRequest.number}`,
30098
- icon: /* @__PURE__ */ jsx2(ReviewIcon, { className: "ghsync-prs-icon" })
30099
- }
30100
- )
30101
- }
30102
- ) : null
30402
+ pullRequest.reviewable && canReviewPullRequests ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
30403
+ /* @__PURE__ */ jsx2(
30404
+ "button",
30405
+ {
30406
+ type: "button",
30407
+ className: "ghsync-prs-table__icon-button",
30408
+ title: `Review #${pullRequest.number}`,
30409
+ "aria-label": `Review #${pullRequest.number}`,
30410
+ onClick: () => openReviewModal(pullRequest.id),
30411
+ disabled: reviewPending,
30412
+ children: /* @__PURE__ */ jsx2(
30413
+ LoadingIconButtonContent,
30414
+ {
30415
+ busy: false,
30416
+ busyLabel: `Reviewing #${pullRequest.number}`,
30417
+ icon: /* @__PURE__ */ jsx2(ReviewIcon, { className: "ghsync-prs-icon" })
30418
+ }
30419
+ )
30420
+ }
30421
+ ),
30422
+ /* @__PURE__ */ jsx2(
30423
+ "button",
30424
+ {
30425
+ type: "button",
30426
+ className: `ghsync-prs-table__icon-button ${getPreviewPullRequestInlineActionToneClass("success")}`,
30427
+ title: `Approve #${pullRequest.number}`,
30428
+ "aria-label": `Approve #${pullRequest.number}`,
30429
+ onClick: () => {
30430
+ void handleQuickPullRequestReview(pullRequest, "approve");
30431
+ },
30432
+ disabled: reviewPending,
30433
+ children: /* @__PURE__ */ jsx2(
30434
+ LoadingIconButtonContent,
30435
+ {
30436
+ busy: reviewApprovePending,
30437
+ busyLabel: `Approving #${pullRequest.number}`,
30438
+ icon: /* @__PURE__ */ jsx2(CheckPassedIcon, { className: "ghsync-prs-icon" })
30439
+ }
30440
+ )
30441
+ }
30442
+ ),
30443
+ /* @__PURE__ */ jsx2(
30444
+ "button",
30445
+ {
30446
+ type: "button",
30447
+ className: `ghsync-prs-table__icon-button ${getPreviewPullRequestInlineActionToneClass("danger")}`,
30448
+ title: `Request changes on #${pullRequest.number}`,
30449
+ "aria-label": `Request changes on #${pullRequest.number}`,
30450
+ onClick: () => {
30451
+ void handleQuickPullRequestReview(pullRequest, "request_changes");
30452
+ },
30453
+ disabled: reviewPending,
30454
+ children: /* @__PURE__ */ jsx2(
30455
+ LoadingIconButtonContent,
30456
+ {
30457
+ busy: reviewRequestChangesPending,
30458
+ busyLabel: `Requesting changes on #${pullRequest.number}`,
30459
+ icon: /* @__PURE__ */ jsx2(CheckFailedIcon, { className: "ghsync-prs-icon" })
30460
+ }
30461
+ )
30462
+ }
30463
+ )
30464
+ ] }) : null
30103
30465
  ] }) }),
30104
30466
  /* @__PURE__ */ jsx2("td", { className: "ghsync-prs-table__cell--center", children: pullRequest.unresolvedReviewThreads > 0 ? /* @__PURE__ */ jsxs2("a", { className: "ghsync-prs-table__metric-link", href: pullRequest.reviewThreadsUrl, target: "_blank", rel: "noreferrer", children: [
30105
30467
  /* @__PURE__ */ jsx2(CommentIcon, { className: "ghsync-prs-icon" }),
30106
30468
  /* @__PURE__ */ jsx2("span", { children: pullRequest.unresolvedReviewThreads })
30107
- ] }) : hasResolvedReviewThreads ? /* @__PURE__ */ jsx2("a", { className: "ghsync-prs-table__metric-link", href: pullRequest.reviewThreadsUrl, target: "_blank", rel: "noreferrer", children: /* @__PURE__ */ jsx2(CheckPassedIcon, { className: "ghsync-prs-icon" }) }) : null }),
30469
+ ] }) : hasResolvedReviewThreads ? /* @__PURE__ */ jsx2("a", { className: "ghsync-prs-table__metric-link", href: pullRequest.reviewThreadsUrl, target: "_blank", rel: "noreferrer", children: /* @__PURE__ */ jsx2(CheckPassedIcon, { className: "ghsync-prs-icon ghsync-prs-table__status--passed" }) }) : null }),
30108
30470
  /* @__PURE__ */ jsx2("td", { className: "ghsync-prs-table__cell--center", children: /* @__PURE__ */ jsxs2("div", { className: "ghsync-prs-table__metric-group", children: [
30109
30471
  /* @__PURE__ */ jsxs2("a", { className: "ghsync-prs-table__metric-link", href: pullRequest.commentsUrl, target: "_blank", rel: "noreferrer", children: [
30110
30472
  /* @__PURE__ */ jsx2(CommentIcon, { className: "ghsync-prs-icon" }),
@@ -30168,7 +30530,7 @@ function GitHubSyncProjectPullRequestsPage() {
30168
30530
  "button",
30169
30531
  {
30170
30532
  type: "button",
30171
- className: "ghsync-prs-table__icon-button",
30533
+ className: `ghsync-prs-table__icon-button ${getPreviewPullRequestInlineActionToneClass("success")}`,
30172
30534
  title: `Merge #${pullRequest.number}`,
30173
30535
  onClick: () => {
30174
30536
  void handleMergePullRequest(pullRequest);
@@ -30188,7 +30550,7 @@ function GitHubSyncProjectPullRequestsPage() {
30188
30550
  "button",
30189
30551
  {
30190
30552
  type: "button",
30191
- className: "ghsync-prs-table__icon-button",
30553
+ className: `ghsync-prs-table__icon-button ${getPreviewPullRequestInlineActionToneClass("danger")}`,
30192
30554
  title: `Close PR #${pullRequest.number}`,
30193
30555
  "aria-label": `Close PR #${pullRequest.number}`,
30194
30556
  onClick: () => {
@@ -30312,10 +30674,7 @@ function GitHubSyncProjectPullRequestsPage() {
30312
30674
  {
30313
30675
  type: "button",
30314
30676
  className: getPluginActionClassName({ variant: "secondary", size: "sm" }),
30315
- onClick: () => {
30316
- setReviewModalPullRequestId(selectedPullRequest.id);
30317
- setReviewModalDraft("");
30318
- },
30677
+ onClick: () => openReviewModal(selectedPullRequest.id),
30319
30678
  disabled: selectedPullRequestReviewPending,
30320
30679
  children: /* @__PURE__ */ jsx2(
30321
30680
  LoadingButtonContent,
@@ -30332,7 +30691,7 @@ function GitHubSyncProjectPullRequestsPage() {
30332
30691
  "button",
30333
30692
  {
30334
30693
  type: "button",
30335
- className: getPluginActionClassName({ variant: "secondary", size: "sm" }),
30694
+ className: getPluginActionClassName({ variant: "warning", size: "sm" }),
30336
30695
  onClick: () => setRerunCiPullRequestId(selectedPullRequest.id),
30337
30696
  disabled: selectedPullRequestRerunCiPending,
30338
30697
  children: /* @__PURE__ */ jsx2(
@@ -30370,7 +30729,7 @@ function GitHubSyncProjectPullRequestsPage() {
30370
30729
  "button",
30371
30730
  {
30372
30731
  type: "button",
30373
- className: getPluginActionClassName({ variant: "primary", size: "sm" }),
30732
+ className: getPluginActionClassName({ variant: "success", size: "sm" }),
30374
30733
  onClick: () => {
30375
30734
  void handleMergePullRequest(selectedPullRequest);
30376
30735
  },
@@ -30999,7 +31358,7 @@ function GitHubSyncProjectPullRequestsPage() {
30999
31358
  className: "ghsync-prs-modal__textarea",
31000
31359
  value: reviewModalDraft,
31001
31360
  onChange: (event) => setReviewModalDraft(event.currentTarget.value),
31002
- placeholder: "Optional comment"
31361
+ placeholder: "Required for Comment and Request changes. Optional for Approve."
31003
31362
  }
31004
31363
  )
31005
31364
  ] }),
@@ -31014,6 +31373,25 @@ function GitHubSyncProjectPullRequestsPage() {
31014
31373
  }
31015
31374
  ),
31016
31375
  /* @__PURE__ */ jsxs2("div", { className: "ghsync-prs-modal__split-actions", children: [
31376
+ /* @__PURE__ */ jsx2(
31377
+ "button",
31378
+ {
31379
+ type: "button",
31380
+ className: getPluginActionClassName({ variant: "secondary" }),
31381
+ onClick: () => {
31382
+ void handleReviewPullRequest("comment");
31383
+ },
31384
+ disabled: !reviewModalDraft.trim() || reviewModalPending,
31385
+ children: /* @__PURE__ */ jsx2(
31386
+ LoadingButtonContent,
31387
+ {
31388
+ busy: reviewModalCommentPending,
31389
+ label: "Comment",
31390
+ busyLabel: "Commenting\u2026"
31391
+ }
31392
+ )
31393
+ }
31394
+ ),
31017
31395
  /* @__PURE__ */ jsx2(
31018
31396
  "button",
31019
31397
  {
@@ -31022,7 +31400,7 @@ function GitHubSyncProjectPullRequestsPage() {
31022
31400
  onClick: () => {
31023
31401
  void handleReviewPullRequest("request_changes");
31024
31402
  },
31025
- disabled: reviewModalPending,
31403
+ disabled: !reviewModalDraft.trim() || reviewModalPending,
31026
31404
  children: /* @__PURE__ */ jsx2(
31027
31405
  LoadingButtonContent,
31028
31406
  {
@@ -31037,7 +31415,7 @@ function GitHubSyncProjectPullRequestsPage() {
31037
31415
  "button",
31038
31416
  {
31039
31417
  type: "button",
31040
- className: getPluginActionClassName({ variant: "primary" }),
31418
+ className: getPluginActionClassName({ variant: "success" }),
31041
31419
  onClick: () => {
31042
31420
  void handleReviewPullRequest("approve");
31043
31421
  },
@@ -31094,7 +31472,7 @@ function GitHubSyncProjectPullRequestsPage() {
31094
31472
  "button",
31095
31473
  {
31096
31474
  type: "button",
31097
- className: getPluginActionClassName({ variant: "primary" }),
31475
+ className: getPluginActionClassName({ variant: "warning" }),
31098
31476
  onClick: () => {
31099
31477
  void handleRerunCi();
31100
31478
  },
@@ -31419,7 +31797,12 @@ function GitHubSyncSettingsPage() {
31419
31797
  const repositoriesUnlocked = tokenStatus === "valid";
31420
31798
  const availableAssignees = getAvailableAssigneeOptions(
31421
31799
  (currentSettings?.availableAssignees?.length ? currentSettings.availableAssignees : null) ?? (form.availableAssignees?.length ? form.availableAssignees : null) ?? browserAvailableAssignees,
31422
- form.advancedSettings.defaultAssigneeAgentId
31800
+ [
31801
+ getAdvancedSettingsAssigneePrincipal(form.advancedSettings, "default"),
31802
+ getAdvancedSettingsAssigneePrincipal(form.advancedSettings, "executor"),
31803
+ getAdvancedSettingsAssigneePrincipal(form.advancedSettings, "reviewer"),
31804
+ getAdvancedSettingsAssigneePrincipal(form.advancedSettings, "approver")
31805
+ ]
31423
31806
  );
31424
31807
  const propagationAgents = getAvailablePropagationAgentOptions(
31425
31808
  (currentSettings?.availableAssignees?.length ? currentSettings.availableAssignees : null) ?? (form.availableAssignees?.length ? form.availableAssignees : null) ?? browserAvailableAssignees,
@@ -31498,9 +31881,17 @@ function GitHubSyncSettingsPage() {
31498
31881
  const assigneeSelectOptions = [
31499
31882
  { value: "", label: "Unassigned" },
31500
31883
  ...availableAssignees.map((option) => ({
31501
- value: option.id,
31884
+ value: getAssigneeOptionValue(option),
31502
31885
  label: formatAssigneeOptionLabel(option),
31503
- icon: "agent"
31886
+ icon: option.kind
31887
+ }))
31888
+ ];
31889
+ const transitionAssigneeSelectOptions = [
31890
+ { value: "", label: "Automatic routing" },
31891
+ ...availableAssignees.map((option) => ({
31892
+ value: getAssigneeOptionValue(option),
31893
+ label: formatAssigneeOptionLabel(option),
31894
+ icon: option.kind
31504
31895
  }))
31505
31896
  ];
31506
31897
  const propagationAgentOptions = propagationAgents.map((option) => ({
@@ -31800,7 +32191,7 @@ function GitHubSyncSettingsPage() {
31800
32191
  throw new Error("Allow pop-ups for Paperclip, then try connecting board access again.");
31801
32192
  }
31802
32193
  const boardApiToken = await waitForBoardAccessApproval(challenge);
31803
- const identity = await fetchBoardAccessIdentity(boardApiToken);
32194
+ const boardIdentity = await fetchBoardAccessIdentity(boardApiToken);
31804
32195
  const secretName = `paperclip_board_api_${companyId.replace(/[^a-z0-9]+/gi, "_").toLowerCase()}`;
31805
32196
  const secret = await resolveOrCreateCompanySecret(companyId, secretName, boardApiToken);
31806
32197
  await patchPluginConfig(pluginId, {
@@ -31811,15 +32202,16 @@ function GitHubSyncSettingsPage() {
31811
32202
  await updateBoardAccess({
31812
32203
  companyId,
31813
32204
  paperclipBoardApiTokenRef: secret.id,
31814
- paperclipBoardAccessIdentity: identity ?? ""
32205
+ paperclipBoardAccessIdentity: boardIdentity.label ?? "",
32206
+ paperclipBoardAccessUserId: boardIdentity.userId ?? ""
31815
32207
  });
31816
- setBoardAccessIdentity(identity);
32208
+ setBoardAccessIdentity(boardIdentity.label);
31817
32209
  setForm((current) => ({
31818
32210
  ...current,
31819
32211
  paperclipBoardAccessConfigured: true
31820
32212
  }));
31821
32213
  toast({
31822
- title: identity ? `Paperclip board access connected as ${identity}` : "Paperclip board access connected",
32214
+ title: boardIdentity.label ? `Paperclip board access connected as ${boardIdentity.label}` : "Paperclip board access connected",
31823
32215
  body: "Direct Paperclip REST calls can now authenticate in authenticated deployments.",
31824
32216
  tone: "success"
31825
32217
  });
@@ -32374,16 +32766,17 @@ function GitHubSyncSettingsPage() {
32374
32766
  SettingsAssigneePicker,
32375
32767
  {
32376
32768
  id: "advanced-default-assignee",
32377
- value: form.advancedSettings.defaultAssigneeAgentId ?? "",
32769
+ value: getSelectedAssigneeOptionValue(form.advancedSettings, "default"),
32378
32770
  options: assigneeSelectOptions,
32379
32771
  disabled: settingsMutationsLocked,
32380
32772
  onChange: (nextValue) => {
32381
32773
  setForm((current) => ({
32382
32774
  ...current,
32383
- advancedSettings: {
32384
- ...current.advancedSettings,
32385
- ...nextValue ? { defaultAssigneeAgentId: nextValue } : { defaultAssigneeAgentId: void 0 }
32386
- }
32775
+ advancedSettings: setAdvancedSettingsAssigneePrincipal(
32776
+ current.advancedSettings,
32777
+ "default",
32778
+ parseAssigneeOptionValue(nextValue)
32779
+ )
32387
32780
  }));
32388
32781
  }
32389
32782
  }
@@ -32409,8 +32802,78 @@ function GitHubSyncSettingsPage() {
32409
32802
  }
32410
32803
  }
32411
32804
  )
32805
+ ] }),
32806
+ /* @__PURE__ */ jsxs2("div", { className: "ghsync__field", children: [
32807
+ /* @__PURE__ */ jsx2("label", { htmlFor: "advanced-executor-assignee", children: "Executor handoff" }),
32808
+ /* @__PURE__ */ jsx2(
32809
+ SettingsAssigneePicker,
32810
+ {
32811
+ id: "advanced-executor-assignee",
32812
+ value: getSelectedAssigneeOptionValue(form.advancedSettings, "executor"),
32813
+ options: transitionAssigneeSelectOptions,
32814
+ disabled: settingsMutationsLocked,
32815
+ onChange: (nextValue) => {
32816
+ setForm((current) => ({
32817
+ ...current,
32818
+ advancedSettings: setAdvancedSettingsAssigneePrincipal(
32819
+ current.advancedSettings,
32820
+ "executor",
32821
+ parseAssigneeOptionValue(nextValue)
32822
+ )
32823
+ }));
32824
+ }
32825
+ }
32826
+ ),
32827
+ /* @__PURE__ */ jsx2("p", { className: "ghsync__hint", children: "The assignee that resumes work when GitHub Sync sends an issue back to active execution, such as failing CI, unresolved review threads, or a trusted new GitHub comment." })
32828
+ ] }),
32829
+ /* @__PURE__ */ jsxs2("div", { className: "ghsync__field", children: [
32830
+ /* @__PURE__ */ jsx2("label", { htmlFor: "advanced-reviewer-assignee", children: "Reviewer handoff" }),
32831
+ /* @__PURE__ */ jsx2(
32832
+ SettingsAssigneePicker,
32833
+ {
32834
+ id: "advanced-reviewer-assignee",
32835
+ value: getSelectedAssigneeOptionValue(form.advancedSettings, "reviewer"),
32836
+ options: transitionAssigneeSelectOptions,
32837
+ disabled: settingsMutationsLocked,
32838
+ onChange: (nextValue) => {
32839
+ setForm((current) => ({
32840
+ ...current,
32841
+ advancedSettings: setAdvancedSettingsAssigneePrincipal(
32842
+ current.advancedSettings,
32843
+ "reviewer",
32844
+ parseAssigneeOptionValue(nextValue)
32845
+ )
32846
+ }));
32847
+ }
32848
+ }
32849
+ ),
32850
+ /* @__PURE__ */ jsx2("p", { className: "ghsync__hint", children: "The assignee that reviews work when GitHub Sync moves an issue into `in_review` because linked pull requests are green and all review threads are resolved." })
32851
+ ] }),
32852
+ /* @__PURE__ */ jsxs2("div", { className: "ghsync__field", children: [
32853
+ /* @__PURE__ */ jsx2("label", { htmlFor: "advanced-approver-assignee", children: "Approver handoff" }),
32854
+ /* @__PURE__ */ jsx2(
32855
+ SettingsAssigneePicker,
32856
+ {
32857
+ id: "advanced-approver-assignee",
32858
+ value: getSelectedAssigneeOptionValue(form.advancedSettings, "approver"),
32859
+ options: transitionAssigneeSelectOptions,
32860
+ disabled: settingsMutationsLocked,
32861
+ onChange: (nextValue) => {
32862
+ setForm((current) => ({
32863
+ ...current,
32864
+ advancedSettings: setAdvancedSettingsAssigneePrincipal(
32865
+ current.advancedSettings,
32866
+ "approver",
32867
+ parseAssigneeOptionValue(nextValue)
32868
+ )
32869
+ }));
32870
+ }
32871
+ }
32872
+ ),
32873
+ /* @__PURE__ */ jsx2("p", { className: "ghsync__hint", children: "The assignee that approves work for the same `in_review` handoff when Paperclip's execution policy says the current stage is approval instead of review." })
32412
32874
  ] })
32413
32875
  ] }),
32876
+ /* @__PURE__ */ jsx2("p", { className: "ghsync__hint", children: `Choose "Automatic routing" to let GitHub Sync follow the issue's Paperclip execution policy and built-in fallback behavior. Pick a specific assignee only when you want a company-wide fallback for missing reviewer, approver, or return-assignee data. When Paperclip board access is connected for this company, each assignee picker also includes "Me" so the connected board user can take the handoff instead of an agent.` }),
32414
32877
  /* @__PURE__ */ jsxs2("div", { className: "ghsync__field", children: [
32415
32878
  /* @__PURE__ */ jsx2("label", { htmlFor: "advanced-ignored-authors", children: "Ignore issues from GitHub usernames" }),
32416
32879
  /* @__PURE__ */ jsx2(