github-mobile-reader 0.1.1 → 0.1.3

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/index.js CHANGED
@@ -21,12 +21,23 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  Priority: () => Priority,
24
+ extractChangedSymbols: () => extractChangedSymbols,
25
+ extractClassName: () => extractClassName,
26
+ extractJSXComponentName: () => extractJSXComponentName,
24
27
  filterDiffLines: () => filterDiffLines,
25
28
  generateReaderMarkdown: () => generateReaderMarkdown,
29
+ hasJSXContent: () => hasJSXContent,
30
+ isClassNameOnlyLine: () => isClassNameOnlyLine,
31
+ isJSXElement: () => isJSXElement,
32
+ isJSXFile: () => isJSXFile,
26
33
  normalizeCode: () => normalizeCode,
34
+ parseClassNameChanges: () => parseClassNameChanges,
27
35
  parseDiffToLogicalFlow: () => parseDiffToLogicalFlow,
36
+ parseJSXToFlowTree: () => parseJSXToFlowTree,
28
37
  parseToFlowTree: () => parseToFlowTree,
29
- renderFlowTree: () => renderFlowTree
38
+ renderFlowTree: () => renderFlowTree,
39
+ renderJSXTreeCompact: () => renderJSXTreeCompact,
40
+ renderStyleChanges: () => renderStyleChanges
30
41
  });
31
42
  module.exports = __toCommonJS(index_exports);
32
43
 
@@ -39,6 +50,132 @@ var Priority = /* @__PURE__ */ ((Priority2) => {
39
50
  Priority2[Priority2["OTHER"] = 5] = "OTHER";
40
51
  return Priority2;
41
52
  })(Priority || {});
53
+ function isJSXFile(filename) {
54
+ return /\.(jsx|tsx)$/.test(filename);
55
+ }
56
+ function hasJSXContent(lines) {
57
+ return lines.some((l) => /<[A-Z][A-Za-z]*[\s/>]/.test(l) || /return\s*\(/.test(l));
58
+ }
59
+ function isClassNameOnlyLine(line) {
60
+ return /^className=/.test(line.trim());
61
+ }
62
+ function extractClassName(line) {
63
+ const staticMatch = line.match(/className="([^"]*)"/);
64
+ if (staticMatch) return staticMatch[1];
65
+ const ternaryMatch = line.match(/className=\{[^?]+\?\s*"([^"]*)"\s*:\s*"([^"]*)"\}/);
66
+ if (ternaryMatch) return `${ternaryMatch[1]} ${ternaryMatch[2]}`;
67
+ const templateMatch = line.match(/className=\{`([^`]*)`\}/);
68
+ if (templateMatch) {
69
+ const raw = templateMatch[1];
70
+ const literals = raw.replace(/\$\{[^}]*\}/g, " ").trim();
71
+ const exprStrings = [...raw.matchAll(/"([^"]*)"/g)].map((m) => m[1]);
72
+ return [literals, ...exprStrings].filter(Boolean).join(" ");
73
+ }
74
+ return null;
75
+ }
76
+ function extractComponentFromLine(line) {
77
+ const tagMatch = line.match(/<([A-Za-z][A-Za-z0-9.]*)/);
78
+ if (tagMatch) return tagMatch[1];
79
+ return "unknown";
80
+ }
81
+ function parseClassNameChanges(addedLines, removedLines) {
82
+ const componentMap = /* @__PURE__ */ new Map();
83
+ for (const line of addedLines.filter((l) => /className=/.test(l))) {
84
+ const cls = extractClassName(line);
85
+ const comp = extractComponentFromLine(line);
86
+ if (!cls) continue;
87
+ if (!componentMap.has(comp)) componentMap.set(comp, { added: /* @__PURE__ */ new Set(), removed: /* @__PURE__ */ new Set() });
88
+ cls.split(/\s+/).filter(Boolean).forEach((c) => componentMap.get(comp).added.add(c));
89
+ }
90
+ for (const line of removedLines.filter((l) => /className=/.test(l))) {
91
+ const cls = extractClassName(line);
92
+ const comp = extractComponentFromLine(line);
93
+ if (!cls) continue;
94
+ if (!componentMap.has(comp)) componentMap.set(comp, { added: /* @__PURE__ */ new Set(), removed: /* @__PURE__ */ new Set() });
95
+ cls.split(/\s+/).filter(Boolean).forEach((c) => componentMap.get(comp).removed.add(c));
96
+ }
97
+ const changes = [];
98
+ for (const [comp, { added, removed }] of componentMap) {
99
+ if (comp === "unknown") continue;
100
+ const pureAdded = [...added].filter((c) => !removed.has(c));
101
+ const pureRemoved = [...removed].filter((c) => !added.has(c));
102
+ if (pureAdded.length === 0 && pureRemoved.length === 0) continue;
103
+ changes.push({ component: comp, added: pureAdded, removed: pureRemoved });
104
+ }
105
+ return changes;
106
+ }
107
+ function renderStyleChanges(changes) {
108
+ const lines = [];
109
+ for (const change of changes) {
110
+ lines.push(`**${change.component}**`);
111
+ if (change.added.length > 0) lines.push(` + ${change.added.join(" ")}`);
112
+ if (change.removed.length > 0) lines.push(` - ${change.removed.join(" ")}`);
113
+ }
114
+ return lines;
115
+ }
116
+ function isJSXElement(line) {
117
+ const t = line.trim();
118
+ return /^<[A-Za-z]/.test(t) || /^<\/[A-Za-z]/.test(t);
119
+ }
120
+ function isJSXClosing(line) {
121
+ return /^<\/[A-Za-z]/.test(line.trim());
122
+ }
123
+ function isJSXSelfClosing(line) {
124
+ return /\/>[\s]*$/.test(line.trim());
125
+ }
126
+ function extractJSXComponentName(line) {
127
+ const trimmed = line.trim();
128
+ const closingMatch = trimmed.match(/^<\/([A-Za-z][A-Za-z0-9.]*)/);
129
+ if (closingMatch) return `/${closingMatch[1]}`;
130
+ const nameMatch = trimmed.match(/^<([A-Za-z][A-Za-z0-9.]*)/);
131
+ if (!nameMatch) return trimmed;
132
+ const name = nameMatch[1];
133
+ const eventProps = [];
134
+ for (const m of trimmed.matchAll(/\b(on[A-Z]\w+)=/g)) {
135
+ eventProps.push(m[1]);
136
+ }
137
+ return eventProps.length > 0 ? `${name}(${eventProps.join(", ")})` : name;
138
+ }
139
+ function shouldIgnoreJSX(line) {
140
+ const t = line.trim();
141
+ return isClassNameOnlyLine(t) || /^style=/.test(t) || /^aria-/.test(t) || /^data-/.test(t) || /^strokeLinecap=/.test(t) || /^strokeLinejoin=/.test(t) || /^strokeWidth=/.test(t) || /^viewBox=/.test(t) || /^fill=/.test(t) || /^stroke=/.test(t) || /^d="/.test(t) || t === "{" || t === "}" || t === "(" || t === ")" || t === "<>" || t === "</>" || /^\{\/\*/.test(t);
142
+ }
143
+ function parseJSXToFlowTree(lines) {
144
+ const roots = [];
145
+ const stack = [];
146
+ for (const line of lines) {
147
+ if (!isJSXElement(line)) continue;
148
+ if (shouldIgnoreJSX(line)) continue;
149
+ const depth = getIndentDepth(line);
150
+ if (isJSXClosing(line)) {
151
+ while (stack.length > 0 && stack[stack.length - 1].depth >= depth) {
152
+ stack.pop();
153
+ }
154
+ continue;
155
+ }
156
+ const name = extractJSXComponentName(line);
157
+ const selfClosing = isJSXSelfClosing(line);
158
+ const node = {
159
+ type: "call",
160
+ name,
161
+ children: [],
162
+ depth,
163
+ priority: 5 /* OTHER */
164
+ };
165
+ while (stack.length > 0 && stack[stack.length - 1].depth >= depth) {
166
+ stack.pop();
167
+ }
168
+ if (stack.length === 0) {
169
+ roots.push(node);
170
+ } else {
171
+ stack[stack.length - 1].node.children.push(node);
172
+ }
173
+ if (!selfClosing) {
174
+ stack.push({ node, depth });
175
+ }
176
+ }
177
+ return roots;
178
+ }
42
179
  function filterDiffLines(diffText) {
43
180
  const lines = diffText.split("\n");
44
181
  const added = lines.filter((l) => l.startsWith("+") && !l.startsWith("+++") && l.trim() !== "+").map((l) => l.substring(1));
@@ -222,8 +359,59 @@ function parseDiffToLogicalFlow(diffText) {
222
359
  removedCode: removed.join("\n")
223
360
  };
224
361
  }
362
+ function extractChangedSymbols(addedLines, removedLines) {
363
+ const FUNC_RE = /^(?:export\s+)?(?:async\s+)?function\s+([a-z]\w+)|^(?:export\s+)?(?:const|let|var)\s+([a-z]\w+)\s*=\s*(?:async\s*)?\(/;
364
+ const COMPONENT_RE = /^(?:export\s+)?(?:default\s+)?(?:function|const)\s+([A-Z][a-z][A-Za-z0-9]*)/;
365
+ const extract = (lines) => {
366
+ const names = /* @__PURE__ */ new Set();
367
+ for (const line of lines) {
368
+ const cm = line.match(COMPONENT_RE) || line.match(FUNC_RE);
369
+ if (cm) {
370
+ const name = cm[1] || cm[2];
371
+ if (name) names.add(name);
372
+ }
373
+ }
374
+ return names;
375
+ };
376
+ const addedNames = extract(addedLines);
377
+ const removedNames = extract(removedLines);
378
+ const results = [];
379
+ const seen = /* @__PURE__ */ new Set();
380
+ for (const name of addedNames) {
381
+ seen.add(name);
382
+ results.push({ name, status: removedNames.has(name) ? "modified" : "added" });
383
+ }
384
+ for (const name of removedNames) {
385
+ if (!seen.has(name)) {
386
+ results.push({ name, status: "removed" });
387
+ }
388
+ }
389
+ return results;
390
+ }
391
+ function renderJSXTreeCompact(nodes, maxDepth = 3) {
392
+ const lines = [];
393
+ function walk(node, depth) {
394
+ if (depth > maxDepth) return;
395
+ const indent = " ".repeat(depth);
396
+ const hasChildren = node.children.length > 0;
397
+ lines.push(`${indent}${node.name}${hasChildren ? "" : ""}`);
398
+ for (const child of node.children) {
399
+ walk(child, depth + 1);
400
+ }
401
+ }
402
+ for (const root of nodes) {
403
+ walk(root, 0);
404
+ }
405
+ return lines.join("\n");
406
+ }
225
407
  function generateReaderMarkdown(diffText, meta = {}) {
226
- const result = parseDiffToLogicalFlow(diffText);
408
+ const { added, removed } = filterDiffLines(diffText);
409
+ const isJSX = Boolean(
410
+ meta.file && isJSXFile(meta.file) || hasJSXContent(added)
411
+ );
412
+ const changedSymbols = extractChangedSymbols(added, removed);
413
+ const classNameChanges = isJSX ? parseClassNameChanges(added, removed) : [];
414
+ const jsxTree = isJSX ? parseJSXToFlowTree(added) : [];
227
415
  const sections = [];
228
416
  sections.push("# \u{1F4D6} GitHub Reader View\n");
229
417
  sections.push("> Generated by **github-mobile-reader**");
@@ -232,35 +420,47 @@ function generateReaderMarkdown(diffText, meta = {}) {
232
420
  if (meta.commit) sections.push(`> Commit: \`${meta.commit}\``);
233
421
  if (meta.file) sections.push(`> File: \`${meta.file}\``);
234
422
  sections.push("\n");
235
- if (result.root.length > 0) {
236
- sections.push("## \u{1F9E0} Logical Flow\n");
237
- sections.push("```");
238
- sections.push(...renderFlowTree(result.root));
239
- sections.push("```\n");
423
+ if (changedSymbols.length > 0) {
424
+ sections.push("### \uBCC0\uACBD\uB41C \uD568\uC218 / \uCEF4\uD3EC\uB10C\uD2B8\n");
425
+ const STATUS_ICON = { added: "\u2705", removed: "\u274C", modified: "\u270F\uFE0F" };
426
+ for (const { name, status } of changedSymbols) {
427
+ sections.push(`- ${STATUS_ICON[status]} \`${name}()\` \u2014 ${status}`);
428
+ }
429
+ sections.push("");
240
430
  }
241
- if (result.rawCode.trim()) {
242
- sections.push("## \u2705 Added Code\n");
243
- sections.push("```typescript");
244
- sections.push(result.rawCode);
431
+ if (isJSX && jsxTree.length > 0) {
432
+ sections.push("### \u{1F3A8} JSX Structure\n");
433
+ sections.push("```");
434
+ sections.push(renderJSXTreeCompact(jsxTree));
245
435
  sections.push("```\n");
246
436
  }
247
- if (result.removedCode.trim()) {
248
- sections.push("## \u274C Removed Code\n");
249
- sections.push("```typescript");
250
- sections.push(result.removedCode);
251
- sections.push("```\n");
437
+ if (isJSX && classNameChanges.length > 0) {
438
+ sections.push("### \u{1F485} Style Changes\n");
439
+ sections.push(...renderStyleChanges(classNameChanges));
440
+ sections.push("");
252
441
  }
253
442
  sections.push("---");
254
- sections.push("\u{1F6E0} Auto-generated by [github-mobile-reader](https://github.com/your-org/github-mobile-reader). Do not edit manually.");
443
+ sections.push("\u{1F6E0} Auto-generated by [github-mobile-reader](https://github.com/3rdflr/github-mobile-reader). Do not edit manually.");
255
444
  return sections.join("\n");
256
445
  }
257
446
  // Annotate the CommonJS export names for ESM import in node:
258
447
  0 && (module.exports = {
259
448
  Priority,
449
+ extractChangedSymbols,
450
+ extractClassName,
451
+ extractJSXComponentName,
260
452
  filterDiffLines,
261
453
  generateReaderMarkdown,
454
+ hasJSXContent,
455
+ isClassNameOnlyLine,
456
+ isJSXElement,
457
+ isJSXFile,
262
458
  normalizeCode,
459
+ parseClassNameChanges,
263
460
  parseDiffToLogicalFlow,
461
+ parseJSXToFlowTree,
264
462
  parseToFlowTree,
265
- renderFlowTree
463
+ renderFlowTree,
464
+ renderJSXTreeCompact,
465
+ renderStyleChanges
266
466
  });
package/dist/index.mjs CHANGED
@@ -7,6 +7,132 @@ var Priority = /* @__PURE__ */ ((Priority2) => {
7
7
  Priority2[Priority2["OTHER"] = 5] = "OTHER";
8
8
  return Priority2;
9
9
  })(Priority || {});
10
+ function isJSXFile(filename) {
11
+ return /\.(jsx|tsx)$/.test(filename);
12
+ }
13
+ function hasJSXContent(lines) {
14
+ return lines.some((l) => /<[A-Z][A-Za-z]*[\s/>]/.test(l) || /return\s*\(/.test(l));
15
+ }
16
+ function isClassNameOnlyLine(line) {
17
+ return /^className=/.test(line.trim());
18
+ }
19
+ function extractClassName(line) {
20
+ const staticMatch = line.match(/className="([^"]*)"/);
21
+ if (staticMatch) return staticMatch[1];
22
+ const ternaryMatch = line.match(/className=\{[^?]+\?\s*"([^"]*)"\s*:\s*"([^"]*)"\}/);
23
+ if (ternaryMatch) return `${ternaryMatch[1]} ${ternaryMatch[2]}`;
24
+ const templateMatch = line.match(/className=\{`([^`]*)`\}/);
25
+ if (templateMatch) {
26
+ const raw = templateMatch[1];
27
+ const literals = raw.replace(/\$\{[^}]*\}/g, " ").trim();
28
+ const exprStrings = [...raw.matchAll(/"([^"]*)"/g)].map((m) => m[1]);
29
+ return [literals, ...exprStrings].filter(Boolean).join(" ");
30
+ }
31
+ return null;
32
+ }
33
+ function extractComponentFromLine(line) {
34
+ const tagMatch = line.match(/<([A-Za-z][A-Za-z0-9.]*)/);
35
+ if (tagMatch) return tagMatch[1];
36
+ return "unknown";
37
+ }
38
+ function parseClassNameChanges(addedLines, removedLines) {
39
+ const componentMap = /* @__PURE__ */ new Map();
40
+ for (const line of addedLines.filter((l) => /className=/.test(l))) {
41
+ const cls = extractClassName(line);
42
+ const comp = extractComponentFromLine(line);
43
+ if (!cls) continue;
44
+ if (!componentMap.has(comp)) componentMap.set(comp, { added: /* @__PURE__ */ new Set(), removed: /* @__PURE__ */ new Set() });
45
+ cls.split(/\s+/).filter(Boolean).forEach((c) => componentMap.get(comp).added.add(c));
46
+ }
47
+ for (const line of removedLines.filter((l) => /className=/.test(l))) {
48
+ const cls = extractClassName(line);
49
+ const comp = extractComponentFromLine(line);
50
+ if (!cls) continue;
51
+ if (!componentMap.has(comp)) componentMap.set(comp, { added: /* @__PURE__ */ new Set(), removed: /* @__PURE__ */ new Set() });
52
+ cls.split(/\s+/).filter(Boolean).forEach((c) => componentMap.get(comp).removed.add(c));
53
+ }
54
+ const changes = [];
55
+ for (const [comp, { added, removed }] of componentMap) {
56
+ if (comp === "unknown") continue;
57
+ const pureAdded = [...added].filter((c) => !removed.has(c));
58
+ const pureRemoved = [...removed].filter((c) => !added.has(c));
59
+ if (pureAdded.length === 0 && pureRemoved.length === 0) continue;
60
+ changes.push({ component: comp, added: pureAdded, removed: pureRemoved });
61
+ }
62
+ return changes;
63
+ }
64
+ function renderStyleChanges(changes) {
65
+ const lines = [];
66
+ for (const change of changes) {
67
+ lines.push(`**${change.component}**`);
68
+ if (change.added.length > 0) lines.push(` + ${change.added.join(" ")}`);
69
+ if (change.removed.length > 0) lines.push(` - ${change.removed.join(" ")}`);
70
+ }
71
+ return lines;
72
+ }
73
+ function isJSXElement(line) {
74
+ const t = line.trim();
75
+ return /^<[A-Za-z]/.test(t) || /^<\/[A-Za-z]/.test(t);
76
+ }
77
+ function isJSXClosing(line) {
78
+ return /^<\/[A-Za-z]/.test(line.trim());
79
+ }
80
+ function isJSXSelfClosing(line) {
81
+ return /\/>[\s]*$/.test(line.trim());
82
+ }
83
+ function extractJSXComponentName(line) {
84
+ const trimmed = line.trim();
85
+ const closingMatch = trimmed.match(/^<\/([A-Za-z][A-Za-z0-9.]*)/);
86
+ if (closingMatch) return `/${closingMatch[1]}`;
87
+ const nameMatch = trimmed.match(/^<([A-Za-z][A-Za-z0-9.]*)/);
88
+ if (!nameMatch) return trimmed;
89
+ const name = nameMatch[1];
90
+ const eventProps = [];
91
+ for (const m of trimmed.matchAll(/\b(on[A-Z]\w+)=/g)) {
92
+ eventProps.push(m[1]);
93
+ }
94
+ return eventProps.length > 0 ? `${name}(${eventProps.join(", ")})` : name;
95
+ }
96
+ function shouldIgnoreJSX(line) {
97
+ const t = line.trim();
98
+ return isClassNameOnlyLine(t) || /^style=/.test(t) || /^aria-/.test(t) || /^data-/.test(t) || /^strokeLinecap=/.test(t) || /^strokeLinejoin=/.test(t) || /^strokeWidth=/.test(t) || /^viewBox=/.test(t) || /^fill=/.test(t) || /^stroke=/.test(t) || /^d="/.test(t) || t === "{" || t === "}" || t === "(" || t === ")" || t === "<>" || t === "</>" || /^\{\/\*/.test(t);
99
+ }
100
+ function parseJSXToFlowTree(lines) {
101
+ const roots = [];
102
+ const stack = [];
103
+ for (const line of lines) {
104
+ if (!isJSXElement(line)) continue;
105
+ if (shouldIgnoreJSX(line)) continue;
106
+ const depth = getIndentDepth(line);
107
+ if (isJSXClosing(line)) {
108
+ while (stack.length > 0 && stack[stack.length - 1].depth >= depth) {
109
+ stack.pop();
110
+ }
111
+ continue;
112
+ }
113
+ const name = extractJSXComponentName(line);
114
+ const selfClosing = isJSXSelfClosing(line);
115
+ const node = {
116
+ type: "call",
117
+ name,
118
+ children: [],
119
+ depth,
120
+ priority: 5 /* OTHER */
121
+ };
122
+ while (stack.length > 0 && stack[stack.length - 1].depth >= depth) {
123
+ stack.pop();
124
+ }
125
+ if (stack.length === 0) {
126
+ roots.push(node);
127
+ } else {
128
+ stack[stack.length - 1].node.children.push(node);
129
+ }
130
+ if (!selfClosing) {
131
+ stack.push({ node, depth });
132
+ }
133
+ }
134
+ return roots;
135
+ }
10
136
  function filterDiffLines(diffText) {
11
137
  const lines = diffText.split("\n");
12
138
  const added = lines.filter((l) => l.startsWith("+") && !l.startsWith("+++") && l.trim() !== "+").map((l) => l.substring(1));
@@ -190,8 +316,59 @@ function parseDiffToLogicalFlow(diffText) {
190
316
  removedCode: removed.join("\n")
191
317
  };
192
318
  }
319
+ function extractChangedSymbols(addedLines, removedLines) {
320
+ const FUNC_RE = /^(?:export\s+)?(?:async\s+)?function\s+([a-z]\w+)|^(?:export\s+)?(?:const|let|var)\s+([a-z]\w+)\s*=\s*(?:async\s*)?\(/;
321
+ const COMPONENT_RE = /^(?:export\s+)?(?:default\s+)?(?:function|const)\s+([A-Z][a-z][A-Za-z0-9]*)/;
322
+ const extract = (lines) => {
323
+ const names = /* @__PURE__ */ new Set();
324
+ for (const line of lines) {
325
+ const cm = line.match(COMPONENT_RE) || line.match(FUNC_RE);
326
+ if (cm) {
327
+ const name = cm[1] || cm[2];
328
+ if (name) names.add(name);
329
+ }
330
+ }
331
+ return names;
332
+ };
333
+ const addedNames = extract(addedLines);
334
+ const removedNames = extract(removedLines);
335
+ const results = [];
336
+ const seen = /* @__PURE__ */ new Set();
337
+ for (const name of addedNames) {
338
+ seen.add(name);
339
+ results.push({ name, status: removedNames.has(name) ? "modified" : "added" });
340
+ }
341
+ for (const name of removedNames) {
342
+ if (!seen.has(name)) {
343
+ results.push({ name, status: "removed" });
344
+ }
345
+ }
346
+ return results;
347
+ }
348
+ function renderJSXTreeCompact(nodes, maxDepth = 3) {
349
+ const lines = [];
350
+ function walk(node, depth) {
351
+ if (depth > maxDepth) return;
352
+ const indent = " ".repeat(depth);
353
+ const hasChildren = node.children.length > 0;
354
+ lines.push(`${indent}${node.name}${hasChildren ? "" : ""}`);
355
+ for (const child of node.children) {
356
+ walk(child, depth + 1);
357
+ }
358
+ }
359
+ for (const root of nodes) {
360
+ walk(root, 0);
361
+ }
362
+ return lines.join("\n");
363
+ }
193
364
  function generateReaderMarkdown(diffText, meta = {}) {
194
- const result = parseDiffToLogicalFlow(diffText);
365
+ const { added, removed } = filterDiffLines(diffText);
366
+ const isJSX = Boolean(
367
+ meta.file && isJSXFile(meta.file) || hasJSXContent(added)
368
+ );
369
+ const changedSymbols = extractChangedSymbols(added, removed);
370
+ const classNameChanges = isJSX ? parseClassNameChanges(added, removed) : [];
371
+ const jsxTree = isJSX ? parseJSXToFlowTree(added) : [];
195
372
  const sections = [];
196
373
  sections.push("# \u{1F4D6} GitHub Reader View\n");
197
374
  sections.push("> Generated by **github-mobile-reader**");
@@ -200,34 +377,46 @@ function generateReaderMarkdown(diffText, meta = {}) {
200
377
  if (meta.commit) sections.push(`> Commit: \`${meta.commit}\``);
201
378
  if (meta.file) sections.push(`> File: \`${meta.file}\``);
202
379
  sections.push("\n");
203
- if (result.root.length > 0) {
204
- sections.push("## \u{1F9E0} Logical Flow\n");
205
- sections.push("```");
206
- sections.push(...renderFlowTree(result.root));
207
- sections.push("```\n");
380
+ if (changedSymbols.length > 0) {
381
+ sections.push("### \uBCC0\uACBD\uB41C \uD568\uC218 / \uCEF4\uD3EC\uB10C\uD2B8\n");
382
+ const STATUS_ICON = { added: "\u2705", removed: "\u274C", modified: "\u270F\uFE0F" };
383
+ for (const { name, status } of changedSymbols) {
384
+ sections.push(`- ${STATUS_ICON[status]} \`${name}()\` \u2014 ${status}`);
385
+ }
386
+ sections.push("");
208
387
  }
209
- if (result.rawCode.trim()) {
210
- sections.push("## \u2705 Added Code\n");
211
- sections.push("```typescript");
212
- sections.push(result.rawCode);
388
+ if (isJSX && jsxTree.length > 0) {
389
+ sections.push("### \u{1F3A8} JSX Structure\n");
390
+ sections.push("```");
391
+ sections.push(renderJSXTreeCompact(jsxTree));
213
392
  sections.push("```\n");
214
393
  }
215
- if (result.removedCode.trim()) {
216
- sections.push("## \u274C Removed Code\n");
217
- sections.push("```typescript");
218
- sections.push(result.removedCode);
219
- sections.push("```\n");
394
+ if (isJSX && classNameChanges.length > 0) {
395
+ sections.push("### \u{1F485} Style Changes\n");
396
+ sections.push(...renderStyleChanges(classNameChanges));
397
+ sections.push("");
220
398
  }
221
399
  sections.push("---");
222
- sections.push("\u{1F6E0} Auto-generated by [github-mobile-reader](https://github.com/your-org/github-mobile-reader). Do not edit manually.");
400
+ sections.push("\u{1F6E0} Auto-generated by [github-mobile-reader](https://github.com/3rdflr/github-mobile-reader). Do not edit manually.");
223
401
  return sections.join("\n");
224
402
  }
225
403
  export {
226
404
  Priority,
405
+ extractChangedSymbols,
406
+ extractClassName,
407
+ extractJSXComponentName,
227
408
  filterDiffLines,
228
409
  generateReaderMarkdown,
410
+ hasJSXContent,
411
+ isClassNameOnlyLine,
412
+ isJSXElement,
413
+ isJSXFile,
229
414
  normalizeCode,
415
+ parseClassNameChanges,
230
416
  parseDiffToLogicalFlow,
417
+ parseJSXToFlowTree,
231
418
  parseToFlowTree,
232
- renderFlowTree
419
+ renderFlowTree,
420
+ renderJSXTreeCompact,
421
+ renderStyleChanges
233
422
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github-mobile-reader",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Transform git diffs into mobile-friendly Markdown — no more horizontal scrolling when reviewing code on your phone.",
5
5
  "keywords": [
6
6
  "github",
@@ -17,10 +17,13 @@
17
17
  },
18
18
  "repository": {
19
19
  "type": "git",
20
- "url": "https://github.com/3rdflr/github-mobile-reader.git"
20
+ "url": "git+https://github.com/3rdflr/github-mobile-reader.git"
21
21
  },
22
22
  "license": "MIT",
23
23
  "author": "3rdflrhtl@gmail.com",
24
+ "bin": {
25
+ "github-mobile-reader": "dist/cli.js"
26
+ },
24
27
  "main": "./dist/index.js",
25
28
  "module": "./dist/index.mjs",
26
29
  "types": "./dist/index.d.ts",
@@ -39,7 +42,8 @@
39
42
  "scripts": {
40
43
  "build": "tsup src/index.ts --format cjs,esm --dts --clean",
41
44
  "build:action": "tsup src/action.ts --format cjs --outDir dist --no-splitting",
42
- "build:all": "npm run build && npm run build:action",
45
+ "build:cli": "tsup src/cli.ts --format cjs --outDir dist --no-splitting",
46
+ "build:all": "npm run build && npm run build:action && npm run build:cli",
43
47
  "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
44
48
  "prepublishOnly": "npm run build:all"
45
49
  },