@ondrej-svec/hog 1.14.0 → 1.15.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/README.md CHANGED
@@ -88,7 +88,9 @@ Labels are abbreviated automatically: `size:M` → `[M]`, `priority:high` → `[
88
88
  | Key | Action |
89
89
  |-----|--------|
90
90
  | `p` | Pick up issue — assign to yourself + optional TickTick task |
91
- | `a` / `u` | Assign / unassign collaborator |
91
+ | `a` | Assign issue to yourself (no-op if already assigned to anyone) |
92
+ | `u` | Undo last reversible action |
93
+ | `e` | Edit issue in `$EDITOR` — change assignee, title, status, labels, body |
92
94
  | `m` | Change project status |
93
95
  | `l` | Add / remove labels |
94
96
  | `c` | Add comment |
package/dist/cli.js CHANGED
@@ -694,6 +694,18 @@ function fetchProjectFields(repo, issueNumber, projectNumber) {
694
694
  field { ... on ProjectV2SingleSelectField { name } }
695
695
  name
696
696
  }
697
+ ... on ProjectV2ItemFieldTextValue {
698
+ field { ... on ProjectV2Field { name } }
699
+ text
700
+ }
701
+ ... on ProjectV2ItemFieldNumberValue {
702
+ field { ... on ProjectV2Field { name } }
703
+ number
704
+ }
705
+ ... on ProjectV2ItemFieldIterationValue {
706
+ field { ... on ProjectV2IterationField { name } }
707
+ title
708
+ }
697
709
  }
698
710
  }
699
711
  }
@@ -724,11 +736,17 @@ function fetchProjectFields(repo, issueNumber, projectNumber) {
724
736
  const fieldValues = projectItem.fieldValues?.nodes ?? [];
725
737
  for (const fv of fieldValues) {
726
738
  if (!fv) continue;
727
- if ("date" in fv && DATE_FIELD_NAME_RE2.test(fv.field?.name ?? "")) {
739
+ const fieldName = fv.field?.name ?? "";
740
+ if ("date" in fv && DATE_FIELD_NAME_RE2.test(fieldName)) {
728
741
  fields.targetDate = fv.date;
729
- }
730
- if ("name" in fv && fv.field?.name === "Status") {
742
+ } else if ("name" in fv && fieldName === "Status") {
731
743
  fields.status = fv.name;
744
+ } else if (fieldName) {
745
+ const value = "text" in fv && fv.text != null ? fv.text : "number" in fv && fv.number != null ? String(fv.number) : "name" in fv && fv.name != null ? fv.name : "title" in fv && fv.title != null ? fv.title : null;
746
+ if (value != null) {
747
+ if (!fields.customFields) fields.customFields = {};
748
+ fields.customFields[fieldName] = value;
749
+ }
732
750
  }
733
751
  }
734
752
  return fields;
@@ -761,6 +779,18 @@ function fetchProjectEnrichment(repo, projectNumber) {
761
779
  field { ... on ProjectV2SingleSelectField { name } }
762
780
  name
763
781
  }
782
+ ... on ProjectV2ItemFieldTextValue {
783
+ field { ... on ProjectV2Field { name } }
784
+ text
785
+ }
786
+ ... on ProjectV2ItemFieldNumberValue {
787
+ field { ... on ProjectV2Field { name } }
788
+ number
789
+ }
790
+ ... on ProjectV2ItemFieldIterationValue {
791
+ field { ... on ProjectV2IterationField { name } }
792
+ title
793
+ }
764
794
  }
765
795
  }
766
796
  }
@@ -793,11 +823,17 @@ function fetchProjectEnrichment(repo, projectNumber) {
793
823
  const fieldValues = item.fieldValues?.nodes ?? [];
794
824
  for (const fv of fieldValues) {
795
825
  if (!fv) continue;
796
- if ("date" in fv && fv.date && DATE_FIELD_NAME_RE2.test(fv.field?.name ?? "")) {
826
+ const fieldName = fv.field?.name ?? "";
827
+ if ("date" in fv && fv.date && DATE_FIELD_NAME_RE2.test(fieldName)) {
797
828
  enrichment.targetDate = fv.date;
798
- }
799
- if ("name" in fv && fv.field?.name === "Status" && fv.name) {
829
+ } else if ("name" in fv && fieldName === "Status" && fv.name) {
800
830
  enrichment.projectStatus = fv.name;
831
+ } else if (fieldName) {
832
+ const value = "text" in fv && fv.text != null ? fv.text : "number" in fv && fv.number != null ? String(fv.number) : "name" in fv && fv.name != null ? fv.name : "title" in fv && fv.title != null ? fv.title : null;
833
+ if (value != null) {
834
+ if (!enrichment.customFields) enrichment.customFields = {};
835
+ enrichment.customFields[fieldName] = value;
836
+ }
801
837
  }
802
838
  }
803
839
  enrichMap.set(item.content.number, enrichment);
@@ -2273,7 +2309,7 @@ function useKeyboard({
2273
2309
  handleToggleLog
2274
2310
  ]
2275
2311
  );
2276
- const inputActive = ui.state.mode === "normal" || ui.state.mode === "multiSelect" || ui.state.mode === "focus";
2312
+ const inputActive = ui.state.mode === "normal" || ui.state.mode === "multiSelect" || ui.state.mode === "focus" || ui.state.mode === "overlay:detail";
2277
2313
  useInput(handleInput, { isActive: inputActive });
2278
2314
  const handleSearchEscape = useCallback4(
2279
2315
  (_input, key) => {
@@ -2983,6 +3019,7 @@ function formatCommentAge(createdAt) {
2983
3019
  function DetailPanel({
2984
3020
  issue,
2985
3021
  width,
3022
+ height,
2986
3023
  isActive,
2987
3024
  commentsState,
2988
3025
  fetchComments,
@@ -2994,9 +3031,9 @@ function DetailPanel({
2994
3031
  fetchComments(issueRepo, issue.number);
2995
3032
  }, [issue, issueRepo, fetchComments, commentsState]);
2996
3033
  if (!issue) {
2997
- return /* @__PURE__ */ jsx4(Panel, { title: "[0] Detail", isActive, width, children: /* @__PURE__ */ jsx4(Text4, { color: "gray", children: "No item selected" }) });
3034
+ return /* @__PURE__ */ jsx4(Panel, { title: "[0] Detail", isActive, width, height, children: /* @__PURE__ */ jsx4(Text4, { color: "gray", children: "No item selected" }) });
2998
3035
  }
2999
- return /* @__PURE__ */ jsxs4(Panel, { title: "[0] Detail", isActive, width, children: [
3036
+ return /* @__PURE__ */ jsxs4(Panel, { title: "[0] Detail", isActive, width, height, children: [
3000
3037
  /* @__PURE__ */ jsxs4(Text4, { color: "cyan", bold: true, children: [
3001
3038
  "#",
3002
3039
  issue.number,
@@ -4191,8 +4228,9 @@ var init_help_overlay = __esm({
4191
4228
  category: "Actions",
4192
4229
  items: [
4193
4230
  { key: "p", desc: "Pick issue (assign + TickTick)" },
4194
- { key: "a", desc: "Assign to self" },
4231
+ { key: "a", desc: "Assign to self (no-op if already assigned)" },
4195
4232
  { key: "u", desc: "Undo last reversible action" },
4233
+ { key: "e", desc: "Edit issue in $EDITOR (title, assignee, status, labels)" },
4196
4234
  { key: "c", desc: "Comment on issue" },
4197
4235
  { key: "m", desc: "Move status" },
4198
4236
  { key: "e", desc: "Edit issue in $EDITOR" },
@@ -4468,7 +4506,7 @@ function SearchBar({ defaultValue, onChange, onSubmit }) {
4468
4506
  TextInput5,
4469
4507
  {
4470
4508
  defaultValue,
4471
- placeholder: "search...",
4509
+ placeholder: "title, label, status, @user, #123, unassigned\u2026",
4472
4510
  onChange,
4473
4511
  onSubmit
4474
4512
  }
@@ -5314,6 +5352,31 @@ function RefreshAge({ lastRefresh }) {
5314
5352
  timeAgo(lastRefresh)
5315
5353
  ] });
5316
5354
  }
5355
+ function matchesSearch(issue, query) {
5356
+ if (!query.trim()) return true;
5357
+ const tokens = query.toLowerCase().trim().split(/\s+/);
5358
+ const labels = issue.labels ?? [];
5359
+ const assignees = issue.assignees ?? [];
5360
+ return tokens.every((token) => {
5361
+ if (token.startsWith("#")) {
5362
+ const num = parseInt(token.slice(1), 10);
5363
+ return !Number.isNaN(num) && issue.number === num;
5364
+ }
5365
+ if (token.startsWith("@")) {
5366
+ const login = token.slice(1);
5367
+ return assignees.some((a) => a.login.toLowerCase().includes(login));
5368
+ }
5369
+ if (token === "unassigned") return assignees.length === 0;
5370
+ if (token === "assigned") return assignees.length > 0;
5371
+ if (issue.title.toLowerCase().includes(token)) return true;
5372
+ if (labels.some((l) => l.name.toLowerCase().includes(token))) return true;
5373
+ if (issue.projectStatus?.toLowerCase().includes(token)) return true;
5374
+ if (issue.customFields && Object.values(issue.customFields).some((v) => v.toLowerCase().includes(token)))
5375
+ return true;
5376
+ if (assignees.some((a) => a.login.toLowerCase().includes(token))) return true;
5377
+ return false;
5378
+ });
5379
+ }
5317
5380
  function Dashboard({ config: config2, options, activeProfile }) {
5318
5381
  const { exit } = useApp();
5319
5382
  const refreshMs = config2.board.refreshInterval * 1e3;
@@ -5363,8 +5426,7 @@ function Dashboard({ config: config2, options, activeProfile }) {
5363
5426
  })).filter((rd) => rd.issues.length > 0);
5364
5427
  }
5365
5428
  if (!searchQuery) return filtered;
5366
- const q = searchQuery.toLowerCase();
5367
- return filtered.map((rd) => ({ ...rd, issues: rd.issues.filter((i) => i.title.toLowerCase().includes(q)) })).filter((rd) => rd.issues.length > 0);
5429
+ return filtered.map((rd) => ({ ...rd, issues: rd.issues.filter((i) => matchesSearch(i, searchQuery)) })).filter((rd) => rd.issues.length > 0);
5368
5430
  }, [allRepos, searchQuery, mineOnly, config2.board.assignee]);
5369
5431
  const boardTree = useMemo3(() => buildBoardTree(repos, allActivity), [repos, allActivity]);
5370
5432
  const [selectedRepoIdx, setSelectedRepoIdx] = useState16(0);
@@ -5966,7 +6028,8 @@ function Dashboard({ config: config2, options, activeProfile }) {
5966
6028
  DetailPanel,
5967
6029
  {
5968
6030
  issue: selectedItem.issue,
5969
- width: termSize.cols,
6031
+ width: usableWidth,
6032
+ height: issuesPanelHeight + ACTIVITY_HEIGHT,
5970
6033
  isActive: true,
5971
6034
  issueRepo: selectedItem.repoName,
5972
6035
  fetchComments: handleFetchComments,
@@ -6163,6 +6226,7 @@ async function fetchDashboard(config2, options = {}) {
6163
6226
  ...issue,
6164
6227
  ...e?.targetDate !== void 0 ? { targetDate: e.targetDate } : {},
6165
6228
  ...e?.projectStatus !== void 0 ? { projectStatus: e.projectStatus } : {},
6229
+ ...e?.customFields !== void 0 ? { customFields: e.customFields } : {},
6166
6230
  ...slackUrl ? { slackThreadUrl: slackUrl } : {}
6167
6231
  };
6168
6232
  });
@@ -7209,7 +7273,7 @@ function resolveProjectId(projectId) {
7209
7273
  process.exit(1);
7210
7274
  }
7211
7275
  var program = new Command();
7212
- program.name("hog").description("Personal command deck \u2014 unified task dashboard for GitHub Projects + TickTick").version("1.14.0").option("--json", "Force JSON output").option("--human", "Force human-readable output").hook("preAction", (thisCommand) => {
7276
+ program.name("hog").description("Personal command deck \u2014 unified task dashboard for GitHub Projects + TickTick").version("1.15.0").option("--json", "Force JSON output").option("--human", "Force human-readable output").hook("preAction", (thisCommand) => {
7213
7277
  const opts = thisCommand.opts();
7214
7278
  if (opts.json) setFormat("json");
7215
7279
  if (opts.human) setFormat("human");