paperclip-github-plugin 0.4.8 → 0.5.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/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
@@ -23927,9 +23934,15 @@ var PAGE_STYLES = `
23927
23934
 
23928
23935
  .ghsync__picker {
23929
23936
  position: relative;
23937
+ min-width: 0;
23938
+ }
23939
+
23940
+ .ghsync__picker--full {
23941
+ width: 100%;
23930
23942
  }
23931
23943
 
23932
23944
  .ghsync__picker-trigger {
23945
+ box-sizing: border-box;
23933
23946
  width: fit-content;
23934
23947
  max-width: 100%;
23935
23948
  min-height: 0;
@@ -23962,6 +23975,10 @@ var PAGE_STYLES = `
23962
23975
  background: var(--ghsync-surfaceRaised);
23963
23976
  }
23964
23977
 
23978
+ .ghsync__picker--full .ghsync__picker-trigger {
23979
+ width: 100%;
23980
+ }
23981
+
23965
23982
  .ghsync__picker-trigger--assignee {
23966
23983
  min-width: 10rem;
23967
23984
  font-size: 14px;
@@ -24012,6 +24029,7 @@ var PAGE_STYLES = `
24012
24029
  }
24013
24030
 
24014
24031
  .ghsync__picker-panel {
24032
+ box-sizing: border-box;
24015
24033
  position: absolute;
24016
24034
  top: calc(100% + 8px);
24017
24035
  left: 0;
@@ -24024,7 +24042,8 @@ var PAGE_STYLES = `
24024
24042
  }
24025
24043
 
24026
24044
  .ghsync__picker-panel--assignee {
24027
- width: min(20rem, calc(100vw - 2rem));
24045
+ width: min(20rem, 100%);
24046
+ max-width: calc(100vw - 2rem);
24028
24047
  }
24029
24048
 
24030
24049
  .ghsync__picker-panel--status {
@@ -24976,6 +24995,7 @@ var PROJECT_PULL_REQUESTS_PAGE_STYLES = `
24976
24995
  font-weight: 700;
24977
24996
  letter-spacing: 0.02em;
24978
24997
  flex: 0 0 auto;
24998
+ overflow: hidden;
24979
24999
  }
24980
25000
 
24981
25001
  .ghsync-prs-avatar img,
@@ -26458,12 +26478,6 @@ var EXTENSION_SURFACE_STYLES = `
26458
26478
  text-transform: uppercase;
26459
26479
  }
26460
26480
 
26461
- .ghsync-issue-detail__creator .ghsync-prs-avatar {
26462
- width: 20px;
26463
- height: 20px;
26464
- font-size: 10px;
26465
- }
26466
-
26467
26481
  .ghsync-extension-heading h3,
26468
26482
  .ghsync-extension-heading h4 {
26469
26483
  margin: 0;
@@ -26636,14 +26650,45 @@ function normalizeAgentIds(value) {
26636
26650
  rawEntries.map((entry) => typeof entry === "string" && entry.trim() ? entry.trim() : null).filter((entry) => Boolean(entry))
26637
26651
  )].sort((left, right) => left.localeCompare(right));
26638
26652
  }
26653
+ function normalizeAdvancedSettingsAssigneeOverride(record, keys2) {
26654
+ const rawUserId = record[keys2.userId];
26655
+ const userId = typeof rawUserId === "string" && rawUserId.trim() ? rawUserId.trim() : void 0;
26656
+ if (userId) {
26657
+ return {
26658
+ [keys2.userId]: userId
26659
+ };
26660
+ }
26661
+ const rawAgentId = record[keys2.agentId];
26662
+ const agentId = typeof rawAgentId === "string" && rawAgentId.trim() ? rawAgentId.trim() : void 0;
26663
+ if (agentId) {
26664
+ return {
26665
+ [keys2.agentId]: agentId
26666
+ };
26667
+ }
26668
+ return {};
26669
+ }
26639
26670
  function normalizeAdvancedSettings(value) {
26640
26671
  if (!value || typeof value !== "object") {
26641
26672
  return DEFAULT_ADVANCED_SETTINGS;
26642
26673
  }
26643
26674
  const record = value;
26644
- const defaultAssigneeAgentId = typeof record.defaultAssigneeAgentId === "string" && record.defaultAssigneeAgentId.trim() ? record.defaultAssigneeAgentId.trim() : void 0;
26645
26675
  return {
26646
- ...defaultAssigneeAgentId ? { defaultAssigneeAgentId } : {},
26676
+ ...normalizeAdvancedSettingsAssigneeOverride(record, {
26677
+ agentId: "defaultAssigneeAgentId",
26678
+ userId: "defaultAssigneeUserId"
26679
+ }),
26680
+ ...normalizeAdvancedSettingsAssigneeOverride(record, {
26681
+ agentId: "executorAssigneeAgentId",
26682
+ userId: "executorAssigneeUserId"
26683
+ }),
26684
+ ...normalizeAdvancedSettingsAssigneeOverride(record, {
26685
+ agentId: "reviewerAssigneeAgentId",
26686
+ userId: "reviewerAssigneeUserId"
26687
+ }),
26688
+ ...normalizeAdvancedSettingsAssigneeOverride(record, {
26689
+ agentId: "approverAssigneeAgentId",
26690
+ userId: "approverAssigneeUserId"
26691
+ }),
26647
26692
  defaultStatus: normalizePaperclipIssueStatus(record.defaultStatus),
26648
26693
  ignoredIssueAuthorUsernames: "ignoredIssueAuthorUsernames" in record ? normalizeIgnoredIssueAuthorUsernames(record.ignoredIssueAuthorUsernames) : DEFAULT_ADVANCED_SETTINGS.ignoredIssueAuthorUsernames,
26649
26694
  ...normalizeAgentIds(record.githubTokenPropagationAgentIds).length > 0 ? { githubTokenPropagationAgentIds: normalizeAgentIds(record.githubTokenPropagationAgentIds) } : {}
@@ -26662,6 +26707,13 @@ function getComparableAdvancedSettings(value) {
26662
26707
  const settings = normalizeAdvancedSettings(value);
26663
26708
  return {
26664
26709
  ...settings.defaultAssigneeAgentId ? { defaultAssigneeAgentId: settings.defaultAssigneeAgentId } : {},
26710
+ ...settings.defaultAssigneeUserId ? { defaultAssigneeUserId: settings.defaultAssigneeUserId } : {},
26711
+ ...settings.executorAssigneeAgentId ? { executorAssigneeAgentId: settings.executorAssigneeAgentId } : {},
26712
+ ...settings.executorAssigneeUserId ? { executorAssigneeUserId: settings.executorAssigneeUserId } : {},
26713
+ ...settings.reviewerAssigneeAgentId ? { reviewerAssigneeAgentId: settings.reviewerAssigneeAgentId } : {},
26714
+ ...settings.reviewerAssigneeUserId ? { reviewerAssigneeUserId: settings.reviewerAssigneeUserId } : {},
26715
+ ...settings.approverAssigneeAgentId ? { approverAssigneeAgentId: settings.approverAssigneeAgentId } : {},
26716
+ ...settings.approverAssigneeUserId ? { approverAssigneeUserId: settings.approverAssigneeUserId } : {},
26665
26717
  defaultStatus: settings.defaultStatus,
26666
26718
  ignoredIssueAuthorUsernames: [...settings.ignoredIssueAuthorUsernames].sort((left, right) => left.localeCompare(right)),
26667
26719
  ...settings.githubTokenPropagationAgentIds?.length ? { githubTokenPropagationAgentIds: [...settings.githubTokenPropagationAgentIds].sort((left, right) => left.localeCompare(right)) } : {}
@@ -26670,43 +26722,127 @@ function getComparableAdvancedSettings(value) {
26670
26722
  function formatAssigneeOptionLabel(option) {
26671
26723
  return option.title?.trim() ? `${option.name} (${option.title.trim()})` : option.name;
26672
26724
  }
26673
- function getAvailableAssigneeOptions(options, selectedAgentId) {
26725
+ function getAssigneeOptionKey(option) {
26726
+ return `${option.kind}:${option.id}`;
26727
+ }
26728
+ function getAssigneeOptionValue(option) {
26729
+ return getAssigneeOptionKey(option);
26730
+ }
26731
+ function parseAssigneeOptionValue(value) {
26732
+ const trimmedValue = typeof value === "string" ? value.trim() : "";
26733
+ if (!trimmedValue) {
26734
+ return null;
26735
+ }
26736
+ if (trimmedValue.startsWith("agent:")) {
26737
+ const id = trimmedValue.slice("agent:".length).trim();
26738
+ return id ? { kind: "agent", id } : null;
26739
+ }
26740
+ if (trimmedValue.startsWith("user:")) {
26741
+ const id = trimmedValue.slice("user:".length).trim();
26742
+ return id ? { kind: "user", id } : null;
26743
+ }
26744
+ return { kind: "agent", id: trimmedValue };
26745
+ }
26746
+ function getAdvancedSettingsAssigneePrincipal(advancedSettings, role) {
26747
+ switch (role) {
26748
+ case "default":
26749
+ return advancedSettings.defaultAssigneeUserId ? { kind: "user", id: advancedSettings.defaultAssigneeUserId } : advancedSettings.defaultAssigneeAgentId ? { kind: "agent", id: advancedSettings.defaultAssigneeAgentId } : null;
26750
+ case "executor":
26751
+ return advancedSettings.executorAssigneeUserId ? { kind: "user", id: advancedSettings.executorAssigneeUserId } : advancedSettings.executorAssigneeAgentId ? { kind: "agent", id: advancedSettings.executorAssigneeAgentId } : null;
26752
+ case "reviewer":
26753
+ return advancedSettings.reviewerAssigneeUserId ? { kind: "user", id: advancedSettings.reviewerAssigneeUserId } : advancedSettings.reviewerAssigneeAgentId ? { kind: "agent", id: advancedSettings.reviewerAssigneeAgentId } : null;
26754
+ case "approver":
26755
+ return advancedSettings.approverAssigneeUserId ? { kind: "user", id: advancedSettings.approverAssigneeUserId } : advancedSettings.approverAssigneeAgentId ? { kind: "agent", id: advancedSettings.approverAssigneeAgentId } : null;
26756
+ }
26757
+ }
26758
+ function setAdvancedSettingsAssigneePrincipal(advancedSettings, role, principal) {
26759
+ switch (role) {
26760
+ case "default":
26761
+ return {
26762
+ ...advancedSettings,
26763
+ defaultAssigneeAgentId: principal?.kind === "agent" ? principal.id : void 0,
26764
+ defaultAssigneeUserId: principal?.kind === "user" ? principal.id : void 0
26765
+ };
26766
+ case "executor":
26767
+ return {
26768
+ ...advancedSettings,
26769
+ executorAssigneeAgentId: principal?.kind === "agent" ? principal.id : void 0,
26770
+ executorAssigneeUserId: principal?.kind === "user" ? principal.id : void 0
26771
+ };
26772
+ case "reviewer":
26773
+ return {
26774
+ ...advancedSettings,
26775
+ reviewerAssigneeAgentId: principal?.kind === "agent" ? principal.id : void 0,
26776
+ reviewerAssigneeUserId: principal?.kind === "user" ? principal.id : void 0
26777
+ };
26778
+ case "approver":
26779
+ return {
26780
+ ...advancedSettings,
26781
+ approverAssigneeAgentId: principal?.kind === "agent" ? principal.id : void 0,
26782
+ approverAssigneeUserId: principal?.kind === "user" ? principal.id : void 0
26783
+ };
26784
+ }
26785
+ }
26786
+ function getSelectedAssigneeOptionValue(advancedSettings, role) {
26787
+ const principal = getAdvancedSettingsAssigneePrincipal(advancedSettings, role);
26788
+ return principal ? getAssigneeOptionValue(principal) : "";
26789
+ }
26790
+ function compareAssigneeOptions(left, right) {
26791
+ if (left.kind !== right.kind) {
26792
+ return left.kind === "user" ? -1 : 1;
26793
+ }
26794
+ return left.name.localeCompare(right.name);
26795
+ }
26796
+ function getAvailableAssigneeOptions(options, selectedAssignees) {
26674
26797
  const normalizedOptions = [...options ?? []];
26675
- if (selectedAgentId && !normalizedOptions.some((option) => option.id === selectedAgentId)) {
26676
- normalizedOptions.push({
26677
- id: selectedAgentId,
26678
- name: "Unavailable agent"
26679
- });
26798
+ const selectedPrincipals = Array.isArray(selectedAssignees) ? selectedAssignees.filter((selectedAssignee) => Boolean(selectedAssignee)) : selectedAssignees ? [selectedAssignees] : [];
26799
+ for (const selectedPrincipal of selectedPrincipals) {
26800
+ if (!normalizedOptions.some((option) => getAssigneeOptionKey(option) === getAssigneeOptionKey(selectedPrincipal))) {
26801
+ normalizedOptions.push({
26802
+ kind: selectedPrincipal.kind,
26803
+ id: selectedPrincipal.id,
26804
+ name: selectedPrincipal.kind === "user" ? "Unavailable user" : "Unavailable agent"
26805
+ });
26806
+ }
26680
26807
  }
26681
- return normalizedOptions;
26808
+ return normalizedOptions.sort(compareAssigneeOptions);
26682
26809
  }
26683
26810
  function getAvailablePropagationAgentOptions(options, selectedAgentIds) {
26684
- const normalizedOptions = [...options ?? []];
26811
+ const normalizedOptions = [...(options ?? []).filter((option) => option.kind === "agent")];
26685
26812
  const selectedIds = normalizeAgentIds(selectedAgentIds);
26686
26813
  for (const selectedAgentId of selectedIds) {
26687
26814
  if (!normalizedOptions.some((option) => option.id === selectedAgentId)) {
26688
26815
  normalizedOptions.push({
26816
+ kind: "agent",
26689
26817
  id: selectedAgentId,
26690
26818
  name: "Unavailable agent"
26691
26819
  });
26692
26820
  }
26693
26821
  }
26694
- return normalizedOptions.sort((left, right) => left.name.localeCompare(right.name));
26822
+ return normalizedOptions.sort(compareAssigneeOptions);
26695
26823
  }
26696
26824
  function formatAdvancedSettingsSummary(advancedSettings, availableAssignees, options) {
26697
- const assigneeLabel = advancedSettings.defaultAssigneeAgentId ? formatAssigneeOptionLabel(
26698
- availableAssignees.find((option) => option.id === advancedSettings.defaultAssigneeAgentId) ?? {
26699
- id: advancedSettings.defaultAssigneeAgentId,
26700
- name: "Unavailable agent"
26701
- }
26702
- ) : "Unassigned";
26825
+ const resolveAssigneeLabel = (principal, fallbackLabel = "None") => {
26826
+ return principal ? formatAssigneeOptionLabel(
26827
+ availableAssignees.find((option) => getAssigneeOptionKey(option) === getAssigneeOptionKey(principal)) ?? {
26828
+ kind: principal.kind,
26829
+ id: principal.id,
26830
+ name: principal.kind === "user" ? "Unavailable user" : "Unavailable agent"
26831
+ }
26832
+ ) : fallbackLabel;
26833
+ };
26834
+ const assigneeLabel = resolveAssigneeLabel(getAdvancedSettingsAssigneePrincipal(advancedSettings, "default"), "Unassigned");
26835
+ const executorLabel = resolveAssigneeLabel(getAdvancedSettingsAssigneePrincipal(advancedSettings, "executor"), "Automatic routing");
26836
+ const reviewerLabel = resolveAssigneeLabel(getAdvancedSettingsAssigneePrincipal(advancedSettings, "reviewer"), "Automatic routing");
26837
+ const approverLabel = resolveAssigneeLabel(getAdvancedSettingsAssigneePrincipal(advancedSettings, "approver"), "Automatic routing");
26703
26838
  const statusLabel = PAPERCLIP_STATUS_OPTIONS.find((option) => option.value === advancedSettings.defaultStatus)?.label ?? "Backlog";
26704
26839
  const ignoredAuthorsLabel = advancedSettings.ignoredIssueAuthorUsernames.length > 0 ? advancedSettings.ignoredIssueAuthorUsernames.join(", ") : "none";
26840
+ const handoffLabel = `Back to work: ${executorLabel} \xB7 Review: ${reviewerLabel} \xB7 Approval: ${approverLabel}`;
26705
26841
  if (options?.includePropagation) {
26706
26842
  const propagatedAgentsLabel = advancedSettings.githubTokenPropagationAgentIds?.length ? `${advancedSettings.githubTokenPropagationAgentIds.length} selected` : "none";
26707
- return `Assignee: ${assigneeLabel} \xB7 Status: ${statusLabel} \xB7 Ignore: ${ignoredAuthorsLabel} \xB7 Propagate: ${propagatedAgentsLabel}`;
26843
+ return `Import: ${assigneeLabel} \xB7 ${handoffLabel} \xB7 Status: ${statusLabel} \xB7 Ignore: ${ignoredAuthorsLabel} \xB7 Propagate: ${propagatedAgentsLabel}`;
26708
26844
  }
26709
- return `Assignee: ${assigneeLabel} \xB7 Status: ${statusLabel} \xB7 Ignore: ${ignoredAuthorsLabel}`;
26845
+ return `Import: ${assigneeLabel} \xB7 ${handoffLabel} \xB7 Status: ${statusLabel} \xB7 Ignore: ${ignoredAuthorsLabel}`;
26710
26846
  }
26711
26847
  function resolveSavedTokenUiState(params) {
26712
26848
  if (params.githubTokenConfigured) {
@@ -26777,6 +26913,29 @@ function AgentIcon() {
26777
26913
  )
26778
26914
  ] });
26779
26915
  }
26916
+ function UserIcon() {
26917
+ return /* @__PURE__ */ jsxs2("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
26918
+ /* @__PURE__ */ jsx2("circle", { cx: "8", cy: "5.1", r: "2.3", stroke: "currentColor", strokeWidth: "1.2" }),
26919
+ /* @__PURE__ */ jsx2(
26920
+ "path",
26921
+ {
26922
+ 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",
26923
+ stroke: "currentColor",
26924
+ strokeWidth: "1.2",
26925
+ strokeLinecap: "round"
26926
+ }
26927
+ )
26928
+ ] });
26929
+ }
26930
+ function SettingsSelectIcon(props) {
26931
+ if (props.icon === "agent") {
26932
+ return /* @__PURE__ */ jsx2(AgentIcon, {});
26933
+ }
26934
+ if (props.icon === "user") {
26935
+ return /* @__PURE__ */ jsx2(UserIcon, {});
26936
+ }
26937
+ return null;
26938
+ }
26780
26939
  function SettingsAssigneePicker(props) {
26781
26940
  const { id, value, options, disabled, onChange } = props;
26782
26941
  const [open, setOpen] = useState2(false);
@@ -26819,7 +26978,7 @@ function SettingsAssigneePicker(props) {
26819
26978
  setOpen(false);
26820
26979
  }
26821
26980
  }, [disabled, open]);
26822
- return /* @__PURE__ */ jsxs2("div", { className: "ghsync__picker", ref: rootRef, children: [
26981
+ return /* @__PURE__ */ jsxs2("div", { className: "ghsync__picker ghsync__picker--full", ref: rootRef, children: [
26823
26982
  /* @__PURE__ */ jsxs2(
26824
26983
  "button",
26825
26984
  {
@@ -26837,7 +26996,7 @@ function SettingsAssigneePicker(props) {
26837
26996
  },
26838
26997
  children: [
26839
26998
  /* @__PURE__ */ jsxs2("span", { className: "ghsync__picker-trigger-main", children: [
26840
- selectedOption?.icon === "agent" ? /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-agent-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(AgentIcon, {}) }) : null,
26999
+ selectedOption?.icon ? /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-agent-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(SettingsSelectIcon, { icon: selectedOption.icon }) }) : null,
26841
27000
  /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-trigger-label", children: selectedOption?.label ?? "No assignee" })
26842
27001
  ] }),
26843
27002
  /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-trigger-icon", children: /* @__PURE__ */ jsx2(PickerChevronIcon, {}) })
@@ -26879,7 +27038,7 @@ function SettingsAssigneePicker(props) {
26879
27038
  },
26880
27039
  children: [
26881
27040
  /* @__PURE__ */ jsxs2("span", { className: "ghsync__picker-trigger-main", children: [
26882
- option.icon === "agent" ? /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-agent-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(AgentIcon, {}) }) : null,
27041
+ option.icon ? /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-agent-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(SettingsSelectIcon, { icon: option.icon }) }) : null,
26883
27042
  /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-option-label", children: option.label })
26884
27043
  ] }),
26885
27044
  /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-option-check", "aria-hidden": "true", children: selected ? /* @__PURE__ */ jsx2(PickerCheckIcon, {}) : null })
@@ -26935,7 +27094,7 @@ function SettingsAgentMultiPicker(props) {
26935
27094
  setOpen(false);
26936
27095
  }
26937
27096
  }, [disabled, open]);
26938
- return /* @__PURE__ */ jsxs2("div", { className: "ghsync__picker", ref: rootRef, children: [
27097
+ return /* @__PURE__ */ jsxs2("div", { className: "ghsync__picker ghsync__picker--full", ref: rootRef, children: [
26939
27098
  /* @__PURE__ */ jsxs2(
26940
27099
  "button",
26941
27100
  {
@@ -26995,7 +27154,7 @@ function SettingsAgentMultiPicker(props) {
26995
27154
  },
26996
27155
  children: [
26997
27156
  /* @__PURE__ */ jsxs2("span", { className: "ghsync__picker-trigger-main", children: [
26998
- option.icon === "agent" ? /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-agent-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(AgentIcon, {}) }) : null,
27157
+ option.icon ? /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-agent-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(SettingsSelectIcon, { icon: option.icon }) }) : null,
26999
27158
  /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-option-label", children: option.label })
27000
27159
  ] }),
27001
27160
  /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-option-check", "aria-hidden": "true", children: selected ? /* @__PURE__ */ jsx2(PickerCheckIcon, {}) : null })
@@ -27268,6 +27427,22 @@ function formatShortDateTime(value, fallback = "Unknown time") {
27268
27427
  function pluralize(count, singular, plural = `${singular}s`) {
27269
27428
  return `${count} ${count === 1 ? singular : plural}`;
27270
27429
  }
27430
+ function resolvePreviewPersonLabels(person) {
27431
+ const displayHandle = person.handle.trim();
27432
+ const displayName = person.name.trim() || displayHandle || "Unknown user";
27433
+ const normalizedName = displayName.replace(/^@/, "").trim().toLowerCase();
27434
+ const normalizedHandle = displayHandle.replace(/^@/, "").trim().toLowerCase();
27435
+ if (displayHandle && normalizedName && normalizedHandle && normalizedName === normalizedHandle) {
27436
+ return {
27437
+ primary: displayHandle,
27438
+ secondary: null
27439
+ };
27440
+ }
27441
+ return {
27442
+ primary: displayName,
27443
+ secondary: displayHandle && displayHandle !== displayName ? displayHandle : null
27444
+ };
27445
+ }
27271
27446
  function hashString(value) {
27272
27447
  let hash = 0;
27273
27448
  for (const character of value) {
@@ -28082,6 +28257,20 @@ function getCliAuthIdentityLabel(identity) {
28082
28257
  }
28083
28258
  return null;
28084
28259
  }
28260
+ function getCliAuthIdentityUserId(identity) {
28261
+ const candidates = [
28262
+ identity.user?.id,
28263
+ identity.user?.userId,
28264
+ identity.id,
28265
+ identity.userId
28266
+ ];
28267
+ for (const candidate of candidates) {
28268
+ if (typeof candidate === "string" && candidate.trim()) {
28269
+ return candidate.trim();
28270
+ }
28271
+ }
28272
+ return null;
28273
+ }
28085
28274
  function waitForDuration(durationMs) {
28086
28275
  return new Promise((resolve) => {
28087
28276
  globalThis.setTimeout(resolve, durationMs);
@@ -28136,7 +28325,10 @@ async function fetchBoardAccessIdentity(boardApiToken) {
28136
28325
  authorization: `Bearer ${boardApiToken.trim()}`
28137
28326
  }
28138
28327
  });
28139
- return getCliAuthIdentityLabel(identity);
28328
+ return {
28329
+ label: getCliAuthIdentityLabel(identity),
28330
+ userId: getCliAuthIdentityUserId(identity)
28331
+ };
28140
28332
  }
28141
28333
  function getSyncStatus(syncState, runningSync, syncUnlocked) {
28142
28334
  if (!syncUnlocked) {
@@ -28772,17 +28964,57 @@ function SyncDiagnosticsPanel(props) {
28772
28964
  function PreviewAvatar(props) {
28773
28965
  const backgroundColor = getPreviewAvatarColor(props.person.handle);
28774
28966
  const className = props.stacked ? "ghsync-prs-avatar-stack__item" : "ghsync-prs-avatar";
28967
+ const labels = resolvePreviewPersonLabels(props.person);
28968
+ const initialsSource = props.person.name.trim() || props.person.handle.replace(/^@/, "").trim();
28969
+ const title = labels.secondary ? `${labels.primary} (${labels.secondary})` : labels.primary;
28970
+ const avatarStyle = {
28971
+ backgroundColor
28972
+ };
28973
+ const imageStyle = props.size === "sm" ? {
28974
+ width: "100%",
28975
+ height: "100%",
28976
+ borderRadius: "inherit",
28977
+ objectFit: "cover",
28978
+ display: "block"
28979
+ } : void 0;
28980
+ if (props.size === "sm") {
28981
+ Object.assign(avatarStyle, {
28982
+ width: 20,
28983
+ height: 20,
28984
+ fontSize: 10,
28985
+ display: "inline-flex",
28986
+ alignItems: "center",
28987
+ justifyContent: "center",
28988
+ borderRadius: 999,
28989
+ color: "white",
28990
+ fontWeight: 700,
28991
+ letterSpacing: "0.02em",
28992
+ flex: "0 0 auto",
28993
+ overflow: "hidden"
28994
+ });
28995
+ }
28775
28996
  return /* @__PURE__ */ jsx2(
28776
28997
  "span",
28777
28998
  {
28778
28999
  className,
28779
- style: { backgroundColor },
28780
- title: `${props.person.name} (${props.person.handle})`,
29000
+ style: avatarStyle,
29001
+ title,
28781
29002
  "aria-hidden": "true",
28782
- children: props.person.avatarUrl ? /* @__PURE__ */ jsx2("img", { src: props.person.avatarUrl, alt: "", loading: "lazy" }) : getInitials(props.person.name)
29003
+ children: props.person.avatarUrl ? /* @__PURE__ */ jsx2("img", { src: props.person.avatarUrl, alt: "", loading: "lazy", style: imageStyle }) : getInitials(initialsSource)
28783
29004
  }
28784
29005
  );
28785
29006
  }
29007
+ function PreviewPersonCopy(props) {
29008
+ const labels = resolvePreviewPersonLabels(props.person);
29009
+ return /* @__PURE__ */ jsxs2("span", { className: "ghsync-prs-table__person-copy", children: [
29010
+ /* @__PURE__ */ jsx2("span", { className: "ghsync-prs-table__person-name", children: labels.primary }),
29011
+ labels.secondary ? /* @__PURE__ */ jsx2("span", { className: "ghsync-prs-table__person-handle", children: labels.secondary }) : null
29012
+ ] });
29013
+ }
29014
+ function PreviewPersonInlineLabel(props) {
29015
+ const labels = resolvePreviewPersonLabels(props.person);
29016
+ return /* @__PURE__ */ jsx2("span", { children: labels.secondary ? `${labels.primary} (${labels.secondary})` : labels.primary });
29017
+ }
28786
29018
  function PreviewMarkdown(props) {
28787
29019
  return /* @__PURE__ */ jsx2("div", { className: "ghsync-prs-markdown paperclip-markdown prose prose-sm max-w-none break-words overflow-hidden", children: /* @__PURE__ */ jsx2(
28788
29020
  Markdown,
@@ -29964,10 +30196,7 @@ function GitHubSyncProjectPullRequestsPage() {
29964
30196
  rel: "noreferrer",
29965
30197
  children: [
29966
30198
  /* @__PURE__ */ jsx2(PreviewAvatar, { person: pullRequest.author }),
29967
- /* @__PURE__ */ jsxs2("span", { className: "ghsync-prs-table__person-copy", children: [
29968
- /* @__PURE__ */ jsx2("span", { className: "ghsync-prs-table__person-name", children: pullRequest.author.name }),
29969
- /* @__PURE__ */ jsx2("span", { className: "ghsync-prs-table__person-handle", children: pullRequest.author.handle })
29970
- ] })
30199
+ /* @__PURE__ */ jsx2(PreviewPersonCopy, { person: pullRequest.author })
29971
30200
  ]
29972
30201
  }
29973
30202
  ) }),
@@ -30453,12 +30682,7 @@ function GitHubSyncProjectPullRequestsPage() {
30453
30682
  /* @__PURE__ */ jsx2("span", { className: "ghsync-prs-meta__label", children: "Author" }),
30454
30683
  /* @__PURE__ */ jsxs2("div", { className: "ghsync-prs-meta__value ghsync-prs-meta__value--stack", children: [
30455
30684
  /* @__PURE__ */ jsx2(PreviewAvatar, { person: selectedPullRequest.author }),
30456
- /* @__PURE__ */ jsxs2("span", { children: [
30457
- selectedPullRequest.author.name,
30458
- " (",
30459
- selectedPullRequest.author.handle,
30460
- ")"
30461
- ] })
30685
+ /* @__PURE__ */ jsx2(PreviewPersonInlineLabel, { person: selectedPullRequest.author })
30462
30686
  ] })
30463
30687
  ] }),
30464
30688
  /* @__PURE__ */ jsxs2("div", { className: "ghsync-prs-meta__row", children: [
@@ -31396,7 +31620,12 @@ function GitHubSyncSettingsPage() {
31396
31620
  const repositoriesUnlocked = tokenStatus === "valid";
31397
31621
  const availableAssignees = getAvailableAssigneeOptions(
31398
31622
  (currentSettings?.availableAssignees?.length ? currentSettings.availableAssignees : null) ?? (form.availableAssignees?.length ? form.availableAssignees : null) ?? browserAvailableAssignees,
31399
- form.advancedSettings.defaultAssigneeAgentId
31623
+ [
31624
+ getAdvancedSettingsAssigneePrincipal(form.advancedSettings, "default"),
31625
+ getAdvancedSettingsAssigneePrincipal(form.advancedSettings, "executor"),
31626
+ getAdvancedSettingsAssigneePrincipal(form.advancedSettings, "reviewer"),
31627
+ getAdvancedSettingsAssigneePrincipal(form.advancedSettings, "approver")
31628
+ ]
31400
31629
  );
31401
31630
  const propagationAgents = getAvailablePropagationAgentOptions(
31402
31631
  (currentSettings?.availableAssignees?.length ? currentSettings.availableAssignees : null) ?? (form.availableAssignees?.length ? form.availableAssignees : null) ?? browserAvailableAssignees,
@@ -31475,9 +31704,17 @@ function GitHubSyncSettingsPage() {
31475
31704
  const assigneeSelectOptions = [
31476
31705
  { value: "", label: "Unassigned" },
31477
31706
  ...availableAssignees.map((option) => ({
31478
- value: option.id,
31707
+ value: getAssigneeOptionValue(option),
31479
31708
  label: formatAssigneeOptionLabel(option),
31480
- icon: "agent"
31709
+ icon: option.kind
31710
+ }))
31711
+ ];
31712
+ const transitionAssigneeSelectOptions = [
31713
+ { value: "", label: "Automatic routing" },
31714
+ ...availableAssignees.map((option) => ({
31715
+ value: getAssigneeOptionValue(option),
31716
+ label: formatAssigneeOptionLabel(option),
31717
+ icon: option.kind
31481
31718
  }))
31482
31719
  ];
31483
31720
  const propagationAgentOptions = propagationAgents.map((option) => ({
@@ -31777,7 +32014,7 @@ function GitHubSyncSettingsPage() {
31777
32014
  throw new Error("Allow pop-ups for Paperclip, then try connecting board access again.");
31778
32015
  }
31779
32016
  const boardApiToken = await waitForBoardAccessApproval(challenge);
31780
- const identity = await fetchBoardAccessIdentity(boardApiToken);
32017
+ const boardIdentity = await fetchBoardAccessIdentity(boardApiToken);
31781
32018
  const secretName = `paperclip_board_api_${companyId.replace(/[^a-z0-9]+/gi, "_").toLowerCase()}`;
31782
32019
  const secret = await resolveOrCreateCompanySecret(companyId, secretName, boardApiToken);
31783
32020
  await patchPluginConfig(pluginId, {
@@ -31788,15 +32025,16 @@ function GitHubSyncSettingsPage() {
31788
32025
  await updateBoardAccess({
31789
32026
  companyId,
31790
32027
  paperclipBoardApiTokenRef: secret.id,
31791
- paperclipBoardAccessIdentity: identity ?? ""
32028
+ paperclipBoardAccessIdentity: boardIdentity.label ?? "",
32029
+ paperclipBoardAccessUserId: boardIdentity.userId ?? ""
31792
32030
  });
31793
- setBoardAccessIdentity(identity);
32031
+ setBoardAccessIdentity(boardIdentity.label);
31794
32032
  setForm((current) => ({
31795
32033
  ...current,
31796
32034
  paperclipBoardAccessConfigured: true
31797
32035
  }));
31798
32036
  toast({
31799
- title: identity ? `Paperclip board access connected as ${identity}` : "Paperclip board access connected",
32037
+ title: boardIdentity.label ? `Paperclip board access connected as ${boardIdentity.label}` : "Paperclip board access connected",
31800
32038
  body: "Direct Paperclip REST calls can now authenticate in authenticated deployments.",
31801
32039
  tone: "success"
31802
32040
  });
@@ -32351,16 +32589,17 @@ function GitHubSyncSettingsPage() {
32351
32589
  SettingsAssigneePicker,
32352
32590
  {
32353
32591
  id: "advanced-default-assignee",
32354
- value: form.advancedSettings.defaultAssigneeAgentId ?? "",
32592
+ value: getSelectedAssigneeOptionValue(form.advancedSettings, "default"),
32355
32593
  options: assigneeSelectOptions,
32356
32594
  disabled: settingsMutationsLocked,
32357
32595
  onChange: (nextValue) => {
32358
32596
  setForm((current) => ({
32359
32597
  ...current,
32360
- advancedSettings: {
32361
- ...current.advancedSettings,
32362
- ...nextValue ? { defaultAssigneeAgentId: nextValue } : { defaultAssigneeAgentId: void 0 }
32363
- }
32598
+ advancedSettings: setAdvancedSettingsAssigneePrincipal(
32599
+ current.advancedSettings,
32600
+ "default",
32601
+ parseAssigneeOptionValue(nextValue)
32602
+ )
32364
32603
  }));
32365
32604
  }
32366
32605
  }
@@ -32386,8 +32625,78 @@ function GitHubSyncSettingsPage() {
32386
32625
  }
32387
32626
  }
32388
32627
  )
32628
+ ] }),
32629
+ /* @__PURE__ */ jsxs2("div", { className: "ghsync__field", children: [
32630
+ /* @__PURE__ */ jsx2("label", { htmlFor: "advanced-executor-assignee", children: "Executor handoff" }),
32631
+ /* @__PURE__ */ jsx2(
32632
+ SettingsAssigneePicker,
32633
+ {
32634
+ id: "advanced-executor-assignee",
32635
+ value: getSelectedAssigneeOptionValue(form.advancedSettings, "executor"),
32636
+ options: transitionAssigneeSelectOptions,
32637
+ disabled: settingsMutationsLocked,
32638
+ onChange: (nextValue) => {
32639
+ setForm((current) => ({
32640
+ ...current,
32641
+ advancedSettings: setAdvancedSettingsAssigneePrincipal(
32642
+ current.advancedSettings,
32643
+ "executor",
32644
+ parseAssigneeOptionValue(nextValue)
32645
+ )
32646
+ }));
32647
+ }
32648
+ }
32649
+ ),
32650
+ /* @__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." })
32651
+ ] }),
32652
+ /* @__PURE__ */ jsxs2("div", { className: "ghsync__field", children: [
32653
+ /* @__PURE__ */ jsx2("label", { htmlFor: "advanced-reviewer-assignee", children: "Reviewer handoff" }),
32654
+ /* @__PURE__ */ jsx2(
32655
+ SettingsAssigneePicker,
32656
+ {
32657
+ id: "advanced-reviewer-assignee",
32658
+ value: getSelectedAssigneeOptionValue(form.advancedSettings, "reviewer"),
32659
+ options: transitionAssigneeSelectOptions,
32660
+ disabled: settingsMutationsLocked,
32661
+ onChange: (nextValue) => {
32662
+ setForm((current) => ({
32663
+ ...current,
32664
+ advancedSettings: setAdvancedSettingsAssigneePrincipal(
32665
+ current.advancedSettings,
32666
+ "reviewer",
32667
+ parseAssigneeOptionValue(nextValue)
32668
+ )
32669
+ }));
32670
+ }
32671
+ }
32672
+ ),
32673
+ /* @__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." })
32674
+ ] }),
32675
+ /* @__PURE__ */ jsxs2("div", { className: "ghsync__field", children: [
32676
+ /* @__PURE__ */ jsx2("label", { htmlFor: "advanced-approver-assignee", children: "Approver handoff" }),
32677
+ /* @__PURE__ */ jsx2(
32678
+ SettingsAssigneePicker,
32679
+ {
32680
+ id: "advanced-approver-assignee",
32681
+ value: getSelectedAssigneeOptionValue(form.advancedSettings, "approver"),
32682
+ options: transitionAssigneeSelectOptions,
32683
+ disabled: settingsMutationsLocked,
32684
+ onChange: (nextValue) => {
32685
+ setForm((current) => ({
32686
+ ...current,
32687
+ advancedSettings: setAdvancedSettingsAssigneePrincipal(
32688
+ current.advancedSettings,
32689
+ "approver",
32690
+ parseAssigneeOptionValue(nextValue)
32691
+ )
32692
+ }));
32693
+ }
32694
+ }
32695
+ ),
32696
+ /* @__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." })
32389
32697
  ] })
32390
32698
  ] }),
32699
+ /* @__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.` }),
32391
32700
  /* @__PURE__ */ jsxs2("div", { className: "ghsync__field", children: [
32392
32701
  /* @__PURE__ */ jsx2("label", { htmlFor: "advanced-ignored-authors", children: "Ignore issues from GitHub usernames" }),
32393
32702
  /* @__PURE__ */ jsx2(
@@ -33515,11 +33824,8 @@ function GitHubSyncIssueDetailTabContent(props) {
33515
33824
  rel: "noreferrer",
33516
33825
  className: "ghsync-prs-table__person ghsync-issue-detail__creator",
33517
33826
  children: [
33518
- /* @__PURE__ */ jsx2(PreviewAvatar, { person: issueDetails.creator }),
33519
- /* @__PURE__ */ jsxs2("span", { className: "ghsync-prs-table__person-copy", children: [
33520
- /* @__PURE__ */ jsx2("span", { className: "ghsync-prs-table__person-name", children: issueDetails.creator.name }),
33521
- /* @__PURE__ */ jsx2("span", { className: "ghsync-prs-table__person-handle", children: issueDetails.creator.handle })
33522
- ] })
33827
+ /* @__PURE__ */ jsx2(PreviewAvatar, { person: issueDetails.creator, size: "sm" }),
33828
+ /* @__PURE__ */ jsx2(PreviewPersonCopy, { person: issueDetails.creator })
33523
33829
  ]
33524
33830
  }
33525
33831
  )
@@ -33705,6 +34011,7 @@ export {
33705
34011
  index_default as default,
33706
34012
  resolveGitHubIssueDetailTabState,
33707
34013
  resolveOrCreateProject,
34014
+ resolvePreviewPersonLabels,
33708
34015
  resolveSavedTokenUiState,
33709
34016
  resolveToolbarButtonState,
33710
34017
  syncGitHubTokenPropagationForAgents