clairo 2.2.0 → 2.2.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.
Files changed (2) hide show
  1. package/dist/cli.js +88 -54
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1146,25 +1146,45 @@ async function getBoardIssues(auth, boardId, opts) {
1146
1146
  function escapeJql(text) {
1147
1147
  return text.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
1148
1148
  }
1149
- function appendTextSearch(jql, searchText) {
1149
+ function createSearchClause(searchText) {
1150
1150
  const escaped = escapeJql(searchText);
1151
- return `(${jql}) AND text ~ "${escaped}"`;
1151
+ if (/^[A-Z]+-\d+$/i.test(searchText)) {
1152
+ return `(key = "${escaped}" OR text ~ "${escaped}")`;
1153
+ }
1154
+ if (/^\d+$/.test(searchText)) {
1155
+ return `(key ~ "*-${escaped}" OR text ~ "${escaped}")`;
1156
+ }
1157
+ return `text ~ "${escaped}"`;
1158
+ }
1159
+ function appendTextSearch(jql, searchText) {
1160
+ return `(${jql}) AND ${createSearchClause(searchText)}`;
1161
+ }
1162
+ function createAssigneeClause(filter) {
1163
+ if (filter === "unassigned") return "assignee is EMPTY";
1164
+ return "assignee = currentUser()";
1152
1165
  }
1153
1166
  async function fetchViewIssues(auth, view, opts) {
1154
- const { searchText, ...pageOpts } = opts ?? {};
1167
+ const { searchText, assigneeFilter, ...pageOpts } = opts ?? {};
1155
1168
  switch (view.source.type) {
1156
1169
  case "jql": {
1157
- const jql = searchText ? appendTextSearch(view.source.jql, searchText) : view.source.jql;
1170
+ let jql = view.source.jql;
1171
+ if (searchText) jql = appendTextSearch(jql, searchText);
1172
+ if (assigneeFilter) jql = `(${jql}) AND ${createAssigneeClause(assigneeFilter)}`;
1158
1173
  return searchIssues(auth, jql, pageOpts);
1159
1174
  }
1160
1175
  case "filter": {
1161
1176
  const filterResult = await getFilterJql(auth, view.source.filterId);
1162
1177
  if (!filterResult.success) return filterResult;
1163
- const jql = searchText ? appendTextSearch(filterResult.data, searchText) : filterResult.data;
1178
+ let jql = filterResult.data;
1179
+ if (searchText) jql = appendTextSearch(jql, searchText);
1180
+ if (assigneeFilter) jql = `(${jql}) AND ${createAssigneeClause(assigneeFilter)}`;
1164
1181
  return searchIssues(auth, jql, pageOpts);
1165
1182
  }
1166
1183
  case "board": {
1167
- const jql = searchText ? `text ~ "${escapeJql(searchText)}"` : void 0;
1184
+ const clauses = [];
1185
+ if (searchText) clauses.push(createSearchClause(searchText));
1186
+ if (assigneeFilter) clauses.push(createAssigneeClause(assigneeFilter));
1187
+ const jql = clauses.length > 0 ? clauses.join(" AND ") : void 0;
1168
1188
  return getBoardIssues(auth, view.source.boardId, { ...pageOpts, jql });
1169
1189
  }
1170
1190
  }
@@ -3783,6 +3803,7 @@ function JiraIssueDetailView({
3783
3803
  auth,
3784
3804
  myAccountId,
3785
3805
  myDisplayName,
3806
+ onFetchCurrentUser,
3786
3807
  isActive,
3787
3808
  onClose,
3788
3809
  onIssueUpdated,
@@ -3844,16 +3865,27 @@ function JiraIssueDetailView({
3844
3865
  setActionLoading(null);
3845
3866
  };
3846
3867
  const handleAssignToMe = async () => {
3847
- if (!myAccountId || !myDisplayName) return;
3848
3868
  setActionLoading("Assigning...");
3849
3869
  setActionError(null);
3850
- const result = await assignIssue(auth, issueKey, myAccountId);
3870
+ let accountId = myAccountId;
3871
+ let displayName = myDisplayName;
3872
+ if (!accountId || !displayName) {
3873
+ const user = await onFetchCurrentUser();
3874
+ if (!user) {
3875
+ setActionError("Failed to get current user");
3876
+ setActionLoading(null);
3877
+ return;
3878
+ }
3879
+ accountId = user.accountId;
3880
+ displayName = user.displayName;
3881
+ }
3882
+ const result = await assignIssue(auth, issueKey, accountId);
3851
3883
  if (result.success) {
3852
- const assignee = { accountId: myAccountId, displayName: myDisplayName };
3884
+ const assignee = { accountId, displayName };
3853
3885
  setDetail((prev) => prev ? { ...prev, fields: { ...prev.fields, assignee } } : prev);
3854
3886
  onIssueUpdated(issueKey, { assignee });
3855
- duckEvents.emit("jira:assigned", { ticketKey: issueKey, assignee: myDisplayName });
3856
- logJiraAssigneeChanged(issueKey, issueSummary, "assigned", myDisplayName);
3887
+ duckEvents.emit("jira:assigned", { ticketKey: issueKey, assignee: displayName });
3888
+ logJiraAssigneeChanged(issueKey, issueSummary, "assigned", displayName);
3857
3889
  onLogUpdated == null ? void 0 : onLogUpdated();
3858
3890
  } else {
3859
3891
  setActionError(result.error);
@@ -3906,7 +3938,7 @@ function JiraIssueDetailView({
3906
3938
  if (input === "s" && !actionLoading) {
3907
3939
  openTransitionPicker();
3908
3940
  }
3909
- if (input === "a" && !actionLoading && myAccountId) {
3941
+ if (input === "a" && !actionLoading) {
3910
3942
  handleAssignToMe();
3911
3943
  }
3912
3944
  if (input === "A" && !actionLoading) {
@@ -4054,6 +4086,7 @@ function JiraSavedViewBrowserBox({
4054
4086
  auth,
4055
4087
  myAccountId,
4056
4088
  myDisplayName,
4089
+ onFetchCurrentUser,
4057
4090
  isActive,
4058
4091
  onInputModeChange,
4059
4092
  onLogUpdated
@@ -4074,22 +4107,10 @@ function JiraSavedViewBrowserBox({
4074
4107
  const title = "[6] Issues";
4075
4108
  const borderColor = isActive ? "yellow" : void 0;
4076
4109
  const displayTitle = view ? `${title} - ${view.name}` : title;
4077
- const filteredIssues = useMemo2(() => {
4078
- if (assigneeFilter === "unassigned") {
4079
- return issues.filter((issue) => !issue.fields.assignee);
4080
- }
4081
- if (assigneeFilter === "me" && myAccountId) {
4082
- return issues.filter((issue) => {
4083
- var _a;
4084
- return ((_a = issue.fields.assignee) == null ? void 0 : _a.accountId) === myAccountId;
4085
- });
4086
- }
4087
- return issues;
4088
- }, [issues, assigneeFilter, myAccountId]);
4089
4110
  const rows = useMemo2(() => {
4090
- const groups = groupBySprint(filteredIssues);
4111
+ const groups = groupBySprint(issues);
4091
4112
  return buildRows(groups);
4092
- }, [filteredIssues]);
4113
+ }, [issues]);
4093
4114
  const navigableIndices = useMemo2(
4094
4115
  () => rows.map((r, i) => r.type === "issue" ? i : -1).filter((i) => i >= 0),
4095
4116
  [rows]
@@ -4104,14 +4125,19 @@ function JiraSavedViewBrowserBox({
4104
4125
  }, [rows, navigableIndices, highlightedIndex]);
4105
4126
  const hasMore = issues.length < total;
4106
4127
  const doFetch = useCallback9(
4107
- async (search, startAt = 0, append = false) => {
4128
+ async (search, filter, startAt = 0, append = false) => {
4108
4129
  if (!view || !auth) return;
4109
4130
  setLoading(true);
4110
4131
  setError(null);
4132
+ if (!append) {
4133
+ setIssues([]);
4134
+ setTotal(0);
4135
+ }
4111
4136
  const result = await fetchViewIssues(auth, view, {
4112
4137
  startAt,
4113
4138
  maxResults: 50,
4114
- searchText: search || void 0
4139
+ searchText: search || void 0,
4140
+ assigneeFilter: filter === "all" ? void 0 : filter
4115
4141
  });
4116
4142
  if (result.success) {
4117
4143
  setIssues((prev) => append ? [...prev, ...result.data.issues] : result.data.issues);
@@ -4132,7 +4158,8 @@ function JiraSavedViewBrowserBox({
4132
4158
  if (view && auth) {
4133
4159
  setSearchText("");
4134
4160
  setInputText("");
4135
- doFetch("");
4161
+ setAssigneeFilter("all");
4162
+ doFetch("", "all");
4136
4163
  } else {
4137
4164
  setIssues([]);
4138
4165
  setTotal(0);
@@ -4176,7 +4203,7 @@ function JiraSavedViewBrowserBox({
4176
4203
  const newSearch = inputText.trim();
4177
4204
  if (newSearch !== searchText) {
4178
4205
  setSearchText(newSearch);
4179
- doFetch(newSearch);
4206
+ doFetch(newSearch, assigneeFilter);
4180
4207
  }
4181
4208
  return;
4182
4209
  }
@@ -4219,31 +4246,33 @@ function JiraSavedViewBrowserBox({
4219
4246
  return;
4220
4247
  }
4221
4248
  if (input === "u") {
4222
- setAssigneeFilter((f) => f === "unassigned" ? "all" : "unassigned");
4249
+ const newFilter = assigneeFilter === "unassigned" ? "all" : "unassigned";
4250
+ setAssigneeFilter(newFilter);
4251
+ doFetch(searchText, newFilter);
4223
4252
  setHighlightedIndex(0);
4224
4253
  return;
4225
4254
  }
4226
4255
  if (input === "m") {
4227
- setAssigneeFilter((f) => f === "me" ? "all" : "me");
4256
+ const newFilter = assigneeFilter === "me" ? "all" : "me";
4257
+ setAssigneeFilter(newFilter);
4258
+ doFetch(searchText, newFilter);
4228
4259
  setHighlightedIndex(0);
4229
4260
  return;
4230
4261
  }
4231
4262
  if (input === "x") {
4232
4263
  setAssigneeFilter("all");
4233
- if (searchText) {
4234
- setSearchText("");
4235
- setInputText("");
4236
- doFetch("");
4237
- }
4264
+ setSearchText("");
4265
+ setInputText("");
4266
+ doFetch("", "all");
4238
4267
  setHighlightedIndex(0);
4239
4268
  return;
4240
4269
  }
4241
4270
  if (input === "l" && hasMore) {
4242
- doFetch(searchText, issues.length, true);
4271
+ doFetch(searchText, assigneeFilter, issues.length, true);
4243
4272
  return;
4244
4273
  }
4245
4274
  if (input === "r") {
4246
- doFetch(searchText);
4275
+ doFetch(searchText, assigneeFilter);
4247
4276
  }
4248
4277
  },
4249
4278
  { isActive }
@@ -4260,6 +4289,7 @@ function JiraSavedViewBrowserBox({
4260
4289
  auth,
4261
4290
  myAccountId,
4262
4291
  myDisplayName,
4292
+ onFetchCurrentUser,
4263
4293
  isActive,
4264
4294
  onClose: () => setDetailIssue(null),
4265
4295
  onIssueUpdated: handleIssueUpdated,
@@ -4276,7 +4306,7 @@ function JiraSavedViewBrowserBox({
4276
4306
  /* @__PURE__ */ jsxs10(Text11, { dimColor: true, children: [
4277
4307
  " ",
4278
4308
  "(",
4279
- filteredIssues.length,
4309
+ issues.length,
4280
4310
  "/",
4281
4311
  total,
4282
4312
  ")"
@@ -4290,8 +4320,7 @@ function JiraSavedViewBrowserBox({
4290
4320
  " Loading issues..."
4291
4321
  ] }) }),
4292
4322
  view && error && /* @__PURE__ */ jsx12(Box10, { paddingX: 1, children: /* @__PURE__ */ jsx12(Text11, { color: "red", children: error }) }),
4293
- view && !loading && !error && issues.length === 0 && /* @__PURE__ */ jsx12(Box10, { paddingX: 1, children: /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: searchText ? "No issues match search" : "No issues found" }) }),
4294
- view && !loading && !error && filteredIssues.length === 0 && issues.length > 0 && /* @__PURE__ */ jsx12(Box10, { paddingX: 1, children: /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: "No issues match filter" }) }),
4323
+ view && !loading && !error && issues.length === 0 && /* @__PURE__ */ jsx12(Box10, { paddingX: 1, children: /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: hasActiveFilters ? "No issues match filter" : "No issues found" }) }),
4295
4324
  rows.length > 0 && /* @__PURE__ */ jsx12(ScrollView6, { ref: scrollRef, children: rows.map((row, rowIdx) => {
4296
4325
  if (row.type === "header") {
4297
4326
  const stateLabel = row.state === "active" ? " (active)" : "";
@@ -4456,6 +4485,7 @@ function JiraBrowserView({
4456
4485
  const [myDisplayName, setMyDisplayName] = useState17(null);
4457
4486
  const [inputModeActive, setInputModeActive] = useState17(false);
4458
4487
  const lastRepoRef = useRef7(null);
4488
+ const fetchingUserRef = useRef7(false);
4459
4489
  const auth = useMemo3(() => {
4460
4490
  if (!repo.repoPath || !isJiraConfigured(repo.repoPath)) return null;
4461
4491
  const siteUrl = getJiraSiteUrl(repo.repoPath);
@@ -4473,18 +4503,21 @@ function JiraBrowserView({
4473
4503
  setSelectedViewId(loaded[0].id);
4474
4504
  }
4475
4505
  }, [repo.repoPath]);
4476
- useEffect13(() => {
4477
- if (!auth) {
4478
- setMyAccountId(null);
4479
- return;
4506
+ const fetchCurrentUser = useCallback10(async () => {
4507
+ if (myAccountId && myDisplayName) {
4508
+ return { accountId: myAccountId, displayName: myDisplayName };
4480
4509
  }
4481
- getCurrentUser(auth).then((result) => {
4482
- if (result.success) {
4483
- setMyAccountId(result.data.accountId);
4484
- setMyDisplayName(result.data.displayName);
4485
- }
4486
- });
4487
- }, [auth == null ? void 0 : auth.siteUrl, auth == null ? void 0 : auth.email]);
4510
+ if (!auth || fetchingUserRef.current) return null;
4511
+ fetchingUserRef.current = true;
4512
+ const result = await getCurrentUser(auth);
4513
+ fetchingUserRef.current = false;
4514
+ if (result.success) {
4515
+ setMyAccountId(result.data.accountId);
4516
+ setMyDisplayName(result.data.displayName);
4517
+ return { accountId: result.data.accountId, displayName: result.data.displayName };
4518
+ }
4519
+ return null;
4520
+ }, [auth, myAccountId, myDisplayName]);
4488
4521
  useEffect13(() => {
4489
4522
  onModalChange == null ? void 0 : onModalChange(modal.isOpen || inputModeActive);
4490
4523
  }, [modal.isOpen, inputModeActive, onModalChange]);
@@ -4547,7 +4580,7 @@ function JiraBrowserView({
4547
4580
  if (input === "5") onFocusedBoxChange("saved-views");
4548
4581
  if (input === "6") onFocusedBoxChange("browser");
4549
4582
  },
4550
- { isActive: isActive && !modal.isOpen }
4583
+ { isActive: isActive && !modal.isOpen && !inputModeActive }
4551
4584
  );
4552
4585
  if (modal.type === "add") {
4553
4586
  return /* @__PURE__ */ jsx14(Box12, { flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx14(
@@ -4586,6 +4619,7 @@ function JiraBrowserView({
4586
4619
  auth,
4587
4620
  myAccountId,
4588
4621
  myDisplayName,
4622
+ onFetchCurrentUser: fetchCurrentUser,
4589
4623
  isActive: isActive && focusedBox === "browser",
4590
4624
  onInputModeChange: setInputModeActive,
4591
4625
  onLogUpdated
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clairo",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",