Some checks failed
windows-msi / full-msi-unsigned (push) Has been cancelled
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.
278 lines
7.6 KiB
Go
278 lines
7.6 KiB
Go
// 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)
|
||
}
|