mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 02:29:33 +01:00
fix(story-mode): a growth too small to apply never costs a balloon a line
A panel ignores a growth of 2 px or less, but the clamp measured against the exact room, so a balloon that fell 0.6 px short of two lines showed one. The clamp now lets a balloon use those 2 px of the 4 px gap it keeps from the art and Claude's head, which is what the ignored growth would have bought, and rounds away float error. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
0bf6b77170
commit
09af5c3dc8
2 files changed
+29
-6
No files matched your search
@@ -2965,11 +2965,16 @@ Object.assign(CodemanApp.prototype, {
|
||||
const fit = rt.balloonFit;
|
||||
rt.balloonKey = `${h}|${fit?.serial ?? '-'}|${b.className}|${b.textContent}`;
|
||||
text.style.removeProperty('--story-lines');
|
||||
const limit = fit ? fit.limit + h - fit.stageH : h - 12;
|
||||
// A panel ignores a growth of 2 px or less (_storyFitCaption): the balloon may use that much of the
|
||||
// gap it keeps, or such a panel would show one line fewer than its fit made room for.
|
||||
const limit = (fit ? fit.limit + h - fit.stageH : h - 12) + 2;
|
||||
const lh = parseFloat(getComputedStyle(text).lineHeight) || 19;
|
||||
const pad = b.offsetHeight - text.offsetHeight;
|
||||
if (b.offsetHeight && b.offsetTop + b.offsetHeight > limit)
|
||||
text.style.setProperty('--story-lines', String(Math.max(1, Math.floor((limit - b.offsetTop - pad) / lh))));
|
||||
text.style.setProperty(
|
||||
'--story-lines',
|
||||
String(Math.max(1, Math.floor((limit - b.offsetTop - pad) / lh + 0.001)))
|
||||
);
|
||||
},
|
||||
|
||||
/** Canvas click regions become real buttons: focusable, labelled, keyboard-reachable. */
|
||||
|
||||
@@ -1320,7 +1320,8 @@ describe('story-mode.js speech balloons', () => {
|
||||
});
|
||||
|
||||
/** A panel with a box model: JSDOM lays nothing out, so every box the fit reads is given here. */
|
||||
function boxes(rt: Record<string, any>, capHeight: number) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function boxes(rt: Record<string, any>, capHeight: number, { capWidth = 250, pad = 24 } = {}) {
|
||||
const def = (el: HTMLElement, props: Record<string, () => number>) => {
|
||||
for (const [k, get] of Object.entries(props)) Object.defineProperty(el, k, { get, configurable: true });
|
||||
};
|
||||
@@ -1335,18 +1336,18 @@ describe('story-mode.js speech balloons', () => {
|
||||
offsetTop: () => 10,
|
||||
offsetLeft: () => 10,
|
||||
offsetHeight: () => capHeight,
|
||||
offsetWidth: () => 250,
|
||||
offsetWidth: () => capWidth,
|
||||
});
|
||||
const b = rt.balloon as HTMLElement;
|
||||
const text = b.querySelector('.story-balloon-text') as HTMLElement;
|
||||
// Three lines of 19 px, fewer once clamped; 24 px of padding and border around them.
|
||||
// Three lines of 19 px, fewer once clamped; `pad` px of padding and border around them.
|
||||
const lines = () => Number(text.style.getPropertyValue('--story-lines')) || 3;
|
||||
const right = () => parseFloat(b.style.right);
|
||||
def(b, {
|
||||
offsetTop: () => parseFloat(b.style.top) || 0,
|
||||
offsetWidth: () => 200,
|
||||
offsetLeft: () => (Number.isNaN(right()) ? parseFloat(b.style.left) || 0 : 394 - right() - 200),
|
||||
offsetHeight: () => 24 + lines() * 19,
|
||||
offsetHeight: () => pad + lines() * 19,
|
||||
});
|
||||
def(text, { offsetHeight: () => lines() * 19 });
|
||||
return state;
|
||||
@@ -1378,6 +1379,23 @@ describe('story-mode.js speech balloons', () => {
|
||||
expect(text.style.getPropertyValue('--story-lines')).toBe('');
|
||||
});
|
||||
|
||||
it('a growth too small for the panel to take never costs the balloon a line', async () => {
|
||||
boot(storyOf(demoTurn()));
|
||||
app.openStoryMode(SID);
|
||||
await settle();
|
||||
sizeAndDraw();
|
||||
const rt = app._story.panels.find((p: { kind: string }) => p.kind === 'investigate');
|
||||
// A short caption: the balloon stands beside it, 10 px down. Two lines need 22 + 38 px and Claude's
|
||||
// head leaves 59.4 (69.4 - 10): 0.6 px short, under the 2 px a panel ignores.
|
||||
boxes(rt, 40, { capWidth: 120, pad: 22 });
|
||||
app._storyFitCaption(rt);
|
||||
expect(rt.balloon.style.top).toBe('10px');
|
||||
expect(rt.el.style.minHeight).toBe('');
|
||||
expect(
|
||||
(rt.balloon.querySelector('.story-balloon-text') as HTMLElement).style.getPropertyValue('--story-lines')
|
||||
).toBe('2');
|
||||
});
|
||||
|
||||
it('your balloon stands below a long caption, never under it', async () => {
|
||||
boot(storyOf(demoTurn()));
|
||||
app.openStoryMode(SID);
|
||||
|
||||
Reference in new issue
Block a user