mirror of
https://github.com/mineracks/seedhammer-v1-companion.git
synced 2026-06-26 22:01:05 +10:00
The Phase 2 scaffolding boot screen is replaced with the actual
upstream v1.3.0 gui package running in browser. cmd/emulator now
calls gui.NewApp(plat, version) + drives a.Frame() in a goroutine.
Lifts:
gui/ (top-level UI + state machine)
gui/assets (icons, fonts, 9-patch images)
gui/layout (constraint-based UI layout)
gui/op (drawing op primitives)
gui/saver (screensaver + idle timeout)
gui/text (text shaping)
gui/widget (button/menu/keyboard widgets)
All 11 .go files lifted verbatim from seedhammer/seedhammer @ v1.3.0
(commit 2f071c1d...), import paths rewritten seedhammer.com → mineracks.
browserPlatform now implements gui.Platform (12 methods):
Events(deadline) — drains v1.Event chan, maps to gui.ButtonEvent
(gui.Button enum order matches platform/v1.Button,
so the conversion is a direct uint cast)
Wakeup() — no-op (no sleep state in browser)
PlateSizes() — backup.SquarePlate, backup.LargePlate
Engraver() — nullEngraver stub (browser can't punch metal)
EngraverParams() — copy of mjolnir.Params {StrokeWidth: 38,
Millimeter: 126}. Inlined because tarm/serial
(mjolnir's USB dep) doesn't compile to js/wasm.
CameraFrame(size) — emits FrameEvent{Error: stubbed} so QR-scan
screens fall through cleanly; real handoff
lands when SeedSigner sim wires up (Phase 2.5)
Now() — time.Now()
DisplaySize() — 240×240
Dirty(r) — records rect, resets chunk cursor
NextChunk() — one-shot: returns full sub-image once per
Dirty cycle, then flushes the whole frame
buffer to JS via emulatorPaint()
ScanQR() — returns nil decodes (stub)
Debug() — false
The Engraver interface is satisfied by nullEngraver — Engrave() returns
"engraver not connected" so the GUI's engrave flow fails cleanly
instead of looking like it's working.
WASM grows 2.7MB → 8.0MB (gui package + btcd + crypto deps + fonts
all linked in). Acceptable for the v1 emulator one-time cache.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
119 lines
2.0 KiB
Go
119 lines
2.0 KiB
Go
package text
|
|
|
|
import (
|
|
"image"
|
|
"unicode"
|
|
"unicode/utf8"
|
|
|
|
"golang.org/x/image/math/fixed"
|
|
"github.com/mineracks/seedhammer-v1-companion/font/bitmap"
|
|
)
|
|
|
|
type Line struct {
|
|
Text string
|
|
Width int
|
|
Dot image.Point
|
|
}
|
|
|
|
type Style struct {
|
|
Face *bitmap.Face
|
|
Alignment Alignment
|
|
LineHeight float32
|
|
LetterSpacing int
|
|
}
|
|
|
|
type Alignment int
|
|
|
|
const (
|
|
AlignStart Alignment = iota
|
|
AlignEnd
|
|
AlignCenter
|
|
)
|
|
|
|
func (l Style) Layout(maxWidth int, txt string) ([]Line, image.Point) {
|
|
var lines []Line
|
|
prevC := rune(-1)
|
|
adv := fixed.I(0)
|
|
wordAdv := fixed.I(0)
|
|
wordIdx := 0
|
|
maxAdv := 0
|
|
prev := 0
|
|
idx := 0
|
|
m := l.Face.Metrics()
|
|
asc, desc := m.Ascent, m.Descent
|
|
lheight := m.Height.Ceil()
|
|
if l.LineHeight != 0 {
|
|
lheight = int(float32(lheight) * l.LineHeight)
|
|
}
|
|
doty := asc.Ceil()
|
|
endLine := func() {
|
|
prevC = -1
|
|
if a := adv.Ceil(); a > maxAdv {
|
|
maxAdv = a
|
|
}
|
|
lines = append(lines, Line{
|
|
Text: txt[prev:idx],
|
|
Width: adv.Ceil(),
|
|
Dot: image.Pt(0, doty),
|
|
})
|
|
wordIdx = 0
|
|
wordAdv = 0
|
|
doty += lheight
|
|
}
|
|
for idx < len(txt) {
|
|
c, n := utf8.DecodeRuneInString(txt[idx:])
|
|
a, ok := l.Face.GlyphAdvance(c)
|
|
if !ok {
|
|
prevC = -1
|
|
idx += n
|
|
continue
|
|
}
|
|
softnl := unicode.IsSpace(c)
|
|
if softnl {
|
|
wordIdx = idx
|
|
wordAdv = adv
|
|
}
|
|
if prevC >= 0 {
|
|
a += l.Face.Kern(prevC, c)
|
|
}
|
|
a += fixed.I(l.LetterSpacing)
|
|
if c == '\n' || (idx > prev && (adv+a).Ceil() > maxWidth) {
|
|
if wordIdx > 0 {
|
|
idx = wordIdx
|
|
adv = wordAdv
|
|
_, n = utf8.DecodeRuneInString(txt[idx:])
|
|
softnl = true
|
|
}
|
|
endLine()
|
|
prev = idx
|
|
idx += n
|
|
adv = a
|
|
if softnl {
|
|
// Skip space or newline.
|
|
prev += n
|
|
adv = 0
|
|
}
|
|
continue
|
|
}
|
|
idx += n
|
|
prevC = c
|
|
adv += a
|
|
}
|
|
idx = len(txt)
|
|
if prev < idx {
|
|
endLine()
|
|
}
|
|
for i, line := range lines {
|
|
switch l.Alignment {
|
|
case AlignCenter:
|
|
lines[i].Dot.X = (maxAdv - line.Width) / 2
|
|
case AlignEnd:
|
|
lines[i].Dot.X = maxAdv - line.Width
|
|
}
|
|
}
|
|
return lines, image.Point{
|
|
X: maxAdv,
|
|
Y: doty - lheight + desc.Ceil(),
|
|
}
|
|
}
|