PrintToVeraeTimeStamp/internal/statuslang/status.go
George Lambert 6f8796c25e
Some checks failed
windows-msi / full-msi-unsigned (push) Has been cancelled
Open Zap Billing when PrintIt stamps fail for credits
On HTTP 402/429 (and credit/billing error bodies), hold the print job and open
https://zap.verae.ai/portal/?tab=billing so the user can reload prepaid balance.
Throttle browser opens to once per 10 minutes. Auth failures still use the
sign-in flow unchanged.
2026-10-20 01:07:39 -04:00

278 lines
7.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package statuslang holds exact Verae 2026 receipt status strings (BrandingBook).
// Only "final" (and "existing") is evidence — never present a jobId as proof.
package statuslang
import (
"fmt"
"strings"
)
// Exact BrandingBook receipt status labels (brand-tokens status.*).
const (
Queued = "Queued offline"
Hashing = "Hashing on device…"
Existing = "Already registered"
Pending = "Pending receipt"
Provisional = "Stamped, awaiting blockchain"
Failed = "Failed – retrying"
Action = "Failed – action needed"
// Notify / tray short titles (not chip labels; still brand-aligned).
FinalReceiptReady = "Final receipt ready"
SignInToFinish = "Sign in to finish timestamp"
OutOfCredits = "Out of credits — add funds"
PrinterReady = "Printer ready"
// EvidenceNote is the standing disclaimer for queue / tray UI.
EvidenceNote = "Only a final receipt is evidence — hold ids are local queue handles, not proof."
// BrandingBook proof vocabulary (name the chain fully once per view).
ChainName = "Verae Substrate blockchain"
NetworkName = "VeraeNet"
VerifyBase = "https://verify.verae.ai"
ExplorerURL = "https://explorer.verae.ai/time"
)
// BrandingBook status.desc strings (tooltips / secondary copy).
const (
DescQueued = "Not yet sent. Will submit when online."
DescHashing = "SHA-256 computed locally; file never leaves the device."
DescExisting = "Hash was known: existing receipt returned instantly."
DescPending = "Submitted. Waiting for VeraeTime + Substrate."
DescProvisional = "VeraeTime receipt issued; not yet in a Substrate block."
DescIncluded = "In a Substrate block; waiting for GRANDPA finality."
DescFinal = "GRANDPA-finalized. Receipt + proof available. This is evidence."
DescFailed = "Submission failed; retrying with backoff (never resubmits blindly)."
DescAction = "Needs the user (e.g. key expired, 401)."
)
func Included(block int) string {
return fmt.Sprintf("Included in block #%s (not final)", comma(block))
}
func Finalized(block int) string {
return fmt.Sprintf("Final: Substrate block #%s, finalized", comma(block))
}
// IncludedN / FinalizedN use literal "N" when the block number is unknown
// (matches BrandingBook / Swift ReceiptState when block is nil).
func IncludedN() string {
return "Included in block #N (not final)"
}
func FinalizedN() string {
return "Final: Substrate block #N, finalized"
}
// Label returns the exact BrandingBook chip string for a receipt state key.
// block is required for included/final when known; pass 0 (or omit) to use #N.
func Label(state string, block ...int) string {
var n int
have := false
if len(block) > 0 {
n = block[0]
have = n > 0
}
switch strings.ToLower(strings.TrimSpace(state)) {
case "queued":
return Queued
case "hashing":
return Hashing
case "existing":
return Existing
case "pending":
return Pending
case "provisional":
return Provisional
case "included":
if have {
return Included(n)
}
return IncludedN()
case "final", "completed", "ok":
if have {
return Finalized(n)
}
return FinalizedN()
case "failed":
return Failed
case "action":
return Action
default:
return ""
}
}
// Desc returns the BrandingBook status.desc for a state key.
func Desc(state string) string {
switch strings.ToLower(strings.TrimSpace(state)) {
case "queued":
return DescQueued
case "hashing":
return DescHashing
case "existing":
return DescExisting
case "pending":
return DescPending
case "provisional":
return DescProvisional
case "included":
return DescIncluded
case "final", "completed", "ok":
return DescFinal
case "failed":
return DescFailed
case "action":
return DescAction
default:
return ""
}
}
// IsEvidence is true only for final and already-registered (BrandingBook rule 3).
// Certificates, Download proof, and legal claims are allowed only in these states.
func IsEvidence(state string) bool {
switch strings.ToLower(strings.TrimSpace(state)) {
case "final", "completed", "ok", "existing":
return true
default:
return false
}
}
// NotifyTitle is a short notification title for printit tray / OS banners.
func NotifyTitle(state string) string {
switch state {
case "queued":
return Queued
case "hashing":
return Hashing
case "existing":
return Existing
case "pending":
return Pending
case "provisional":
return Provisional
case "failed":
return Failed
case "action":
return Action
case "held", "signin":
return SignInToFinish
case "billing", "credits", "payment":
return OutOfCredits
case "final", "completed", "ok":
return FinalReceiptReady
default:
return "Verae Timestamp"
}
}
// NotifyBody formats a short tray/banner body.
// For held/pending, extra is a local hold id (never evidence).
// For final, extra is the final receipt id (evidence).
// For failed, extra may be a short error hint.
func NotifyBody(state, title, extra string) string {
t := strings.TrimSpace(title)
if t == "" {
t = "document"
}
switch state {
case "held", "signin", "pending":
if extra != "" {
// Local hold id only — not a receipt / not proof.
return fmt.Sprintf("%s · %s · hold %s — sign in to stamp", t, Pending, short(extra))
}
return fmt.Sprintf("%s · %s — sign in to stamp", t, Pending)
case "billing", "credits", "payment":
if extra != "" {
return fmt.Sprintf("%s · %s · hold %s — open Billing to add funds", t, OutOfCredits, short(extra))
}
return fmt.Sprintf("%s · %s — open Billing to add funds", t, OutOfCredits)
case "failed", "action":
if extra != "" {
return fmt.Sprintf("%s · %s · %s", t, Failed, short(extra))
}
return fmt.Sprintf("%s · %s", t, Failed)
case "final", "completed", "ok":
if extra != "" {
return fmt.Sprintf("%s · receipt %s", t, short(extra))
}
return t
default:
if extra != "" {
return fmt.Sprintf("%s · %s", t, short(extra))
}
return t
}
}
// NotifyHeldJobs is the cancel / still-waiting banner when n jobs remain held.
func NotifyHeldJobs(n int) (title, body string) {
title = NotifyTitle("held")
if n <= 0 {
return title, Pending
}
return title, fmt.Sprintf("%d job(s) · %s — will stamp after you sign in", n, Pending)
}
// TrayHeadline is the persistent status line for tray / menu / loopback health
// (PT-W3 / PT-06 substitute when a native NotifyIcon loop is not available).
// held is local pending count; keyOK means an xVerae- (or equivalent) key is stored;
// helperUp means the IPP loopback helper answered.
func TrayHeadline(held int, keyOK, helperUp bool) string {
switch {
case held > 0:
return NotifyTitle("held")
case !keyOK:
return SignInToFinish
case helperUp:
return PrinterReady
default:
return "Helper not running"
}
}
// TrayDetail is a one-line body under TrayHeadline (brand labels; hold ≠ evidence).
func TrayDetail(held int, keyOK, helperUp bool) string {
parts := make([]string, 0, 4)
if helperUp {
parts = append(parts, PrinterReady)
} else {
parts = append(parts, "helper down")
}
if keyOK {
parts = append(parts, "key saved")
} else {
parts = append(parts, "no signing key")
}
if held > 0 {
parts = append(parts, fmt.Sprintf("%d held · %s", held, Pending))
} else {
parts = append(parts, "queue empty")
}
return strings.Join(parts, " · ")
}
func short(s string) string {
s = strings.TrimSpace(s)
if len(s) <= 24 {
return s
}
return s[:20] + "…"
}
func comma(n int) string {
s := fmt.Sprintf("%d", n)
if n < 1000 {
return s
}
out := make([]byte, 0, len(s)+len(s)/3)
for i, c := range s {
if i != 0 && (len(s)-i)%3 == 0 {
out = append(out, ',')
}
out = append(out, byte(c))
}
return string(out)
}