@ixo/editor 6.30.3 → 6.31.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.
@@ -2117,7 +2117,7 @@
2117
2117
  },
2118
2118
  "granteeKind": {
2119
2119
  "type": "string",
2120
- "description": "How to treat the add grantee: user, group-account, or group-members."
2120
+ "description": "How to treat the add grantee: user, group-account, group-members, or address-list."
2121
2121
  },
2122
2122
  "agentQuota": {
2123
2123
  "type": "string",
@@ -2134,10 +2134,20 @@
2134
2134
  "members": {
2135
2135
  "type": "array",
2136
2136
  "description": "Resolved members to fan the grant out to when granteeKind is group-members."
2137
+ },
2138
+ "granteeAddresses": {
2139
+ "type": "array",
2140
+ "description": "Accounts pasted in bulk to grant in one transaction when granteeKind is address-list."
2137
2141
  }
2138
2142
  }
2139
2143
  },
2140
2144
  "outputSchema": [
2145
+ {
2146
+ "path": "operation",
2147
+ "displayName": "Operation",
2148
+ "type": "string",
2149
+ "description": "Which op produced this output: add, revoke or list"
2150
+ },
2141
2151
  {
2142
2152
  "path": "transactionHash",
2143
2153
  "displayName": "Tx Hash",
@@ -2168,6 +2178,12 @@
2168
2178
  "type": "string",
2169
2179
  "description": "Address granted/revoked"
2170
2180
  },
2181
+ {
2182
+ "path": "granteeAddresses",
2183
+ "displayName": "Grantee Addresses",
2184
+ "type": "array",
2185
+ "description": "Every address granted by a bulk/fan-out add"
2186
+ },
2171
2187
  {
2172
2188
  "path": "grantees",
2173
2189
  "displayName": "Grantees",
@@ -1,4 +1,5 @@
1
1
  import {
2
+ COLLECTION_USERS_MAX_GRANTS_PER_TX,
2
3
  DIFFERENT_WHEN_OPTIONS,
3
4
  DIFFERENT_WHEN_OTHER_MAX,
4
5
  EVAL_ENGINE_ACTION_TYPE,
@@ -35,6 +36,7 @@ import {
35
36
  cancelRun,
36
37
  closeRun,
37
38
  collectPhantomLegacyRun,
39
+ collectionUsersBatchCount,
38
40
  countSuccessfulRuns,
39
41
  createInvocationStore,
40
42
  createReference,
@@ -130,7 +132,7 @@ import {
130
132
  writeActionState,
131
133
  writeRunRecordAndReconcile,
132
134
  xeroOracleActivityForBlockForEditor
133
- } from "./chunk-LY32OATI.js";
135
+ } from "./chunk-ZXNBOVAA.js";
134
136
 
135
137
  // src/mantine/hooks/usePanel.ts
136
138
  import { useCallback, useMemo, useEffect, useRef } from "react";
@@ -27314,7 +27316,7 @@ import React270, { useCallback as useCallback96, useEffect as useEffect98, useRe
27314
27316
  import { Alert as Alert39, Button as Button54, Flex as Flex37, Loader as Loader44, Paper as Paper22, Stack as Stack183, Text as Text163, TextInput as TextInput10 } from "@mantine/core";
27315
27317
  import { IconSearch as IconSearch10 } from "@tabler/icons-react";
27316
27318
  var ADDRESS_PREFIX = "ixo1";
27317
- var RecipientInput = ({ value, onChange, isDisabled, placeholder }) => {
27319
+ var RecipientInput = ({ value, onChange, isDisabled, placeholder, onBulkPaste }) => {
27318
27320
  const handlers = useBlocknoteHandlers();
27319
27321
  const handlersRef = useRef30(handlers);
27320
27322
  handlersRef.current = handlers;
@@ -27388,6 +27390,13 @@ var RecipientInput = ({ value, onChange, isDisabled, placeholder }) => {
27388
27390
  rightSection: searching ? /* @__PURE__ */ React270.createElement(Loader44, { size: 14 }) : null,
27389
27391
  value: query,
27390
27392
  onChange: (e) => handleQueryChange(e.currentTarget.value),
27393
+ onPaste: (e) => {
27394
+ if (!onBulkPaste) return;
27395
+ const text = e.clipboardData?.getData("text") ?? "";
27396
+ if (!/[\r\n\t]/.test(text.trim())) return;
27397
+ e.preventDefault();
27398
+ onBulkPaste(text);
27399
+ },
27391
27400
  onKeyDown: (e) => {
27392
27401
  if (e.key === "Enter") {
27393
27402
  e.preventDefault();
@@ -33858,7 +33867,107 @@ import React320, { useCallback as useCallback112, useEffect as useEffect115, use
33858
33867
  import { ActionIcon as ActionIcon47, Alert as MantineAlert, Badge as Badge52, Box as Box60, Button as Button67, Divider as Divider22, Group as Group116, Loader as Loader61, Radio as Radio5, SegmentedControl as SegmentedControl16, Stack as Stack211, Text as Text199 } from "@mantine/core";
33859
33868
  import { useDebouncedValue } from "@mantine/hooks";
33860
33869
  import { IconAlertCircle as IconAlertCircle28, IconArrowLeft as IconArrowLeft10, IconCheck as IconCheck24, IconRefresh as IconRefresh9, IconTrash as IconTrash10 } from "@tabler/icons-react";
33870
+
33871
+ // src/core/utils/accountList.ts
33872
+ var IXO_HRP = "ixo";
33873
+ var TOKEN_SEPARATORS = /[\s,;|]+/;
33874
+ var CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
33875
+ var GENERATOR = [996825010, 642813549, 513874426, 1027748829, 705979059];
33876
+ function polymod(values) {
33877
+ let chk = 1;
33878
+ for (const value of values) {
33879
+ const top = chk >> 25;
33880
+ chk = (chk & 33554431) << 5 ^ value;
33881
+ for (let i = 0; i < 5; i += 1) {
33882
+ if (top >> i & 1) chk ^= GENERATOR[i];
33883
+ }
33884
+ }
33885
+ return chk;
33886
+ }
33887
+ function hrpExpand(hrp) {
33888
+ const high = [];
33889
+ const low = [];
33890
+ for (let i = 0; i < hrp.length; i += 1) {
33891
+ high.push(hrp.charCodeAt(i) >> 5);
33892
+ low.push(hrp.charCodeAt(i) & 31);
33893
+ }
33894
+ return [...high, 0, ...low];
33895
+ }
33896
+ function isValidBech32Address(value, hrp = IXO_HRP) {
33897
+ if (!value) return false;
33898
+ if (value !== value.toLowerCase() && value !== value.toUpperCase()) return false;
33899
+ const address = value.toLowerCase();
33900
+ if (address.length < hrp.length + 8 || address.length > 90) return false;
33901
+ if (!address.startsWith(`${hrp}1`)) return false;
33902
+ const dataPart = address.slice(hrp.length + 1);
33903
+ const data = [];
33904
+ for (const char of dataPart) {
33905
+ const index = CHARSET.indexOf(char);
33906
+ if (index === -1) return false;
33907
+ data.push(index);
33908
+ }
33909
+ return polymod([...hrpExpand(hrp), ...data]) === 1;
33910
+ }
33911
+ function normalizeToken(token) {
33912
+ let value = token.trim();
33913
+ value = value.replace(/^["'`]+|["'`]+$/g, "");
33914
+ value = value.replace(/^[([{<]+|[)\]}>.,;:]+$/g, "");
33915
+ if (/^did:ixo:/i.test(value)) value = value.slice("did:ixo:".length);
33916
+ return value.trim();
33917
+ }
33918
+ function looksLikeAddress(token) {
33919
+ return /^ixo1/i.test(token);
33920
+ }
33921
+ function parseAccountList(text) {
33922
+ const rows = [];
33923
+ const addresses = [];
33924
+ const seen = /* @__PURE__ */ new Set();
33925
+ let duplicates = 0;
33926
+ const lines = String(text ?? "").split(/\r\n|\n|\r/);
33927
+ lines.forEach((line, index) => {
33928
+ const raw = line.replace(/\t/g, " ").trim();
33929
+ if (!raw) return;
33930
+ const row = { line: index + 1, raw };
33931
+ const tokens = line.split(TOKEN_SEPARATORS).map(normalizeToken).filter(Boolean);
33932
+ const candidates = tokens.filter(looksLikeAddress);
33933
+ if (candidates.length === 0) {
33934
+ row.problem = "no-address";
33935
+ rows.push(row);
33936
+ return;
33937
+ }
33938
+ const valid = candidates.map((candidate) => candidate.toLowerCase()).filter((candidate) => isValidBech32Address(candidate));
33939
+ if (valid.length === 0) {
33940
+ row.problem = "invalid-address";
33941
+ rows.push(row);
33942
+ return;
33943
+ }
33944
+ const [address, ...extras] = valid;
33945
+ row.address = address;
33946
+ if (seen.has(address)) {
33947
+ duplicates += 1;
33948
+ row.problem = "duplicate";
33949
+ } else {
33950
+ seen.add(address);
33951
+ addresses.push(address);
33952
+ }
33953
+ rows.push(row);
33954
+ for (const extra of extras) {
33955
+ rows.push({ line: index + 1, raw: extra, address: extra, problem: "extra-in-row" });
33956
+ }
33957
+ });
33958
+ return {
33959
+ addresses,
33960
+ rows,
33961
+ // Duplicates are excluded: they are not a loss (the account is in the list
33962
+ // once already) and they are counted separately.
33963
+ rejected: rows.filter((row) => !!row.problem && row.problem !== "duplicate"),
33964
+ duplicates
33965
+ };
33966
+ }
33967
+
33968
+ // src/mantine/blocks/action/actionTypes/collectionUsers/CollectionUsersFlowDetail.tsx
33861
33969
  var LOG2 = "[collection.users]";
33970
+ var MAX_LISTED_REJECTIONS = 8;
33862
33971
  function roleLabel(role) {
33863
33972
  return role === "submit" ? "Submit (Contributor)" : "Evaluate (Evaluator)";
33864
33973
  }
@@ -33933,6 +34042,72 @@ function MaxAmountsInput({ value, onChange, disabled }) {
33933
34042
  }
33934
34043
  )));
33935
34044
  }
34045
+ function broadcastTxCount(output) {
34046
+ if (Array.isArray(output?.transactionHashes)) return output.transactionHashes.length;
34047
+ return output?.batched === false ? Number(output?.grantedCount) || 1 : 1;
34048
+ }
34049
+ function grantBroadcastSummary(output) {
34050
+ const count = Number(output?.grantedCount);
34051
+ if (!Number.isFinite(count) || count <= 1) return "Grant broadcast";
34052
+ const transactions = broadcastTxCount(output);
34053
+ return transactions === 1 ? `${count} accounts granted in one transaction` : `${count} accounts granted in ${transactions} transactions`;
34054
+ }
34055
+ function rejectionReason(problem) {
34056
+ if (problem === "invalid-address") return "not a valid ixo1\u2026 address";
34057
+ if (problem === "extra-in-row") return "a second account on the same row \u2014 only the first one on a row is granted";
34058
+ return "no account found in this row";
34059
+ }
34060
+ function signingPlan(count, canBatchGrants) {
34061
+ if (count <= 1) return null;
34062
+ if (!canBatchGrants) {
34063
+ return { color: "yellow", message: `This host cannot bundle grants \u2014 it will ask you to sign ${count} separate transactions, one per account.` };
34064
+ }
34065
+ const batches = collectionUsersBatchCount(count);
34066
+ if (batches === 1) return { color: "blue", message: `All ${count} grants go in one transaction \u2014 you sign once.` };
34067
+ return {
34068
+ color: "yellow",
34069
+ message: `${count} grants exceed the ${COLLECTION_USERS_MAX_GRANTS_PER_TX} per transaction limit, so they are split into ${batches} batches \u2014 you will be asked to sign ${batches} times, one after the other.`
34070
+ };
34071
+ }
34072
+ function BulkAccountsInput({
34073
+ text,
34074
+ onChange,
34075
+ parsed,
34076
+ isDisabled,
34077
+ canBatchGrants,
34078
+ actingAsGroup
34079
+ }) {
34080
+ const { addresses, rejected, duplicates } = parsed;
34081
+ const listed = rejected.slice(0, MAX_LISTED_REJECTIONS);
34082
+ const plan = signingPlan(addresses.length, canBatchGrants);
34083
+ return /* @__PURE__ */ React320.createElement(Stack211, { gap: "xs" }, /* @__PURE__ */ React320.createElement(
34084
+ BaseTextArea,
34085
+ {
34086
+ label: "Accounts",
34087
+ description: "Paste (Ctrl+V) a column copied from Google Sheets or Excel \u2014 one account per row. Other columns, headers and blank rows are ignored.",
34088
+ placeholder: "ixo1\u2026\nixo1\u2026\nixo1\u2026",
34089
+ minRows: 4,
34090
+ maxRows: 12,
34091
+ value: text,
34092
+ onChange: (event) => onChange(event.currentTarget.value),
34093
+ disabled: isDisabled,
34094
+ styles: { input: { fontFamily: "var(--mantine-font-family-monospace)" } }
34095
+ }
34096
+ ), /* @__PURE__ */ React320.createElement(Group116, { justify: "space-between", align: "center" }, /* @__PURE__ */ React320.createElement(Group116, { gap: "xs" }, /* @__PURE__ */ React320.createElement(Badge52, { size: "sm", variant: "light", color: addresses.length > 0 ? "green" : "gray" }, addresses.length, " account", addresses.length === 1 ? "" : "s", " to grant"), duplicates > 0 && /* @__PURE__ */ React320.createElement(Badge52, { size: "sm", variant: "light", color: "gray" }, duplicates, " duplicate", duplicates === 1 ? "" : "s", " removed"), rejected.length > 0 && /* @__PURE__ */ React320.createElement(Badge52, { size: "sm", variant: "light", color: "yellow" }, rejected.length, " row", rejected.length === 1 ? "" : "s", " skipped")), !!text.trim() && /* @__PURE__ */ React320.createElement(Button67, { size: "compact-xs", variant: "subtle", color: "gray", onClick: () => onChange(""), disabled: isDisabled }, "Clear")), rejected.length > 0 && /* @__PURE__ */ React320.createElement(DismissibleAlert, { color: "yellow", styles: actionAlertStyles }, /* @__PURE__ */ React320.createElement(Stack211, { gap: 2 }, /* @__PURE__ */ React320.createElement(Text199, { size: "xs", fw: 600 }, "These rows will NOT be granted:"), listed.map((row) => /* @__PURE__ */ React320.createElement(Text199, { key: row.line, size: "xs" }, "Row ", row.line, ": \u201C", row.raw.length > 60 ? `${row.raw.slice(0, 60)}\u2026` : row.raw, "\u201D \u2014 ", rejectionReason(row.problem))), rejected.length > listed.length && /* @__PURE__ */ React320.createElement(Text199, { size: "xs", c: "dimmed" }, "\u2026and ", rejected.length - listed.length, " more."))), addresses.length > 0 && /* @__PURE__ */ React320.createElement(
34097
+ Box60,
34098
+ {
34099
+ style: {
34100
+ border: "1px solid var(--mantine-color-neutralColor-6)",
34101
+ borderRadius: 8,
34102
+ padding: 8,
34103
+ maxHeight: 160,
34104
+ overflowY: "auto",
34105
+ background: "var(--mantine-color-neutralColor-5)"
34106
+ }
34107
+ },
34108
+ /* @__PURE__ */ React320.createElement(Stack211, { gap: 2 }, addresses.map((address, index) => /* @__PURE__ */ React320.createElement(Text199, { key: address, size: "xs", ff: "monospace", truncate: true }, index + 1, ". ", address)))
34109
+ ), actingAsGroup && addresses.length > COLLECTION_USERS_MAX_GRANTS_PER_TX && /* @__PURE__ */ React320.createElement(DismissibleAlert, { color: "red", styles: actionAlertStyles }, "Acting as a POD puts every grant in one proposal, which executes as a single transaction \u2014 so this list cannot be split. Reduce it to ", COLLECTION_USERS_MAX_GRANTS_PER_TX, " ", "accounts or fewer, or grant without acting as a POD."), !actingAsGroup && plan && /* @__PURE__ */ React320.createElement(DismissibleAlert, { color: plan.color, styles: actionAlertStyles }, plan.message));
34110
+ }
33936
34111
  function toQuotaString(value) {
33937
34112
  const n = Number(value);
33938
34113
  if (!Number.isFinite(n) || n < MIN_AGENT_QUOTA) return String(MIN_AGENT_QUOTA);
@@ -34053,7 +34228,28 @@ var CollectionUsersFlowDetail = ({
34053
34228
  const [addMaxAmounts, setAddMaxAmounts] = useState170(EMPTY_MAX_AMOUNTS);
34054
34229
  const [granteeKind, setGranteeKind] = useState170("user");
34055
34230
  const [members, setMembers] = useState170([]);
34231
+ const [addMode, setAddMode] = useState170("single");
34232
+ const [bulkText, setBulkText] = useState170("");
34233
+ const parsedAccounts = useMemo120(() => parseAccountList(bulkText), [bulkText]);
34234
+ const bulkAddresses = parsedAccounts.addresses;
34235
+ const canBatchGrants = typeof handlers.collectionUsers?.grantMany === "function";
34056
34236
  const [revokeTarget, setRevokeTarget] = useState170(null);
34237
+ const handleBulkPaste = useCallback112(
34238
+ (text) => {
34239
+ const carried = addAddress.trim();
34240
+ setActiveOp("add");
34241
+ setRevokeTarget(null);
34242
+ setAddAddress("");
34243
+ setAddMode("list");
34244
+ setBulkText((prev) => [prev.replace(/\s+$/, ""), carried, text].filter((part) => part.trim()).join("\n"));
34245
+ },
34246
+ [addAddress]
34247
+ );
34248
+ const handleAddModeChange = useCallback112((mode) => {
34249
+ setActiveOp("add");
34250
+ setRevokeTarget(null);
34251
+ setAddMode(mode);
34252
+ }, []);
34057
34253
  const classifyHandler = handlers.classifyAddress;
34058
34254
  const enumerateHandler = handlers.enumerateMembers;
34059
34255
  const canClassify = typeof classifyHandler === "function";
@@ -34116,6 +34312,8 @@ var CollectionUsersFlowDetail = ({
34116
34312
  }, [addRole]);
34117
34313
  const isGroup = !!classification?.daodao;
34118
34314
  const canExerciseGrant = !!classification?.daodao?.canExerciseGrant;
34315
+ const pendingGranteeCount = addMode === "list" ? bulkAddresses.length : granteeKind === "group-members" ? members.length : 1;
34316
+ const memberSigningPlan = useMemo120(() => signingPlan(members.length, canBatchGrants), [members.length, canBatchGrants]);
34119
34317
  const listOnLoadRef = useRef42(false);
34120
34318
  const runList = useCallback112(async () => {
34121
34319
  if (!executeAction) {
@@ -34208,20 +34406,49 @@ var CollectionUsersFlowDetail = ({
34208
34406
  if (!revokeTarget?.address) return { ok: false, error: "Pick a grantee to revoke from the list." };
34209
34407
  return { ok: true };
34210
34408
  }
34211
- const address = addAddress.trim();
34212
- if (!address) return { ok: false, error: "Enter a grantee address." };
34213
- if (granteeKind === "group-account" && !canExerciseGrant) {
34214
- return { ok: false, error: "This group cannot exercise the grant. Add its members instead." };
34409
+ if (addMode === "list") {
34410
+ if (bulkAddresses.length === 0) {
34411
+ return { ok: false, error: parsedAccounts.rows.length > 0 ? "No valid ixo1\u2026 accounts in the pasted list." : "Paste a list of accounts to grant." };
34412
+ }
34413
+ } else {
34414
+ const address = addAddress.trim();
34415
+ if (!address) return { ok: false, error: "Enter a grantee address." };
34416
+ if (granteeKind === "group-account" && !canExerciseGrant) {
34417
+ return { ok: false, error: "This group cannot hold the authorization. Grant its members individually instead." };
34418
+ }
34419
+ if (granteeKind === "group-members") {
34420
+ if (members.length === 0) return { ok: false, error: "No group members resolved yet." };
34421
+ }
34215
34422
  }
34216
- if (granteeKind === "group-members") {
34217
- if (members.length === 0) return { ok: false, error: "No group members resolved yet." };
34423
+ if (actAs.actAsGroup && pendingGranteeCount > COLLECTION_USERS_MAX_GRANTS_PER_TX) {
34424
+ return {
34425
+ ok: false,
34426
+ error: `Acting as a POD allows at most ${COLLECTION_USERS_MAX_GRANTS_PER_TX} accounts per proposal; this grants ${pendingGranteeCount}. Remove ${pendingGranteeCount - COLLECTION_USERS_MAX_GRANTS_PER_TX}, or grant without acting as a POD.`
34427
+ };
34218
34428
  }
34219
34429
  const quotaNum = Number(addQuota);
34220
34430
  if (!Number.isFinite(quotaNum) || quotaNum < MIN_AGENT_QUOTA) {
34221
34431
  return { ok: false, error: `Agent quota must be at least ${MIN_AGENT_QUOTA}.` };
34222
34432
  }
34223
34433
  return { ok: true };
34224
- }, [section, collectionId, adminAddress, adminError, activeOp, revokeTarget, addAddress, granteeKind, canExerciseGrant, members.length, addQuota]);
34434
+ }, [
34435
+ section,
34436
+ collectionId,
34437
+ adminAddress,
34438
+ adminError,
34439
+ activeOp,
34440
+ revokeTarget,
34441
+ addMode,
34442
+ bulkAddresses.length,
34443
+ parsedAccounts.rows.length,
34444
+ actAs.actAsGroup,
34445
+ pendingGranteeCount,
34446
+ addAddress,
34447
+ granteeKind,
34448
+ canExerciseGrant,
34449
+ members.length,
34450
+ addQuota
34451
+ ]);
34225
34452
  const buildRuntimeInputs = useCallback112(() => {
34226
34453
  if (section === "bids") {
34227
34454
  return { operation: "list", collectionId, adminAddress };
@@ -34237,19 +34464,21 @@ var CollectionUsersFlowDetail = ({
34237
34464
  };
34238
34465
  }
34239
34466
  const maxAmounts = buildMaxAmounts(addMaxAmounts);
34467
+ const isBulk = addMode === "list";
34240
34468
  return {
34241
34469
  operation: "add",
34242
34470
  collectionId,
34243
34471
  adminAddress,
34244
34472
  deedDid: deedDid || void 0,
34245
- granteeAddress: addAddress.trim(),
34473
+ granteeAddress: isBulk ? void 0 : addAddress.trim(),
34246
34474
  role: addRole,
34247
- granteeKind,
34475
+ granteeKind: isBulk ? "address-list" : granteeKind,
34248
34476
  agentQuota: toQuotaString(addQuota),
34249
34477
  maxAmount: addRole === "evaluate" && maxAmounts.length > 0 ? maxAmounts : void 0,
34250
- members: granteeKind === "group-members" ? members : void 0
34478
+ members: !isBulk && granteeKind === "group-members" ? members : void 0,
34479
+ granteeAddresses: isBulk ? bulkAddresses : void 0
34251
34480
  };
34252
- }, [section, activeOp, collectionId, adminAddress, deedDid, revokeTarget, addAddress, addRole, granteeKind, addQuota, addMaxAmounts, members]);
34481
+ }, [section, activeOp, collectionId, adminAddress, deedDid, revokeTarget, addMode, bulkAddresses, addAddress, addRole, granteeKind, addQuota, addMaxAmounts, members]);
34253
34482
  useEffect115(() => {
34254
34483
  if (!registerRuntimeInputs) return;
34255
34484
  registerRuntimeInputs(buildRuntimeInputs());
@@ -34273,6 +34502,7 @@ var CollectionUsersFlowDetail = ({
34273
34502
  collectionId,
34274
34503
  granteeAddress: runtimeInputs.granteeAddress,
34275
34504
  granteeKind: runtimeInputs.granteeKind,
34505
+ granteeCount: runtimeInputs.granteeAddresses?.length,
34276
34506
  listener: isListener
34277
34507
  });
34278
34508
  try {
@@ -34287,6 +34517,7 @@ var CollectionUsersFlowDetail = ({
34287
34517
  console.log(`${LOG2} execute success`, { operation: runtimeInputs.operation, transactionHash: payload.transactionHash });
34288
34518
  if (runtimeInputs.operation === "add") {
34289
34519
  setAddAddress("");
34520
+ setBulkText("");
34290
34521
  setAddQuota(String(runtimeInputs.role === "evaluate" ? DEFAULT_EVALUATOR_QUOTA : DEFAULT_CONTRIBUTOR_QUOTA));
34291
34522
  setAddMaxAmounts(EMPTY_MAX_AMOUNTS);
34292
34523
  setGranteeKind("user");
@@ -34337,7 +34568,7 @@ var CollectionUsersFlowDetail = ({
34337
34568
  defaultValue: "Configure the {{did}} and {{claimCollection}} in template mode before running this action.",
34338
34569
  did: "DID",
34339
34570
  claimCollection: "claim collection"
34340
- })), isStale && /* @__PURE__ */ React320.createElement(DismissibleAlert, { icon: /* @__PURE__ */ React320.createElement(IconAlertCircle28, { size: 16 }), color: "yellow", title: "Inconsistent State" }, /* @__PURE__ */ React320.createElement(Stack211, { gap: "xs" }, /* @__PURE__ */ React320.createElement(Text199, { size: "sm" }, "Block was marked completed but no result was recorded. This is stale state from a previous run \u2014 reset and re-run."), /* @__PURE__ */ React320.createElement(Group116, null, /* @__PURE__ */ React320.createElement(Button67, { variant: "outline", size: "xs", onClick: handleReset }, "Reset"), /* @__PURE__ */ React320.createElement(Button67, { variant: "filled", size: "xs", onClick: handleForceRun, disabled: !validation.ok }, "Force Run Now")))), isFailed && /* @__PURE__ */ React320.createElement(DismissibleAlert, { icon: /* @__PURE__ */ React320.createElement(IconAlertCircle28, { size: 16 }), color: "red", title: "Action Failed", styles: actionAlertStyles }, /* @__PURE__ */ React320.createElement(Stack211, { gap: "xs" }, errorMessage && /* @__PURE__ */ React320.createElement(Text199, { size: "sm" }, errorMessage), /* @__PURE__ */ React320.createElement(Group116, null, /* @__PURE__ */ React320.createElement(Button67, { variant: "outline", size: "xs", onClick: handleReset }, "Reset"), !!collectionId && !!adminAddress && /* @__PURE__ */ React320.createElement(Button67, { variant: "filled", size: "xs", leftSection: /* @__PURE__ */ React320.createElement(IconRefresh9, { size: 14 }), onClick: handleManualRefreshList }, "Refresh List")))), isCompletedWithProof && hasTxProof && (lastOp === "add" || lastOp === "revoke") && /* @__PURE__ */ React320.createElement(DismissibleAlert, { icon: /* @__PURE__ */ React320.createElement(IconCheck24, { size: 16 }), color: "green", styles: actionAlertStyles }, lastOp === "add" ? "Grant broadcast" : "Revoke broadcast", " \u2014 tx ", truncateAddress4(String(output?.transactionHash || "")), "."), !configMissing && /* @__PURE__ */ React320.createElement(
34571
+ })), isStale && /* @__PURE__ */ React320.createElement(DismissibleAlert, { icon: /* @__PURE__ */ React320.createElement(IconAlertCircle28, { size: 16 }), color: "yellow", title: "Inconsistent State" }, /* @__PURE__ */ React320.createElement(Stack211, { gap: "xs" }, /* @__PURE__ */ React320.createElement(Text199, { size: "sm" }, "Block was marked completed but no result was recorded. This is stale state from a previous run \u2014 reset and re-run."), /* @__PURE__ */ React320.createElement(Group116, null, /* @__PURE__ */ React320.createElement(Button67, { variant: "outline", size: "xs", onClick: handleReset }, "Reset"), /* @__PURE__ */ React320.createElement(Button67, { variant: "filled", size: "xs", onClick: handleForceRun, disabled: !validation.ok }, "Force Run Now")))), isFailed && /* @__PURE__ */ React320.createElement(DismissibleAlert, { icon: /* @__PURE__ */ React320.createElement(IconAlertCircle28, { size: 16 }), color: "red", title: "Action Failed", styles: actionAlertStyles }, /* @__PURE__ */ React320.createElement(Stack211, { gap: "xs" }, errorMessage && /* @__PURE__ */ React320.createElement(Text199, { size: "sm" }, errorMessage), /* @__PURE__ */ React320.createElement(Group116, null, /* @__PURE__ */ React320.createElement(Button67, { variant: "outline", size: "xs", onClick: handleReset }, "Reset"), !!collectionId && !!adminAddress && /* @__PURE__ */ React320.createElement(Button67, { variant: "filled", size: "xs", leftSection: /* @__PURE__ */ React320.createElement(IconRefresh9, { size: 14 }), onClick: handleManualRefreshList }, "Refresh List")))), isCompletedWithProof && hasTxProof && (lastOp === "add" || lastOp === "revoke") && /* @__PURE__ */ React320.createElement(DismissibleAlert, { icon: /* @__PURE__ */ React320.createElement(IconCheck24, { size: 16 }), color: "green", styles: actionAlertStyles }, lastOp === "add" ? grantBroadcastSummary(output) : "Revoke broadcast", " \u2014 ", broadcastTxCount(output) > 1 ? "last tx" : "tx", " ", truncateAddress4(String(output?.transactionHash || "")), "."), !configMissing && /* @__PURE__ */ React320.createElement(
34341
34572
  SegmentedControl16,
34342
34573
  {
34343
34574
  fullWidth: true,
@@ -34379,7 +34610,30 @@ var CollectionUsersFlowDetail = ({
34379
34610
  "Revoke"
34380
34611
  ))
34381
34612
  );
34382
- }), revokeTarget && /* @__PURE__ */ React320.createElement(DismissibleAlert, { color: "red", styles: actionAlertStyles }, "Slide to sign to revoke ", roleLabel(revokeTarget.role), " from ", truncateAddress4(revokeTarget.address), ".")), !configMissing && section === "add" && /* @__PURE__ */ React320.createElement(Stack211, { gap: "sm" }, revokeTarget && /* @__PURE__ */ React320.createElement(DismissibleAlert, { color: "yellow", styles: actionAlertStyles }, "A revoke is currently armed. Switch to the Users tab to sign it, or change the grantee below to arm an add instead."), /* @__PURE__ */ React320.createElement(Stack211, { gap: 4 }, /* @__PURE__ */ React320.createElement(Text199, { size: "sm", fw: 500 }, "Grantee address"), /* @__PURE__ */ React320.createElement(
34613
+ }), revokeTarget && /* @__PURE__ */ React320.createElement(DismissibleAlert, { color: "red", styles: actionAlertStyles }, "Slide to sign to revoke ", roleLabel(revokeTarget.role), " from ", truncateAddress4(revokeTarget.address), ".")), !configMissing && section === "add" && /* @__PURE__ */ React320.createElement(Stack211, { gap: "sm" }, revokeTarget && /* @__PURE__ */ React320.createElement(DismissibleAlert, { color: "yellow", styles: actionAlertStyles }, "A revoke is currently armed. Switch to the Users tab to sign it, or change the grantee below to arm an add instead."), /* @__PURE__ */ React320.createElement(
34614
+ SegmentedControl16,
34615
+ {
34616
+ fullWidth: true,
34617
+ size: "xs",
34618
+ value: addMode,
34619
+ onChange: (value) => handleAddModeChange(value),
34620
+ disabled: isDisabled || isRunning,
34621
+ data: [
34622
+ { label: "One account", value: "single" },
34623
+ { label: "Paste a list", value: "list" }
34624
+ ]
34625
+ }
34626
+ ), addMode === "list" && /* @__PURE__ */ React320.createElement(
34627
+ BulkAccountsInput,
34628
+ {
34629
+ text: bulkText,
34630
+ onChange: setBulkText,
34631
+ parsed: parsedAccounts,
34632
+ isDisabled: isDisabled || isRunning,
34633
+ canBatchGrants,
34634
+ actingAsGroup: actAs.actAsGroup
34635
+ }
34636
+ ), addMode === "single" && /* @__PURE__ */ React320.createElement(Stack211, { gap: 4 }, /* @__PURE__ */ React320.createElement(Text199, { size: "sm", fw: 500 }, "Grantee address"), /* @__PURE__ */ React320.createElement(
34383
34637
  RecipientInput,
34384
34638
  {
34385
34639
  value: addAddress,
@@ -34388,10 +34642,11 @@ var CollectionUsersFlowDetail = ({
34388
34642
  setRevokeTarget(null);
34389
34643
  setAddAddress(address);
34390
34644
  },
34645
+ onBulkPaste: handleBulkPaste,
34391
34646
  isDisabled: isDisabled || isRunning,
34392
34647
  placeholder: "Search by name / @user:\u2026, or paste an ixo1\u2026 address"
34393
34648
  }
34394
- )), canClassify ? /* @__PURE__ */ React320.createElement(Button67, { size: "xs", variant: "light", onClick: runClassify, disabled: !addAddress.trim() || classifying || isRunning, loading: classifying }, "Check address") : /* @__PURE__ */ React320.createElement(Text199, { size: "xs", c: "dimmed" }, "Address classification is unavailable in this host \u2014 the grantee will be added as a plain account."), classifyError && /* @__PURE__ */ React320.createElement(DismissibleAlert, { color: "red", styles: actionAlertStyles }, classifyError), isGroup && /* @__PURE__ */ React320.createElement(Box60, { style: { border: "1px solid var(--mantine-color-neutralColor-6)", borderRadius: 8, padding: 12 } }, /* @__PURE__ */ React320.createElement(Stack211, { gap: "xs" }, /* @__PURE__ */ React320.createElement(Text199, { size: "sm", fw: 600 }, "Add the group, or all the members of the POD?"), /* @__PURE__ */ React320.createElement(Text199, { size: "xs", c: "dimmed" }, "Detected a DAO DAO group (", classification?.daodao?.type, "). Choose how to grant access."), /* @__PURE__ */ React320.createElement(Radio5.Group, { value: granteeKind, onChange: (value) => setGranteeKind(value) }, /* @__PURE__ */ React320.createElement(Stack211, { gap: "xs", mt: "xs" }, /* @__PURE__ */ React320.createElement(
34649
+ )), addMode === "single" && (canClassify ? /* @__PURE__ */ React320.createElement(Button67, { size: "xs", variant: "light", onClick: runClassify, disabled: !addAddress.trim() || classifying || isRunning, loading: classifying }, "Check address") : /* @__PURE__ */ React320.createElement(Text199, { size: "xs", c: "dimmed" }, "Address classification is unavailable in this host \u2014 the grantee will be added as a plain account.")), addMode === "single" && classifyError && /* @__PURE__ */ React320.createElement(DismissibleAlert, { color: "red", styles: actionAlertStyles }, classifyError), addMode === "single" && isGroup && /* @__PURE__ */ React320.createElement(Box60, { style: { border: "1px solid var(--mantine-color-neutralColor-6)", borderRadius: 8, padding: 12 } }, /* @__PURE__ */ React320.createElement(Stack211, { gap: "xs" }, /* @__PURE__ */ React320.createElement(Text199, { size: "sm", fw: 600 }, "Add the group, or all the members of the POD?"), /* @__PURE__ */ React320.createElement(Text199, { size: "xs", c: "dimmed" }, "Detected a DAO DAO group (", classification?.daodao?.type, "). Choose how to grant access."), /* @__PURE__ */ React320.createElement(Radio5.Group, { value: granteeKind, onChange: (value) => setGranteeKind(value) }, /* @__PURE__ */ React320.createElement(Stack211, { gap: "xs", mt: "xs" }, /* @__PURE__ */ React320.createElement(
34395
34650
  Radio5,
34396
34651
  {
34397
34652
  value: "group-account",
@@ -34405,7 +34660,7 @@ var CollectionUsersFlowDetail = ({
34405
34660
  disabled: isDisabled || isRunning,
34406
34661
  label: /* @__PURE__ */ React320.createElement(Stack211, { gap: 0 }, /* @__PURE__ */ React320.createElement(Text199, { size: "sm" }, "Add all members of the POD"), /* @__PURE__ */ React320.createElement(Text199, { size: "xs", c: "dimmed" }, canEnumerate ? `Fan out: grant each member individually${members.length ? ` (${members.length} resolved)` : ""}.` : "Member enumeration is unavailable in this host."))
34407
34662
  }
34408
- ))))), /* @__PURE__ */ React320.createElement(
34663
+ ))), granteeKind === "group-members" && !actAs.actAsGroup && memberSigningPlan && /* @__PURE__ */ React320.createElement(DismissibleAlert, { color: memberSigningPlan.color, styles: actionAlertStyles }, memberSigningPlan.message))), addMode === "list" && bulkAddresses.length > 0 && /* @__PURE__ */ React320.createElement(Text199, { size: "xs", c: "dimmed" }, "The role, quota and limits below apply to all ", bulkAddresses.length, " accounts."), /* @__PURE__ */ React320.createElement(
34409
34664
  BaseSelect,
34410
34665
  {
34411
34666
  label: "Role",
@@ -57934,4 +58189,4 @@ export {
57934
58189
  ixoGraphQLClient,
57935
58190
  getEntity
57936
58191
  };
57937
- //# sourceMappingURL=chunk-XYAKFAZ3.js.map
58192
+ //# sourceMappingURL=chunk-NYQBGIZ2.js.map