webui

package
v1.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 17, 2026 License: Apache-2.0 Imports: 35 Imported by: 0

Documentation

Overview

Package webui owns the web UI's Go side: compose windows, their bound service and the theme variables.

Index

Constants

View Source
const ImageScheme = "godwit-img"

ImageScheme is the URL scheme of a message's remote images in its body, served by ImageHandler: godwit-img://m<message>/<token>.

Variables

View Source
var (
	// ErrNoDraft is returned to a window that has no draft attached.
	ErrNoDraft = errors.New("this window has no draft")
	// ErrNotReady is returned before the data is open.
	ErrNotReady = errors.New("the data is not open yet")
	// ErrNoRevision is returned for a save without the revision it edits;
	// core skips the conflict check for revision 0 (core/compose.go SaveDraft).
	ErrNoRevision = errors.New("a save needs the revision it edits")
	// ErrNoPicker is returned for attaching files when there is no file dialog.
	ErrNoPicker = errors.New("no file dialog")
)
View Source
var (
	// ErrBadHeight is returned for a negative or absurd height.
	ErrBadHeight = errors.New("the height is out of range")
	// ErrBadWheel is returned for a wheel delta that is not a number.
	ErrBadWheel = errors.New("the wheel delta is not a number")
)
View Source
var (
	// SignatureAccount tells the signature page which account to show: it
	// saves the shown one's pending edit first.
	SignatureAccount = mygo.NewEvent[int64]("signatureAccount")
	// SignatureFlush asks the signature page to save now; it answers with
	// SignaturePage.Saved and the same token.
	SignatureFlush = mygo.NewEvent[int64]("signatureFlush")
)
View Source
var (
	// Closing tells a compose page its window is closing: it flushes its
	// input and calls ComposePage.Closed with the payload, the close
	// attempt, so an answer to an earlier attempt counts for nothing.
	Closing = mygo.NewEvent[int]("closing")
	// ThemeChanged carries the theme to every page when the settings change.
	ThemeChanged = mygo.NewEvent[Theme]("theme")
)
View Source
var (
	// ErrBadDecisions is returned for decisions that are not one 1 (accept)
	// or -1 (reject) per change of an inline review.
	ErrBadDecisions = errors.New("one decision, 1 or -1, per change of an inline review")
	// ErrBadSelection is returned for an empty selection or one outside the text.
	ErrBadSelection = errors.New("the selection is empty or outside the text")
	// ErrBadLink is returned for a link that is not http, https or mailto.
	ErrBadLink = errors.New("only http, https and mailto links open")
)
View Source
var (
	// WitOpen tells the Wit page the route's conversation changed.
	WitOpen = mygo.NewEvent[WitOpenMsg]("witOpen")
	// WitState tells the Wit page its model label, copy or theme changed
	// (the settings did); the conversation and its stream stay.
	WitState = mygo.NewEvent[WitPageState]("witState")
	// WitAsk hands the Wit page a question native collected (a search that
	// found nothing): the page asks it once, in the conversation it shows.
	WitAsk = mygo.NewEvent[string]("witAsk")
)
View Source
var ErrBadDirection = errors.New("navigate goes back (-1) or forward (1)")

ErrBadDirection is returned for a Navigate that is not -1 or 1.

View Source
var ErrNotAsk = errors.New("the Wit page acts on its ask conversation only")

ErrNotAsk is returned to the Wit page for a conversation that is not an ask one, or an approval or run not of the ask conversation it follows.

View Source
var ErrSignatureAccountGone = errors.New("signature account gone")

ErrSignatureAccountGone is final: the saver (saver.ts) drops the pending edit.

View Source
var ErrSignatureTooLarge = errors.New("the signature is too large")

ErrSignatureTooLarge is returned for a signature over maxSignatureBytes.

View Source
var ErrWrongPage = errors.New("this page may not call that")

ErrWrongPage is returned to a caller that is not a registered page of the role a method serves: a window, another role's page, or none.

View Source
var ReaderReload = mygo.NewEvent[ReaderBody]("readerBody")

ReaderReload tells a reader page its body changed (加载图片, a retry).

View Source
var WitStream = mygo.NewEvent[WitEvent]("wit")

WitStream carries Wit events to the window or page following them only (a Target). Each follows one conversation at a time: a run or Follow in another conversation replaces what it followed.

Functions

func ThemeVars

func ThemeVars(t theme.Tokens) map[string]string

ThemeVars maps tokens to shadcn's CSS variables, plus Wit's review marks (--ins-* and --del-*), the warning colours (--warning-*) and the floating input's shadow (--shadow-float: ShadowFloat in the light appearance, the FloatInset inner edge in the dark), the window material's colour (--window) and a card's shadow (--shadow-xs: ShadowXs, black in both). --radius is Radius.Lg; styles.css derives sm…4xl from it by fixed offsets, landing on the native 4/6/8/12/16/24/32.

Types

type Account

type Account struct {
	// ID and Email identify the account.
	ID    int64  `json:"id"`
	Email string `json:"email"`
}

Account is a sender the page can pick.

type Approval

type Approval struct {
	ID  int64 `json:"id"`
	Run int64 `json:"run"`
	// CallID is the tool call it answers ("skill" for a skill's edit).
	CallID  string `json:"callId"`
	Tool    string `json:"tool"`
	Summary string `json:"summary"`
	// State is pending, executing, done, failed, rejected or stale.
	State        string   `json:"state"`
	Error        string   `json:"error"`
	Destinations []string `json:"destinations"`
	// Args is the call's arguments, JSON text ("" for none).
	Args string `json:"args"`
	// SessionAllow: 本次对话都允许 can decide it (WitPage.DecideSession).
	SessionAllow bool `json:"sessionAllow"`
}

Approval is a call waiting for the user, or decided.

type AttachResult

type AttachResult struct {
	Change
	// TooLarge are the files over the size limit; Failed those that failed
	// for another reason (logged).
	TooLarge []string `json:"tooLarge"`
	Failed   []string `json:"failed"`
}

AttachResult is the outcome of adding files: a partial success is no error.

type Attachment

type Attachment struct {
	// ID names it to RemoveAttachment; Size is in bytes.
	ID       int64  `json:"id"`
	Filename string `json:"filename"`
	Size     int    `json:"size"`
}

Attachment is a file attached to the draft.

type BodyChange

type BodyChange struct {
	Change
	// Body is the editable HTML as stored.
	Body string `json:"body"`
}

BodyChange is the draft after a Wit action, read again: Body is its editable HTML as stored, which the page adopts with the Change at once.

type Change

type Change struct {
	// Revision and Attachments are the draft's after the change.
	Revision    int64        `json:"revision"`
	Attachments []Attachment `json:"attachments"`
	// Conflict means the draft changed elsewhere: nothing was done.
	Conflict bool `json:"conflict"`
	// Signature is the draft's signature as plain text, SignatureImages its
	// images as data: URLs.
	Signature       string   `json:"signature"`
	SignatureImages []string `json:"signatureImages"`
	// UpdatedAt is when the draft was last stored, in Unix milliseconds.
	UpdatedAt int64 `json:"updatedAt"`
}

Change is the outcome of an account switch or an attachment change.

type Citation

type Citation struct {
	N         int    `json:"n"`
	MessageID int64  `json:"messageId"`
	Subject   string `json:"subject"`
	From      string `json:"from"`
	// Date is in Unix milliseconds, 0 for none.
	Date int64 `json:"date"`
	// Suspect means it seems to hold instructions (wit.suspect).
	Suspect bool `json:"suspect"`
	// Quote is the suspect text from where it starts, at most 40 runes; mail text, shown as plain text.
	Quote string `json:"quote"`
}

Citation is a message Wit read, numbered N from 1 in the conversation (core.Citation.N).

type Compose

type Compose struct {
	// contains filtered or unexported fields
}

Compose keeps each compose window's session: its draft and the parts of the draft the page does not edit. It is not bound: pages reach it only through ComposePage.

func NewCompose

func NewCompose() *Compose

NewCompose is a Compose with no windows; SetService opens it up.

func (*Compose) Attach

func (c *Compose) Attach(winID int, draftID int64)

Attach ties draftID to window winID, as the window opens.

func (*Compose) Detach

func (c *Compose) Detach(winID int)

Detach forgets window winID's draft: new calls from it get ErrNoDraft; one already in progress finishes. Its Wit events stop.

func (*Compose) OnClosed

func (c *Compose) OnClosed(fn func(winID, gen int, saved bool))

OnClosed sets the hook that hears the page's final flush: gen is the close attempt it answers (the Closing event's payload), saved whether it succeeded.

func (*Compose) OnDone

func (c *Compose) OnDone(fn func(winID int))

OnDone sets the hook told that window winID's draft was sent or discarded: the window can close without asking.

func (*Compose) Release

func (c *Compose) Release(winID int)

Release lets go of closed window winID: new calls from it get ErrNoDraft, the call in progress finishes first, then its draft is deleted if it is empty (core DeleteDraftIfEmpty, on the revision checked). Its Wit events stop; a run goes on.

func (*Compose) SetService

func (c *Compose) SetService(svc *core.Service)

SetService gives Compose the service, once the data is open.

type ComposeEdit

type ComposeEdit struct {
	// To, Cc, Bcc, ReplyTo, Subject and Body (HTML) are the edited fields, as
	// in ComposeState; Revision is the one they were made against.
	To       []string `json:"to"`
	Cc       []string `json:"cc"`
	Bcc      []string `json:"bcc"`
	ReplyTo  string   `json:"replyTo"`
	Subject  string   `json:"subject"`
	Body     string   `json:"body"`
	Revision int64    `json:"revision"`
}

ComposeEdit is what the page saves: the fields it edits, and the revision they were made against.

type ComposePage

type ComposePage struct {
	// contains filtered or unexported fields
}

ComposePage is what compose pages call (mygo.Bind); every method acts on the calling window's draft only.

func NewComposePage

func NewComposePage(c *Compose, d PageDeps) *ComposePage

NewComposePage is the bound facade of c.

func (*ComposePage) AddAttachments

func (p *ComposePage) AddAttachments(ctx context.Context, revision int64) (AttachResult, error)

AddAttachments asks for files in a dialog on the calling window and adds each to its draft, if the draft is at revision (else Conflict, without the dialog). A file over the limit or failing is reported, not an error; a write from elsewhere stops the adding with Conflict, the files added so far in Attachments.

The dialog runs without the session lock: Release does not wait for it (AppKit gives no promise a sheet on a closed window ever ends), and once the window is released the files are dropped (ErrNoDraft).

func (*ComposePage) Ask

func (p *ComposePage) Ask(ctx context.Context, input string, quoted []int64) (int64, error)

Ask asks Wit about the calling window's draft, with the mail the user quoted (message IDs; core AssistWith), and returns the run's ID; its events go to the window on WitStream.

func (*ComposePage) Closed

func (p *ComposePage) Closed(ctx context.Context, gen int, saved bool) error

Closed is the page's final flush for the close attempt gen, the payload of the Closing event it answers: saved reports whether it succeeded.

func (*ComposePage) Decide

func (p *ComposePage) Decide(ctx context.Context, approval int64, accept bool) (BodyChange, error)

Decide accepts or rejects approval, any pending one of the calling window's draft (a draft edit or a tool's call). The window follows its conversation first, so the run it resumes streams on WitStream. A draft changed since the edit was proposed is Conflict.

func (*ComposePage) DecideAll

func (p *ComposePage) DecideAll(ctx context.Context, run int64, accept bool) (BodyChange, error)

DecideAll accepts or rejects every pending approval of run, one of the calling window's draft (core ResolveAll; 全部允许). As Decide.

func (*ComposePage) Finish

func (p *ComposePage) Finish(ctx context.Context, approval int64, decisions []int) (BodyChange, error)

Finish settles approval, an inline draft edit, with a decision per change (Review.Changes; 1 accepts, -1 rejects): all accepted or all rejected decides it; a mix applies the text the decisions make. As Decide.

func (*ComposePage) Follow

func (p *ComposePage) Follow(ctx context.Context) (WitHistory, error)

Follow makes the calling window follow its draft's conversation (a run going on, else an edit waiting, else the Assist one) and returns its history as read after subscribing. Events that come during the reads are held back and sent after: a run seen ending meanwhile shows as ended, read again. A run that ends later shows as Running: its end reaches the window after the reply, on the subscription, which goes on.

func (*ComposePage) Latest

func (p *ComposePage) Latest(ctx context.Context) (ComposeState, error)

Latest is the draft as stored now, as Load, for resolving a conflict: its quote and signature become the ones saves keep.

func (*ComposePage) Load

func (p *ComposePage) Load(ctx context.Context) (ComposeState, error)

Load is the calling window's draft, with the accounts, the theme and the page's copy.

func (p *ComposePage) OpenLink(ctx context.Context, link string) error

OpenLink opens a link from Wit's answer: an http or https one in the browser; a mailto one as a new draft to its addresses, from the calling window's account, in a compose window. Any other is ErrBadLink.

func (*ComposePage) OpenModels

func (p *ComposePage) OpenModels(ctx context.Context) error

OpenModels opens the model settings (the input's model chip).

func (*ComposePage) RemoveAttachment

func (p *ComposePage) RemoveAttachment(ctx context.Context, id, revision int64) (Change, error)

RemoveAttachment removes attachment id from the calling window's draft, if the draft is at revision (else Conflict).

func (*ComposePage) Review

func (p *ComposePage) Review(ctx context.Context, approval int64) (Review, error)

Review is approval, a draft edit of the calling window's draft waiting for a decision, as the page reviews it.

func (*ComposePage) Rewrite

func (p *ComposePage) Rewrite(ctx context.Context, revision int64, text string, start, end int, instruction string) (RunStart, error)

Rewrite rewrites [start, end) of the calling window's draft as instruction says (⌘K, core RewriteSelection). The page saved revision and sends its text and the selection in it in UTF-16 offsets (the plainText contract); a draft not at revision, or with another text, is Conflict. The run's events go to the window on WitStream.

func (*ComposePage) Save

Save stores the edit in the calling window's draft, keeping its quote and signature. A draft changed elsewhere is a Conflict, not an error.

func (*ComposePage) SearchMail

func (p *ComposePage) SearchMail(ctx context.Context, query string) ([]MailHit, error)

SearchMail is the quote picker's search (core Search, every account).

func (*ComposePage) Send

func (p *ComposePage) Send(ctx context.Context, revision int64, digest string) (SendResult, error)

Send sends exactly revision of the calling window's draft, else Conflict. digest, from an earlier SendResult's warnings, sends despite them. Once sent, the window closes (OnDone).

func (*ComposePage) SetAccount

func (p *ComposePage) SetAccount(ctx context.Context, id, revision int64) (Change, error)

SetAccount moves the calling window's draft to account id, whose signature replaces the old one; revision must be the stored one, else Conflict.

func (*ComposePage) Skill

func (p *ComposePage) Skill(ctx context.Context, id, language string) (int64, error)

Skill runs skill id on the calling window's draft (core AssistSkill); translate-draft writes in language ("" for the reading language). It returns the run's ID; its events go to the window on WitStream.

func (*ComposePage) Stop

func (p *ComposePage) Stop(ctx context.Context, run int64) error

Stop cancels run, which must be one of the calling window's draft.

func (*ComposePage) Undo

func (p *ComposePage) Undo(ctx context.Context) (BodyChange, error)

Undo takes back the latest edit Wit applied to the calling window's draft (core UndoLastAssist).

func (*ComposePage) WitInfo

func (p *ComposePage) WitInfo(ctx context.Context) (WitInfo, error)

WitInfo is what the Wit panel offers for the calling window's draft.

type ComposeState

type ComposeState struct {
	// DraftID is the window's draft; Accounts are the senders to pick from.
	DraftID  int64     `json:"draftId"`
	Accounts []Account `json:"accounts"`
	// From is the sending account's ID.
	From int64 `json:"from"`
	// To, Cc, Bcc, ReplyTo and Subject are the header fields as stored.
	To      []string `json:"to"`
	Cc      []string `json:"cc"`
	Bcc     []string `json:"bcc"`
	ReplyTo string   `json:"replyTo"`
	Subject string   `json:"subject"`
	// Body is the editable part as sanitized HTML.
	Body string `json:"body"`
	// Quote and Signature are read-only plain text; SignatureImages are the
	// signature's images as data: URLs, shown after its text.
	Quote           string   `json:"quote"`
	Signature       string   `json:"signature"`
	SignatureImages []string `json:"signatureImages"`
	// Attachments are the draft's files.
	Attachments []Attachment `json:"attachments"`
	// Revision is what the next save must pass; Theme styles the page.
	Revision int64 `json:"revision"`
	Theme    Theme `json:"theme"`
	// UpdatedAt is when the draft was last stored, in Unix milliseconds.
	UpdatedAt int64 `json:"updatedAt"`
	// Locale selects the shared catalog bundled with the page.
	Locale string `json:"locale"`
	// Wit is what the Wit panel offers, as WitInfo.
	Wit WitInfo `json:"wit"`
}

ComposeState is what a compose page shows when it opens.

type Language

type Language struct {
	Tag  string `json:"tag"`
	Name string `json:"name"`
}

Language is a 翻译 menu item.

type MailHit

type MailHit struct {
	ID      int64  `json:"id"`
	Subject string `json:"subject"`
	From    string `json:"from"`
	// Date is in Unix milliseconds; 0 for none.
	Date int64 `json:"date"`
}

MailHit is a message the quote picker offers.

type PageDeps

type PageDeps struct {
	// WinOf names the calling window (mygo.CallerWindow(ctx).ID() in production).
	WinOf func(context.Context) (int, bool)
	// PickFiles asks for files to attach, in a dialog on window winID; none
	// picked is no error.
	PickFiles func(ctx context.Context, winID int) ([]string, error)
	// Emit sends a Wit event to a window or page (WitStream.Emit or
	// EmitPage in production).
	Emit func(t Target, ev WitEvent)
	// OpenURL opens an http(s) link in the browser; OpenCompose opens draft
	// id in a compose window.
	OpenURL     func(url string) error
	OpenCompose func(draftID int64)
	// OpenModels opens the model settings in the main window.
	OpenModels func()
}

PageDeps is what ComposePage needs from the platform.

type Pages

type Pages struct {
	// contains filtered or unexported fields
}

Pages keeps the embedded pages and their roles. A page is known by an opaque key (its *mygo.Page in production) and gets an ID of its own.

func NewPages

func NewPages(pageOf func(context.Context) any) *Pages

NewPages is a registry with no pages; pageOf is the calling page's key (mygo.CallerPage(ctx) in production).

func (*Pages) Caller

func (p *Pages) Caller(ctx context.Context, role string) (int, error)

Caller is the calling page's ID, if it is a registered page of role; else ErrWrongPage.

func (*Pages) Key

func (p *Pages) Key(id int) (any, bool)

Key is the key page id was registered with, while it is.

func (*Pages) Register

func (p *Pages) Register(key any, role string) int

Register makes the page with key one of role and returns its ID, new on every call; Unregister it when the page is destroyed.

func (*Pages) Role

func (p *Pages) Role(key any) (string, bool)

Role is the role the page with key is registered as, while it is.

func (*Pages) Unregister

func (p *Pages) Unregister(key any)

Unregister forgets the page with key: its calls get ErrWrongPage.

type Reader

type Reader struct {
	// contains filtered or unexported fields
}

Reader keeps the reader pages (role "reader"), each bound to a message: its body, version and images. It is not bound: the pages reach it only through ReaderPage; native calls Bind, Unbind and Allow.

func NewReader

func NewReader(pages *Pages, d ReaderDeps) *Reader

NewReader keeps the reader pages of pages; SetService opens it up.

func (*Reader) Allow

func (r *Reader) Allow(message int64)

Allow lets message's remote images load (加载图片), or loads its body again (a retry): its pages get the new body, with Remote, by Emit. A message no page shows is left alone. It returns at once: the body is read on a goroutine.

func (*Reader) Bind

func (r *Reader) Bind(key any, message int64)

Bind makes the page with key (registered as "reader") show message; a page shows one message at a time.

func (*Reader) Bound

func (r *Reader) Bound(key any) (message int64, ok bool)

Bound is the message the page with key shows, while it is bound.

func (*Reader) ImageHandler

func (r *Reader) ImageHandler() http.Handler

ImageHandler is the image proxy the pages load remote images from. It serves the image of a token of a message a page shows and whose images may load (Remote), from the cache: downloaded once through core's client (public hosts only, no cookies, no Referer), kept on disk, shared by messages.

func (*Reader) SetService

func (r *Reader) SetService(svc *core.Service)

SetService gives the pages the service, once the data is open.

func (*Reader) Translate added in v1.2.0

func (r *Reader) Translate(message int64, texts map[string]string)

Translate displays translated text in the original body; nil restores the original.

func (*Reader) Unbind

func (r *Reader) Unbind(key any)

Unbind forgets the page with key; the last page of a message takes its state along (allowed images, tokens).

type ReaderBody

type ReaderBody struct {
	Message int64 `json:"message"`
	// Version orders the bodies of a page: it applies only a newer one, so
	// a Load that resolves after a ReaderReload is dropped.
	Version int64 `json:"version"`
	// HTML is the sanitized body (remote images kept when Remote), its
	// inline images within the message's budget and its structure within
	// the limits; "" when the mail has no HTML, which Plain then holds.
	HTML  string `json:"html"`
	Plain string `json:"plain"`
	// Remote is that the message's remote images may load (the setting, or
	// the user's 加载图片): HTML holds them as godwit-img: URLs, which the
	// image proxy (ImageHandler) serves.
	Remote bool `json:"remote"`
	// Blocked is the remote images left out while not Remote.
	Blocked int `json:"blocked"`
	// Cut is that the body is only its start: the Service kept only that,
	// or the limits cut it.
	Cut    bool   `json:"cut"`
	Theme  Theme  `json:"theme"`
	Locale string `json:"locale"`
}

ReaderBody is a message's body as the reader page shows it.

type ReaderDeps

type ReaderDeps struct {
	// Emit sends a body to the page with key page (ReaderReload.EmitPage,
	// which only queues it). It runs with the Reader's lock held, so the
	// page is bound to b's message as it is sent: it must not block or call
	// back into the Reader.
	Emit func(page any, b ReaderBody)
	// OnHeight is the height of message's body in page, in CSS pixels.
	OnHeight func(page any, message int64, px int)
	// OnWheel is a wheel delta page's body did not use: the reader scrolls.
	OnWheel func(page any, dy float64)
	// OnLink opens a link of page's body (the native confirmation; mailto
	// composes).
	OnLink func(page any, url string)
	// OnNavigate goes back (-1) or forward (1), as ⌘[ and ⌘] do natively.
	OnNavigate func(page any, dir int)
	// OnSearch opens the mail list's search, as ⌘F does natively.
	OnSearch func(page any)
	// OnBuilt is told, for each page bound to b.Message, each body built
	// as the message's current version: native shows the truncated notice
	// by b.Cut and hides the remote-image notice once b.Remote. It runs
	// with the Reader's lock held, as Emit does (so a page is told the
	// bodies in their versions' order): it must not block or call back.
	OnBuilt func(page any, b ReaderBody)
	// OnFailed is told, for each page bound to message, that the body
	// Allow read failed: native offers 加载图片 again. Under the lock too.
	OnFailed func(page any, message int64, err error)
}

ReaderDeps is what Reader needs from the platform. The callbacks post to the UI thread themselves. Each names the page (its key) it is about, bound as the call was made: by the time the post runs the page may be closed or rebound, which the UI thread checks.

type ReaderPage

type ReaderPage struct {
	// contains filtered or unexported fields
}

ReaderPage is what a reader page (role "reader", one per expanded message) calls (mygo.Bind). Each acts on the caller's own message.

func NewReaderPage

func NewReaderPage(r *Reader) *ReaderPage

NewReaderPage is the bound facade of r.

func (*ReaderPage) Height

func (p *ReaderPage) Height(ctx context.Context, px int) error

Height is the body's height in CSS pixels, for the native layout.

func (*ReaderPage) Load

func (p *ReaderPage) Load(ctx context.Context) (ReaderBody, error)

Load is the caller's message's body, as of its version.

func (*ReaderPage) Navigate

func (p *ReaderPage) Navigate(ctx context.Context, dir int) error

Navigate goes back (-1) or forward (1), as ⌘[ and ⌘] do natively.

func (p *ReaderPage) OpenLink(ctx context.Context, link string) error

OpenLink opens a link of the body (its data-href) natively: an http or https one after the confirmation, a mailto one in a compose window. Any other is ErrBadLink.

func (*ReaderPage) Search

func (p *ReaderPage) Search(ctx context.Context) error

Search opens the mail list's search, as ⌘F does natively: a page that has the keyboard misses the native shortcut.

func (*ReaderPage) Wheel

func (p *ReaderPage) Wheel(ctx context.Context, dy float64) error

Wheel is a vertical wheel delta the body did not use: the reader scrolls by it.

type Review

type Review struct {
	Approval int64  `json:"approval"`
	Tool     string `json:"tool"`
	// Inline means the changes are shown and decided word by word (Finish);
	// else it is a rewrite, decided as a whole (Decide).
	Inline bool `json:"inline"`
	// Ratio is the share of words changed; Exact means the diff completed
	// within its budget, so Ratio is known (else wit.compose.rewrite_plain).
	Ratio float64 `json:"ratio"`
	Exact bool    `json:"exact"`
	// Blocks are the new text with the changes marked, when the block shapes are kept (Inline); Changes
	// is how many changes they hold, the length Finish wants.
	Blocks  []ReviewBlock `json:"blocks"`
	Changes int           `json:"changes"`
	// Old and New are the editable HTML before and after, for the compare.
	Old string `json:"old"`
	New string `json:"new"`
}

Review is a pending draft edit as the compose page reviews it.

type ReviewBlock

type ReviewBlock struct {
	// Kind is paragraph, heading, quote, listItem or pre; Level and Ordered
	// as in the editor (heading level, quote or list depth; an <ol> item).
	Kind    string `json:"kind"`
	Level   int    `json:"level"`
	Ordered bool   `json:"ordered"`
	// Added and Removed mark a whole block added or removed: change Whole
	// (else -1), even when it is empty.
	Added   bool       `json:"added"`
	Removed bool       `json:"removed"`
	Whole   int        `json:"whole"`
	Ops     []ReviewOp `json:"ops"`
}

ReviewBlock is a block of the review.

type ReviewOp

type ReviewOp struct {
	// Kind is same, del or ins; Change the change it belongs to, -1 for same.
	Kind   string        `json:"kind"`
	Change int           `json:"change"`
	Tokens []ReviewToken `json:"tokens"`
	// Link, on the last op of a change of only a link's address, is the new
	// address (wit.compose.link).
	Link string `json:"link"`
}

ReviewOp is a run of words kept, deleted or inserted.

type ReviewToken

type ReviewToken struct {
	Text   string `json:"text"`
	Bold   bool   `json:"bold"`
	Italic bool   `json:"italic"`
	Href   string `json:"href"`
}

ReviewToken is text in one style.

type RunStart

type RunStart struct {
	Run          int64 `json:"run"`
	Conversation int64 `json:"conversation"`
	Revision     int64 `json:"revision"`
	Conflict     bool  `json:"conflict"`
}

RunStart is a ⌘K run started, or Conflict: the draft is not at the revision, or its text is not the text the page sent. Revision is the draft's stored revision. For WitPage.Ask, Conversation is the run's conversation (made for a new one).

type SaveResult

type SaveResult struct {
	// Revision is the draft's new revision, to pass to the next save.
	Revision int64 `json:"revision"`
	// Conflict means the draft changed elsewhere: nothing was saved.
	Conflict bool `json:"conflict"`
}

SaveResult is the outcome of a save.

type SendResult

type SendResult struct {
	// Sent means it is in the outbox and the window closes.
	Sent bool `json:"sent"`
	// Conflict means the draft is not at the revision sent: nothing was sent.
	Conflict bool `json:"conflict"`
	// Warnings, with Digest, need the user's go-ahead: Send again with Digest.
	Warnings []SendWarning `json:"warnings"`
	Digest   string        `json:"digest"`
	// Blocked is the plugin that refused the draft, and its message.
	Blocked *SendWarning `json:"blocked"`
}

SendResult is the outcome of a send.

type SendWarning

type SendWarning struct {
	// Plugin is its name; Message what it said.
	Plugin  string `json:"plugin"`
	Message string `json:"message"`
}

SendWarning is a plugin's word about the draft.

type Signature

type Signature struct {
	// contains filtered or unexported fields
}

Signature keeps the signature page's state (role "signature", embedded in the account settings): the account native shows and the flushes waiting for the page. It is not bound: the page reaches it only through SignaturePage.

func NewSignature

func NewSignature(pages *Pages, pick func(ctx context.Context) (string, error)) *Signature

NewSignature is the signature page's state; SetService opens it up. pick asks for one image file, in a dialog on the calling page's window: its path, "" for none.

func (*Signature) Close

func (s *Signature) Close()

Close forgets the page (its view closing): the flushes waiting get no answer, and later ones answer at once until a page runs again.

func (*Signature) Flush

func (s *Signature) Flush(emit func(token int64), done func(ok bool))

Flush asks the page to save now through emit (SignatureFlush); done hears its answer, once, on the page's goroutine. A page that never ran holds no edit: done(true) at once.

func (*Signature) SetService

func (s *Signature) SetService(svc *core.Service)

SetService gives the page the service, once the data is open.

func (*Signature) Show

func (s *Signature) Show(account int64)

Show records the account native shows, for a page that loads after the SignatureAccount event went out.

type SignaturePage

type SignaturePage struct {
	// contains filtered or unexported fields
}

SignaturePage is what the signature page calls (mygo.Bind).

func NewSignaturePage

func NewSignaturePage(s *Signature) *SignaturePage

NewSignaturePage is the bound facade of s.

func (*SignaturePage) Load

func (p *SignaturePage) Load(ctx context.Context, account int64) (string, error)

Load is account's signature as the editor's HTML: sanitized, a plain one escaped into a paragraph, its images as data: URLs.

func (*SignaturePage) PickImage

func (p *SignaturePage) PickImage(ctx context.Context) (string, error)

PickImage asks for an image to insert and returns it as a data: URL, "" for none picked: a png, jpeg, gif or webp (by its content) of at most 512 KB, else core.ErrSignatureImage.

func (*SignaturePage) Save

func (p *SignaturePage) Save(ctx context.Context, account int64, html string) error

Save stores html as account's signature, marked as editor HTML ("" for none); drafts sanitize it as they take it. Its data: images become the account's signature images (core.ErrSignatureImage, ErrSignatureImageCount).

func (*SignaturePage) Saved

func (p *SignaturePage) Saved(ctx context.Context, token int64, ok bool) error

Saved answers the flush token: ok is whether the page's save succeeded.

func (*SignaturePage) State

State is the account shown, the locale and the theme.

type SignatureState

type SignatureState struct {
	Account int64  `json:"account"`
	Locale  string `json:"locale"`
	Theme   Theme  `json:"theme"`
}

SignatureState is what the signature page needs besides a signature: the account shown (0 before native showed one), its locale and theme.

type Skill

type Skill struct {
	ID    string `json:"id"`
	Label string `json:"label"`
	// Hint is what the skill changes, shown beside its label.
	Hint string `json:"hint"`
}

Skill is a 调整 menu item.

type Target

type Target struct {
	Window int
	Page   int
}

Target is where Wit events go: a window or a registered page. Exactly one is set; page IDs come from Pages.Register.

type Theme

type Theme struct {
	// Mode is the user's choice: "system", "light" or "dark".
	Mode string `json:"mode"`
	// Light and Dark map CSS variable names to values for each appearance.
	Light map[string]string `json:"light"`
	Dark  map[string]string `json:"dark"`
	// FontSize is the interface font size in CSS pixels.
	FontSize float32 `json:"fontSize"`
	// BodyFontSize is the mail body font size in CSS pixels (the reader's).
	BodyFontSize float32 `json:"bodyFontSize"`
}

Theme is what a web page needs to look like the native UI: the CSS variables of both appearances (the page picks by Mode, following the system's for "system"), the interface font size and the mail body's.

func ThemeOf

func ThemeOf(st core.Settings) Theme

ThemeOf is the theme for the user's settings.

type Window

type Window interface {
	ID() int
	Focus()
	// Destroy closes the window without asking again.
	Destroy()
	// EmitClosing tells the page to flush and call Closed with gen.
	EmitClosing(gen int)
}

Window is one compose window, as Windows drives it.

type Windows

type Windows struct {

	// OnChange, when set (before any window opens), is called after a window
	// registers and after a closed window's release, the empty-draft delete
	// included: IsOpen has changed. Never under mu.
	OnChange func()
	// contains filtered or unexported fields
}

Windows opens one compose window per draft and lets none close or quit with input unsaved: a window closes once its page reports its last input saved, or the user chose to close it anyway. No timeout closes one.

func NewWindows

func NewWindows(c *Compose, newWindow func(draftID int64) Window, ask func(w Window) bool) *Windows

NewWindows opens windows with newWindow. ask shows 「有修改没有保存」 for a window whose last input could not be saved: true closes it anyway. Windows hears the pages' final flush through c.OnClosed.

func (*Windows) CloseAll

func (ws *Windows) CloseAll(done func(ok bool))

CloseAll closes every window as RequestClose does, for quitting; done reports, once every one has gone or been kept, whether all went. A CloseAll while one is in progress joins it: only the first done runs.

func (*Windows) CloseDraft

func (ws *Windows) CloseDraft(draftID int64)

CloseDraft destroys draftID's window, if one is open, as Done: the draft was deleted elsewhere.

func (*Windows) Closed

func (ws *Windows) Closed(winID int)

Closed is told when window winID went, on mygo's main thread: it returns at once, and its page's session is released (Compose.Release, which waits for a call in progress and deletes an empty draft) on another goroutine. The window counts as open, and a CloseAll waits for it, until that release is done, so a quit never exits in the middle of it.

func (*Windows) Count

func (ws *Windows) Count() int

Count is the number of open windows.

func (*Windows) Done

func (ws *Windows) Done(winID int)

Done destroys window winID now, without a flush or a question: its draft was sent or discarded. A close or quit waiting for it settles once it has gone.

func (*Windows) IsOpen

func (ws *Windows) IsOpen(draftID int64) bool

IsOpen reports whether draftID's window is open or being made.

func (*Windows) Open

func (ws *Windows) Open(draftID int64)

Open shows draftID's window, opening it unless it is open already. While the draft's closed window is still being released, the Open waits for that: it opens then, unless the release deleted the draft as empty. While the window is being made, the Open joins that: it is focused once made.

func (*Windows) RequestClose

func (ws *Windows) RequestClose(winID int)

RequestClose starts closing window winID, from its OnClose (always prevented): the page flushes, then the window closes once the page reports saved, or asks on a failed save or after 5 s without an answer. A window already closing goes on as it was: one flush, one question.

type Wit

type Wit struct {
	// contains filtered or unexported fields
}

Wit keeps the Wit page's state: the route's conversation as a versioned snapshot and the page's subscription. It is not bound: the page reaches it only through WitPage; native calls SetConversation and SettingsChanged.

func NewWit

func NewWit(pages *Pages, d WitPageDeps) *Wit

NewWit is the Wit page's state for the pages of role "wit"; SetService opens it up.

func (*Wit) Handoff

func (w *Wit) Handoff(query string)

Handoff gives the page a question to ask (WitAsk): the page starts the run through WitPage.Ask, so it follows it and Stop works. Native navigates to a new conversation first.

func (*Wit) SetConversation

func (w *Wit) SetConversation(id int64) int64

SetConversation is the route's conversation (0: a new one), as native navigates: a new snapshot version, sent to the page (WitOpen) and returned.

func (*Wit) SetCreated

func (w *Wit) SetCreated(id int64) int64

SetCreated is SetConversation for native taking the conversation the page's Ask made (ConversationCreated): WitOpen says Created, so the page's first question goes on in it.

func (*Wit) SetService

func (w *Wit) SetService(svc *core.Service)

SetService gives the page the service, once the data is open.

func (*Wit) SettingsChanged

func (w *Wit) SettingsChanged(st core.Settings)

SettingsChanged sends the page its state for st (WitState).

type WitCall

type WitCall struct {
	ID   string `json:"id"`
	Name string `json:"name"`
	Args string `json:"args"`
}

WitCall is a tool call; Args is its JSON text.

type WitEvent

type WitEvent struct {
	// Conversation and Run are the conversation and the run it belongs to.
	Conversation int64 `json:"conversation"`
	Run          int64 `json:"run"`
	// Kind is context, text, tool_call, tool_result, interrupted, done or error.
	Kind string `json:"kind"`
	// Text is a text delta.
	Text string `json:"text"`
	// Call is a tool_call's call; Result a tool_result's result.
	Call   *WitCall   `json:"call"`
	Result *WitResult `json:"result"`
	// Interrupts are the calls an interrupt waits on; a skill's has ID
	// "skill" and no tool_call before it.
	Interrupts []WitCall `json:"interrupts"`
	// Citations are the mail shown to the model (N is 0: the page numbers
	// them after the history's); Approvals an interrupt's, in call order.
	Citations []Citation `json:"citations"`
	Approvals []Approval `json:"approvals"`
	// Error is an error's i18n key; "" for a stop.
	Error string `json:"error"`
}

WitEvent is one event of a Wit run, as core.WitEvent.

type WitHistory

type WitHistory struct {
	// Conversation is 0 when the draft has none yet.
	Conversation int64        `json:"conversation"`
	Messages     []WitMessage `json:"messages"`
	// Running is the run going on (not one waiting for a decision), else 0.
	Running int64 `json:"running"`
	// Approvals are every approval of the conversation, in any state.
	Approvals []Approval `json:"approvals"`
}

WitHistory is a conversation as the Wit panel shows it.

type WitInfo

type WitInfo struct {
	// Enabled means the panel shows: a model is configured, or a plugin translates.
	Enabled bool `json:"enabled"`
	// TranslateOnly: no model, but a plugin translates. The panel offers its
	// 翻译 menu alone: no input, no 调整, no chips.
	TranslateOnly bool `json:"translateOnly"`
	// Skills are the 调整 menu's; Languages the 翻译 menu's, the reading
	// language first.
	Skills    []Skill    `json:"skills"`
	Languages []Language `json:"languages"`
	// Chips name what Wit knows of the draft.
	Chips []string `json:"chips"`
	// Model is the default model's label, as the input's chip.
	Model string `json:"model"`
}

WitInfo is what the Wit panel offers for the draft.

type WitMessage

type WitMessage struct {
	ID string `json:"id"`
	// Role is user or assistant; an answer's Text is Markdown.
	Role      string      `json:"role"`
	Text      string      `json:"text"`
	Calls     []WitCall   `json:"calls"`
	Results   []WitResult `json:"results"`
	Citations []Citation  `json:"citations"`
	// Runs are the runs that added it (a question: the run it started).
	Runs []int64 `json:"runs"`
}

WitMessage is a question, or the answer to one: everything the model said and called up to the next question.

type WitOpenMsg

type WitOpenMsg struct {
	Conversation int64 `json:"conversation"`
	Version      int64 `json:"version"`
	Created      bool  `json:"created"`
}

WitOpenMsg tells the Wit page to show Conversation (0: a new one), the snapshot at Version. Created marks native taking the conversation the page's Ask made (SetCreated): the page's first question goes on in it, no switch.

type WitPage

type WitPage struct {
	// contains filtered or unexported fields
}

WitPage is what the Wit page (role "wit", embedded in the main window) calls (mygo.Bind). It acts on ask conversations only.

func NewWitPage

func NewWitPage(w *Wit) *WitPage

NewWitPage is the bound facade of w.

func (*WitPage) Ask

func (p *WitPage) Ask(ctx context.Context, conv int64, input string, quoted []int64) (RunStart, error)

Ask asks input in conv, an ask conversation, with the mail the user quoted (message IDs; core AskWith); conv 0 makes one first, which ConversationCreated hears of. The page follows conv from before the run starts, unless a newer selection came meanwhile. Only Stop cancels the run: a page reload does not.

func (*WitPage) Decide

func (p *WitPage) Decide(ctx context.Context, approval int64, accept bool) error

Decide accepts or rejects approval, one of the conversation the page follows: the run it resumes streams on that subscription.

func (*WitPage) DecideAll

func (p *WitPage) DecideAll(ctx context.Context, run int64, accept bool) error

DecideAll accepts or rejects every pending approval of run, one of the conversation the page follows (全部允许). As Decide.

func (*WitPage) DecideSession

func (p *WitPage) DecideSession(ctx context.Context, approval int64) error

DecideSession accepts approval and allows its tool for the rest of the conversation (本次对话都允许; core ResolveApprovalForConversation). As Decide.

func (*WitPage) Follow

func (p *WitPage) Follow(ctx context.Context, conv int64) (WitHistory, error)

Follow makes the page follow conv, an ask conversation (0: none), and returns its history as read after subscribing (follow.read). The newest selection wins: an older Follow or Ask no longer subscribes.

func (*WitPage) Load

func (p *WitPage) Load(ctx context.Context) (WitPageState, error)

Load is the page's state: the snapshot, the model, the copy and the theme.

func (*WitPage) Navigate

func (p *WitPage) Navigate(ctx context.Context, dir int) error

Navigate goes back (-1) or forward (1), as ⌘[ and ⌘] do natively.

func (*WitPage) OpenCited

func (p *WitPage) OpenCited(ctx context.Context, message int64) error

OpenCited opens cited message id in the reader.

func (p *WitPage) OpenLink(ctx context.Context, link string) error

OpenLink opens a link from Wit's answer: an http or https one in the browser; a mailto one as a new draft to its addresses, from the default account, in a compose window. Any other is ErrBadLink.

func (*WitPage) OpenModels

func (p *WitPage) OpenModels(ctx context.Context) error

OpenModels opens the model settings.

func (*WitPage) SearchMail

func (p *WitPage) SearchMail(ctx context.Context, query string) ([]MailHit, error)

SearchMail is the quote picker's search (core Search, every account).

func (*WitPage) Stop

func (p *WitPage) Stop(ctx context.Context, run int64) error

Stop cancels run: one the page's Ask started (a Stop pressed before Ask returned may come after a switch), or one of the conversation it follows.

type WitPageDeps

type WitPageDeps struct {
	// Emit sends a Wit event to the page (WitStream.EmitPage in production).
	Emit func(t Target, ev WitEvent)
	// Open and State send WitOpen and WitState to the page.
	Open  func(WitOpenMsg)
	State func(WitPageState)
	// Ask sends WitAsk to the page.
	Ask func(query string)
	// OpenMessage opens message id in the reader; OpenModels the model
	// settings; Navigate goes back (-1) or forward (1).
	OpenMessage func(id int64)
	OpenModels  func()
	Navigate    func(dir int)
	// ConversationCreated is told Ask made conversation id, asked under
	// snapshot version.
	ConversationCreated func(id, version int64)
	// OpenURL opens an http(s) link in the browser; OpenCompose opens draft
	// id in a compose window.
	OpenURL     func(url string) error
	OpenCompose func(draftID int64)
}

WitPageDeps is what WitPage needs from the platform. The native callbacks post to the UI thread.

type WitPageState

type WitPageState struct {
	// Conversation is the route's (0: a new one) as of Version, the
	// snapshot's: the page applies only newer ones (WitOpen).
	Conversation int64 `json:"conversation"`
	Version      int64 `json:"version"`
	// Model is the default model's label; "" for none.
	Model  string `json:"model"`
	Locale string `json:"locale"`
	Theme  Theme  `json:"theme"`
}

WitPageState is what the Wit page shows besides the conversation.

type WitResult

type WitResult struct {
	CallID string `json:"callId"`
	Name   string `json:"name"`
	Output string `json:"output"`
	Error  string `json:"error"`
}

WitResult is a tool call's result: Output JSON text, or Error.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL