@fffangchenghao/dsh-latex-look 0.3.4 → 0.3.5

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/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.5
4
+
5
+ - **The tool reports facts, not advice.** When the point lookup finds a rule it now
6
+ says what it measured and stops: `ink at this point: horizontal rule at y=47pt,
7
+ 76% of page width`, then either `drawn by line 15: \rule{...}` or `nearest source
8
+ line 40 contains no rule command ... the document body does not draw this rule`.
9
+ The previous wording told the reader to go and look at `\pagestyle` /
10
+ `\headrulewidth`; that is knowledge about typography, not an observation, and the
11
+ tool is the wrong place for it — it lives in `SKILL.md`, where the agent reads it
12
+ and decides.
13
+ - The ink probe searches a ±4 pt band instead of one pixel row. Clicking a line is
14
+ routinely a point or two off, and a miss left the caller trusting the locate answer
15
+ this check exists to challenge. The finding reports the rule's own y, so a query
16
+ two points away still lands on the truth.
17
+
3
18
  ## 0.3.4
4
19
 
5
20
  - Fix the bundle patch for a scoped package name: `name: @scope/pkg` is not valid
package/SKILL.md CHANGED
@@ -170,7 +170,7 @@ document is broken, suspect one of these:
170
170
  | Jumped to another page | `[htbp]` + `\FloatBarrier`; or `\usepackage{float}` + `[H]` |
171
171
  | Adding a caption pushed a near-full-page figure off the page, leaving half a page blank | the figure was already as tall as the text block, so a few millimetres of caption cannot fit above it. Wrapping it in `minipage` makes it unbreakable. Make that figure a float (`\begin{figure}[!tbp]`) so the text after it fills the page and the figure starts the next one |
172
172
  | Caption orphaned on the next page | wrap figure+caption in one `minipage` — a caption is not tied to its graphic by default |
173
- | "Delete the line at the top of the page" and `--point` returns `\end{document}` | the rule is page furniture, and `latex_look` now says so: `^ that is a rule ... NO line of the document draws it`. Look at `\pagestyle`, `\fancyhf`, `\headrulewidth` — never at line 40. When the rule *is* typed, the same line names it: `drawn by line 15: \rule{\linewidth}{0.4pt}` |
173
+ | "Delete the line at the top of the page", and the point lookup reports `the document body does not draw this rule` | the rule comes from the page style. It is `\pagestyle{fancy}` / `\pagestyle{headings}` with the head rule still on, or a `\rule`/`\hrule` in a `\markboth`-style macro. Fix it with `\fancyhf{}` plus `\renewcommand{\headrulewidth}{0pt}`, or switch to `\pagestyle{plain}`. Do **not** edit the line the lookup named (`\end{document}`); it is only the nearest box. When the rule *is* typed in the body, the lookup names it instead: `drawn by line 15: \rule{\linewidth}{0.4pt}`, and that is the line to change |
174
174
  | A figure sits above the chapter title on a chapter's first page | a top float won the opening page. `\let\origchapter\chapter\renewcommand{\chapter}{\suppressfloats[t]\origchapter}` (no `@` in the name, or wrap it in `\makeatletter`) forbids top floats on every chapter opening, now and for chapters added later |
175
175
  | Numbers run backwards: 图 3-4 on one page, 图 3-3 on the next | you floated one object but left its numbered neighbours inline. A float is placed later than an inline object that follows it in the source, and the reader turns the page to see 3-4 before 3-3. Floats of the same class keep their relative order, so float the **whole run** — never a float mixed into inline siblings. Check with `latex_look --sequence 图` after any change to how objects are placed |
176
176
  | Too much space around it | `\setlength{\intextsep}{6pt}`, `\setlength{\textfloatsep}{10pt}` |
@@ -83,11 +83,16 @@ function summary(r) {
83
83
  const at = typeof h.x === 'number' && typeof h.y === 'number' ? ` x=${h.x.toFixed(1)} y=${h.y.toFixed(1)}` : ''
84
84
  L.push(` ${h.file}:${h.line} → p${h.page}${at}${fixed}`)
85
85
  if (h.ink && h.ink.kind === 'rule' && h.ink.origin === 'command' && h.ink.sourceLine) {
86
- L.push(` ^ that is a rule, not text: it is drawn by line ${h.ink.sourceLine.line}: ${h.ink.sourceLine.text}`)
86
+ L.push(` ^ ink at this point: horizontal rule at y=${h.ink.y}pt, ${Math.round(h.ink.span * 100)}% of page width`)
87
+ L.push(` drawn by line ${h.ink.sourceLine.line}: ${h.ink.sourceLine.text}`)
87
88
  } else if (h.ink && h.ink.kind === 'rule') {
88
- L.push(` ^ that is a rule spanning ${Math.round(h.ink.span * 100)}% of the page, and NO line of the document draws it.`)
89
- L.push(' It is page furniture: \\pagestyle / \\fancyhf / \\headrulewidth, or a table/list rule')
90
- L.push(' from a package. The line above is only the nearest box, not the cause.')
89
+ // Facts only. What to do about it is the reader's knowledge, not this tool's,
90
+ // and a hint here would be wrong on any document that does not match the guess.
91
+ L.push(` ^ ink at this point: horizontal rule at y=${h.ink.y}pt, ${Math.round(h.ink.span * 100)}% of page width`)
92
+ L.push(` nearest source line ${h.line} contains no rule command, and neither does any line within 4 of it`)
93
+ L.push(' the document body does not draw this rule')
94
+ } else if (h.ink && h.ink.kind === 'blank') {
95
+ L.push(' ^ ink at this point: none (blank paper; the nearest box belongs to a different spot)')
91
96
  }
92
97
  }
93
98
  }
package/index.js CHANGED
@@ -128,11 +128,16 @@ function format(r) {
128
128
  : ''
129
129
  L.push(` ${h.file}:${h.line} → page ${h.page}${y}${fixed}`)
130
130
  if (h.ink && h.ink.kind === 'rule' && h.ink.origin === 'command' && h.ink.sourceLine) {
131
- L.push(` ^ that is a rule, not text: it is drawn by line ${h.ink.sourceLine.line}: ${h.ink.sourceLine.text}`)
131
+ L.push(` ^ ink at this point: horizontal rule at y=${h.ink.y}pt, ${Math.round(h.ink.span * 100)}% of page width`)
132
+ L.push(` drawn by line ${h.ink.sourceLine.line}: ${h.ink.sourceLine.text}`)
132
133
  } else if (h.ink && h.ink.kind === 'rule') {
133
- L.push(` ^ that is a rule spanning ${Math.round(h.ink.span * 100)}% of the page, and NO line of the document draws it.`)
134
- L.push(' It is page furniture: \\pagestyle / \\fancyhf / \\headrulewidth, or a table/list rule from a package.')
135
- L.push(' The line above is only the nearest box, not the cause.')
134
+ // Facts only: what to do about a rule is the reader's knowledge, not the
135
+ // tool's. A hint here would be wrong on any document that does not match it.
136
+ L.push(` ^ ink at this point: horizontal rule at y=${h.ink.y}pt, ${Math.round(h.ink.span * 100)}% of page width`)
137
+ L.push(` nearest source line ${h.line} contains no rule command, and neither does any line within 4 of it`)
138
+ L.push(' the document body does not draw this rule')
139
+ } else if (h.ink && h.ink.kind === 'blank') {
140
+ L.push(' ^ ink at this point: none (blank paper; the nearest box belongs to a different spot)')
136
141
  }
137
142
  }
138
143
  }
package/lib/core.mjs CHANGED
@@ -824,27 +824,34 @@ export function classifyInk(raster, { x, y, dpi = 72 } = {}) {
824
824
  const o = (yy * raster.width + xx) * 3
825
825
  return raster.data[o] < 200 && raster.data[o + 1] < 200 && raster.data[o + 2] < 200
826
826
  }
827
- let bestSpan = 0
828
- let bestRow = py
829
- for (let yy = Math.max(0, py - 1); yy <= Math.min(raster.height - 1, py + 1); yy++) {
827
+ const rowInk = (yy) => {
830
828
  let count = 0
831
829
  for (let xx = 0; xx < raster.width; xx++) if (at(xx, yy)) count++
832
- if (count > bestSpan) { bestSpan = count; bestRow = yy }
830
+ return count / raster.width
831
+ }
832
+ // Look for a rule in a band around the point, not on one exact row: a reader who
833
+ // clicks a line is routinely a point or two away, and a miss here would leave the
834
+ // caller trusting a locate answer this check exists to challenge.
835
+ const bandPt = 4
836
+ const band = Math.max(1, Math.round((bandPt / 72) * dpi))
837
+ let best = { span: 0, row: py }
838
+ for (let yy = Math.max(0, py - band); yy <= Math.min(raster.height - 1, py + band); yy++) {
839
+ const span = rowInk(yy)
840
+ if (span > best.span) best = { span, row: yy }
841
+ }
842
+ let thin = true
843
+ for (const yy of [best.row - 3, best.row + 3]) {
844
+ if (yy < 0 || yy >= raster.height) continue
845
+ if (rowInk(yy) > 0.3) { thin = false; break }
846
+ }
847
+ if (best.span > 0.35 && thin) {
848
+ return { kind: 'rule', span: Number(best.span.toFixed(2)), y: Number(((best.row / dpi) * 72).toFixed(1)) }
833
849
  }
834
- const span = bestSpan / raster.width
835
850
  let near = 0
836
851
  for (let yy = Math.max(0, py - 4); yy <= Math.min(raster.height - 1, py + 4); yy++) {
837
852
  for (let xx = Math.max(0, px - 4); xx <= Math.min(raster.width - 1, px + 4); xx++) if (at(xx, yy)) near++
838
853
  }
839
- let thin = true
840
- for (const yy of [bestRow - 3, bestRow + 3]) {
841
- if (yy < 0 || yy >= raster.height) continue
842
- let count = 0
843
- for (let xx = 0; xx < raster.width; xx++) if (at(xx, yy)) count++
844
- if (count / raster.width > 0.3) { thin = false; break }
845
- }
846
- const kind = span > 0.35 && thin ? 'rule' : near > 0 ? 'text' : 'blank'
847
- return { kind, span: Number(span.toFixed(2)) }
854
+ return { kind: near > 0 ? 'text' : 'blank' }
848
855
  }
849
856
 
850
857
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fffangchenghao/dsh-latex-look",
3
- "version": "0.3.4",
3
+ "version": "0.3.5",
4
4
  "description": "DSH plugin: let an AI agent see a LaTeX document — compile it, render the page, locate the source line, and diff what an edit changed.",
5
5
  "type": "module",
6
6
  "license": "MIT",