package web import ( "context" "net/url" "sourcecraft.dev/bigbes/sr-ht-dolt/browse" "sourcecraft.dev/bigbes/sr-ht-dolt/core" ) // View is a specialized, read-only rendering of a repository whose table shape // it recognizes. Views are registered at init time via RegisterView and chosen // per-repo by Applies. The generic table browser is always available too, so a // View never has to be exhaustive. // // A View pulls all of its data through the BrowseSession surface only (Tables, // Rows, Branches, Log, ...); there is no SQL engine behind a bare store. Build // returns an opaque value handed to the view's Template as its .Data field. type View interface { Name() string // URL slug, e.g. "beads"; must be a valid path segment, unique Label() string // human tab label, e.g. "Beads" Template() string // page template filename in templates/, e.g. "beads.html" Applies(tables []browse.TableInfo) bool // fingerprint by table names/columns // Build produces the opaque .Data value for the view's Template. query is the // request URL's query string, so a view can offer sub-modes (e.g. beads' // ?issue= detail pane) without a new route. Build(ctx context.Context, sess BrowseSession, repo *core.Repo, ref string, query url.Values) (any, error) } // registeredViews is the package-global registry populated at init time by // RegisterView. It is read once per process: Register snapshots it into // app.views so handlers (and tests, which set app.views directly) never touch // the global at request time. // // Nothing here parses a view's Template() any more: pages discovers every page // in templates/, so a view's page is registered by its file existing. A view // whose Template() names no such file renders as a 500 with a log line, which // is what it is. var registeredViews []View // RegisterView adds v to the global registry. It is meant to be called from an // init() in the file that defines a concrete view. If a view with the same // Name() is already registered it is replaced, so double registration (e.g. // from a duplicated init) is safe. func RegisterView(v View) { for i, existing := range registeredViews { if existing.Name() == v.Name() { registeredViews[i] = v return } } registeredViews = append(registeredViews, v) } // applicableViews returns the subset of views whose Applies reports true for // tables, preserving registration order. It is pure: handlers pass their own // snapshot (app.views) so the result is deterministic and testable. func applicableViews(views []View, tables []browse.TableInfo) []View { out := make([]View, 0, len(views)) for _, v := range views { if v.Applies(tables) { out = append(out, v) } } return out }