From 135e32db16986e577c7a0ad4a3e2cada305053ec Mon Sep 17 00:00:00 2001 From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com> Date: Thu, 7 May 2026 14:53:52 +0800 Subject: [PATCH 01/26] feat(base-path): wire BasePath through Server/templates/dochtml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 加 -base-path flag 让 pkgsite 整站可挂在 URL 子路径下(如 -base-path=/gogodocs,站点入口 http://host/gogodocs/)。空 = 默认挂根 路径,跟上游零差异。需求来自反代 / 子路径部署场景。 主要改动: - cmd/pkgsite/main.go:加 -base-path flag + validateBasePath(/foo 形式,不带尾斜杠);ServerConfig.BasePath 透传 - cmd/internal/pkgsite/server.go:ServerConfig + newServer 增加 BasePath, 传给 frontend.NewServer - internal/frontend/server.go:ServerConfig + Server 加 basePath 字段; Install() 内 shadow handle 入参注入 prefixPattern 自动给所有 mux pattern 加前缀("/static/" → "/gogodocs/static/"),下面的具体路由 声明完全不动;staticHandler / 内部 StripPrefix(third_party / sitemap / files / detail-stats / search-stats)手动加 s.basePath 让 file server 能剥到正确剩余 path - internal/frontend/templates/templates.go:新增 abs / basepath template helper,funcsWithBasePath 在内置 funcs 上叠 closure 实现 - internal/godoc/dochtml/template.go:LoadTemplates 接受 basePath 参数, 存包级 var;BasePath() 暴露给读取(dochtml 单进程使用,包级 state OK) - internal/godoc/dochtml/dochtml.go:godoc cross-reference 链接生成 (PackageURL func)拼上 basePath,让 std lib / 同模块 import 链接在 反代环境正确路由 - internal/godoc/dochtml/{dochtml,symbol}_test.go:LoadTemplates 调用 补空 basePath 参数 - static/**/*.{tmpl,md}:批量把 hardcoded 绝对路径改成 abs / basepath: - "/static/foo.svg" → "{{abs `/static/foo.svg`}}" - "/search?q=..." → "{{abs `/search`}}?q=..." - "/{{.Path}}" → "{{basepath}}/{{.Path}}" - "/" → "{{abs `/`}}" - "/vuln/{{.ID}}" → "{{basepath}}/vuln/{{.ID}}" 约 150 处 mechanical 替换;空 basePath 时 abs / basepath 都返原样 后续 patch 还要做:Go 端 http.Redirect 5 处字面量 / inline HTML in serrors.go / TS 端 ~10 处 + bundle rebuild。 --- cmd/internal/pkgsite/server.go | 9 ++- cmd/pkgsite/main.go | 24 +++++++ internal/frontend/server.go | 68 ++++++++++++++++--- internal/frontend/templates/templates.go | 34 +++++++++- internal/godoc/dochtml/dochtml.go | 5 +- internal/godoc/dochtml/dochtml_test.go | 6 +- internal/godoc/dochtml/symbol_test.go | 2 +- internal/godoc/dochtml/template.go | 16 ++++- static/frontend/_modals.tmpl | 4 +- static/frontend/about/about.tmpl | 10 +-- static/frontend/badge/badge.tmpl | 12 ++-- static/frontend/error/error.tmpl | 2 +- static/frontend/fetch/fetch.tmpl | 6 +- static/frontend/frontend.tmpl | 10 +-- static/frontend/homepage/homepage.tmpl | 16 ++--- static/frontend/search-help/search-help.tmpl | 8 +-- static/frontend/search/search.tmpl | 30 ++++---- static/frontend/subrepo/subrepo.tmpl | 46 ++++++------- static/frontend/unit/_header.tmpl | 30 ++++---- .../frontend/unit/importedby/importedby.tmpl | 4 +- static/frontend/unit/imports/imports.tmpl | 8 +-- static/frontend/unit/licenses/licenses.tmpl | 4 +- static/frontend/unit/main/_build-context.tmpl | 2 +- static/frontend/unit/main/_directories.tmpl | 4 +- static/frontend/unit/main/_doc.tmpl | 4 +- static/frontend/unit/main/_files.tmpl | 2 +- static/frontend/unit/main/_meta.tmpl | 20 +++--- static/frontend/unit/main/_readme.tmpl | 2 +- static/frontend/unit/main/main.tmpl | 6 +- static/frontend/unit/unit.tmpl | 2 +- static/frontend/unit/versions/versions.tmpl | 6 +- static/frontend/vuln/entry/entry.tmpl | 12 ++-- static/frontend/vuln/list/list.tmpl | 16 ++--- static/frontend/vuln/main/main.tmpl | 8 +-- static/frontend/vuln/vuln.tmpl | 4 +- static/shared/breadcrumb/breadcrumb.md | 2 +- static/shared/clipboard/clipboard.md | 6 +- static/shared/footer/footer.tmpl | 16 ++--- static/shared/form/form.md | 12 ++-- static/shared/gopher/gopher.tmpl | 2 +- static/shared/header/header.tmpl | 66 +++++++++--------- static/shared/message/message.md | 6 +- static/shared/modal/modal.md | 6 +- static/shared/tooltip/tooltip.md | 2 +- static/shared/vuln/vuln.tmpl | 4 +- static/worker/excluded.tmpl | 2 +- static/worker/index.tmpl | 28 ++++---- static/worker/versions.tmpl | 4 +- 48 files changed, 362 insertions(+), 236 deletions(-) diff --git a/cmd/internal/pkgsite/server.go b/cmd/internal/pkgsite/server.go index d1fb19d79..81e610a76 100644 --- a/cmd/internal/pkgsite/server.go +++ b/cmd/internal/pkgsite/server.go @@ -44,6 +44,10 @@ type ServerConfig struct { GoDocMode bool RecordCodeWikiMetrics frontend.RecordClickFunc + // BasePath:URL 前缀(如 "/gogodocs")让站点挂在子路径下。空 = 挂根。 + // 详见 cmd/pkgsite/main.go 的 -base-path flag。 + BasePath string + Proxy *proxy.Client // client, or nil; controlled by the -proxy flag } @@ -115,7 +119,7 @@ func BuildServer(ctx context.Context, serverCfg ServerConfig) (*frontend.Server, return allModules[i].ModulePath < allModules[j].ModulePath }) - return newServer(getters, allModules, cfg.proxy, serverCfg.GoDocMode, serverCfg.DevMode, serverCfg.DevModeStaticDir) + return newServer(getters, allModules, cfg.proxy, serverCfg.GoDocMode, serverCfg.DevMode, serverCfg.DevModeStaticDir, serverCfg.BasePath) } // getModuleDirs returns the set of workspace modules for each directory, @@ -276,7 +280,7 @@ func buildGetters(ctx context.Context, cfg getterConfig) ([]fetch.ModuleGetter, return getters, nil } -func newServer(getters []fetch.ModuleGetter, localModules []frontend.LocalModule, prox *proxy.Client, goDocMode bool, devMode bool, staticFlag string) (*frontend.Server, error) { +func newServer(getters []fetch.ModuleGetter, localModules []frontend.LocalModule, prox *proxy.Client, goDocMode bool, devMode bool, staticFlag string, basePath string) (*frontend.Server, error) { lds := fetchdatasource.Options{ Getters: getters, ProxyClientForLatest: prox, @@ -307,6 +311,7 @@ func newServer(getters []fetch.ModuleGetter, localModules []frontend.LocalModule LocalMode: true, LocalModules: localModules, ThirdPartyFS: thirdparty.FS, + BasePath: basePath, }) if err != nil { return nil, err diff --git a/cmd/pkgsite/main.go b/cmd/pkgsite/main.go index 49b99a597..c32211f9b 100644 --- a/cmd/pkgsite/main.go +++ b/cmd/pkgsite/main.go @@ -75,6 +75,10 @@ var ( goRepoPath = flag.String("gorepo", "", "path to Go repo on local filesystem") useProxy = flag.Bool("proxy", false, "fetch from GOPROXY if not found locally") openFlag = flag.Bool("open", false, "open a browser window to the server's address") + // basePath:把整个站点挂在 URL 子路径下(如 -base-path=/gogodocs, + // 站点入口 http://host/gogodocs/)。空字符串 = 默认挂根路径,跟上游一致。 + // fork 加入这个 flag 是为了让 pkgsite 能跟主网关共用域名(反代而非 subdomain)。 + basePath = flag.String("base-path", "", "URL prefix to mount the site under (e.g. /gogodocs). Must start with / and not end with /.") // other flags are bound to ServerConfig below ) @@ -97,10 +101,15 @@ func main() { } flag.Parse() + if err := validateBasePath(*basePath); err != nil { + dief("%v", err) + } + serverCfg.UseLocalStdlib = true serverCfg.GoRepoPath = *goRepoPath serverCfg.Paths = collectPaths(flag.Args()) serverCfg.RecordCodeWikiMetrics = nil + serverCfg.BasePath = *basePath if serverCfg.UseCache || *useProxy { fmt.Fprintf(os.Stderr, "BYPASSING LICENSE CHECKING: MAY DISPLAY NON-REDISTRIBUTABLE INFORMATION\n") @@ -169,3 +178,18 @@ func collectPaths(args []string) []string { } return paths } + +// validateBasePath 强制 base-path 形如 "/foo" 或空——空表示挂根。 +// 不允许尾部斜杠(mux pattern 拼起来会双斜杠引发匹配失败)。 +func validateBasePath(p string) error { + if p == "" { + return nil + } + if !strings.HasPrefix(p, "/") { + return fmt.Errorf("-base-path %q must start with /", p) + } + if strings.HasSuffix(p, "/") { + return fmt.Errorf("-base-path %q must not end with /", p) + } + return nil +} diff --git a/internal/frontend/server.go b/internal/frontend/server.go index 89f63a9c1..fc0dddacf 100644 --- a/internal/frontend/server.go +++ b/internal/frontend/server.go @@ -63,6 +63,11 @@ type Server struct { instanceID string HTTPClient *http.Client recordCodeWikiMetrics RecordClickFunc + // basePath:URL 前缀(如 "/gogodocs"),空字符串 = 默认挂根。 + // 由调用方通过 ServerConfig.BasePath 设置,并贯穿 mux pattern / StripPrefix / + // template "abs" 助手 / 内部 redirect / dochtml 链接拼接,使整站能整体迁移到子路径。 + // 关于不变式:basePath 要么空、要么形如 "/foo"——尾部不带斜杠(拼 mux 时双斜杠会失配)。 + basePath string mu sync.Mutex // Protects all fields below templates map[string]*template.Template @@ -101,16 +106,18 @@ type ServerConfig struct { VulndbClient *vuln.Client HTTPClient *http.Client RecordCodeWikiMetrics RecordClickFunc + // BasePath:URL 子路径前缀(如 "/gogodocs"),空 = 挂根。详见 [Server.basePath]。 + BasePath string } // NewServer creates a new Server for the given database and template directory. func NewServer(scfg ServerConfig) (_ *Server, err error) { defer derrors.Wrap(&err, "NewServer(...)") - ts, err := templates.ParsePageTemplates(scfg.TemplateFS) + ts, err := templates.ParsePageTemplates(scfg.TemplateFS, scfg.BasePath) if err != nil { return nil, fmt.Errorf("error parsing templates: %v", err) } - dochtml.LoadTemplates(scfg.TemplateFS) + dochtml.LoadTemplates(scfg.TemplateFS, scfg.BasePath) s := &Server{ fetchServer: scfg.FetchServer, getDataSource: scfg.DataSourceGetter, @@ -128,6 +135,7 @@ func NewServer(scfg ServerConfig) (_ *Server, err error) { vulnClient: scfg.VulndbClient, HTTPClient: scfg.HTTPClient, recordCodeWikiMetrics: scfg.RecordCodeWikiMetrics, + basePath: scfg.BasePath, } if s.HTTPClient == nil { s.HTTPClient = http.DefaultClient @@ -177,7 +185,21 @@ type Cacher interface { // Install registers server routes using the given handler registration func. // authValues is the set of values that can be set on authHeader to bypass the // cache. +// +// fork 注:handle 入参在函数体内被 shadow 成 base-path-aware 版本——所有 +// "GET /static/"、"/v1/api" 之类的 pattern 都自动通过 [Server.prefixPattern] +// 加上 [Server.basePath],无需改下面任何具体路由声明。空 basePath 时 prefix +// 是 no-op,行为与上游完全一致。internal StripPrefix 仍需手动加 s.basePath +// 让 file server 能剥到正确的剩余 path。 func (s *Server) Install(handle func(string, http.Handler), cacher Cacher, authValues []string) { + // shadow 入参 handle:所有 handle("GET /static/", ...) 自动变成 + // realHandle("GET /gogodocs/static/", ...),保持原代码零改动。 + // installDebugHandlers(handle) 透传时也用包装版本——_debug/pprof 等 + // 调试端点同样落在 base-path 下。 + realHandle := handle + handle = func(pattern string, h http.Handler) { + realHandle(s.prefixPattern(pattern), h) + } var ( detailHandler http.Handler = s.errorHandler(s.serveDetails) fetchHandler http.Handler @@ -208,12 +230,12 @@ func (s *Server) Install(handle func(string, http.Handler), cacher Cacher, authV log.Infof(r.Context(), "Request made to %q", r.URL.Path) })) handle("GET /static/", s.staticHandler()) - handle("GET /third_party/", http.StripPrefix("/third_party", http.FileServer(http.FS(s.thirdPartyFS)))) + handle("GET /third_party/", http.StripPrefix(s.basePath+"/third_party", http.FileServer(http.FS(s.thirdPartyFS)))) handle("GET /favicon.ico", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { serveFileFS(w, r, s.staticFS, "shared/icon/favicon.ico") })) - handle("/sitemap/", http.StripPrefix("/sitemap/", http.FileServer(http.Dir("private/sitemap")))) + handle("/sitemap/", http.StripPrefix(s.basePath+"/sitemap/", http.FileServer(http.Dir("private/sitemap")))) handle("GET /mod/", http.HandlerFunc(s.handleModuleDetailsRedirect)) handle("GET /pkg/", http.HandlerFunc(s.handlePackageDetailsRedirect)) if fetchHandler != nil { @@ -234,7 +256,7 @@ func (s *Server) Install(handle func(string, http.Handler), cacher Cacher, authV })) handle("GET /codewiki", http.HandlerFunc(s.handleCodeWikiRedirect)) handle("GET /golang.org/x", s.staticPageHandler("subrepo", "Sub-repositories")) - handle("GET /files/", http.StripPrefix("/files", s.fileMux)) + handle("GET /files/", http.StripPrefix(s.basePath+"/files", s.fileMux)) handle("GET /vuln/", vulnHandler) handle("GET /v1/package/", s.apiHandler(api.ServePackage)) handle("GET /v1/symbols/", s.apiHandler(api.ServePackageSymbols)) @@ -260,9 +282,9 @@ func (s *Server) Install(handle func(string, http.Handler), cacher Cacher, authV handle("/", detailHandler) if s.serveStats { handle("/detail-stats/", - stats.Stats()(http.StripPrefix("/detail-stats", s.errorHandler(s.serveDetails)))) + stats.Stats()(http.StripPrefix(s.basePath+"/detail-stats", s.errorHandler(s.serveDetails)))) handle("/search-stats/", - stats.Stats()(http.StripPrefix("/search-stats", s.errorHandler(s.serveSearch)))) + stats.Stats()(http.StripPrefix(s.basePath+"/search-stats", s.errorHandler(s.serveSearch)))) } handle("/robots.txt", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain; charset=utf-8") @@ -353,10 +375,38 @@ func (s *Server) installDebugHandlers(handle func(string, http.Handler)) { } // InstallFS adds path under the /files handler, serving the files in fsys. +// +// 注:fileMux 是子 mux,外层 [Server.Install] 在 /files/ 这一层已经 StripPrefix +// 掉了 [Server.basePath]+"/files",所以 fileMux 看到的请求 path 就是 raw path +// (从 path 段开始)。这里的 StripPrefix(path) 不需要带 basePath。 func (s *Server) InstallFS(path string, fsys fs.FS) { s.fileMux.Handle("GET "+path+"/", http.StripPrefix(path, http.FileServer(http.FS(fsys)))) } +// prefixPattern 把 mux pattern 里的 path 部分加 [Server.basePath] 前缀。 +// +// pattern 形式(go 1.22 ServeMux 语法): +// - "GET /static/" — 带 method +// - "/sitemap/" — 不带 method +// - "POST /play/share" — 任意 method +// +// basePath 为空时返回原 pattern(与上游零差异);非空时把 path 段前置 basePath。 +// +// 注:basePath 已在 [validateBasePath] 校验过形如 "/foo" 不带尾斜杠, +// 拼上去保证不会出现双斜杠(如 basePath="/x" + pattern="/static/" = "/x/static/")。 +func (s *Server) prefixPattern(pattern string) string { + if s.basePath == "" { + return pattern + } + // 拆 method 和 path:" " 分隔,最多一处。 + method, p, ok := strings.Cut(pattern, " ") + if !ok { + // 没空格——pattern 整段就是 path + return s.basePath + pattern + } + return method + " " + s.basePath + p +} + const ( // defaultTTL is used when details tab contents are subject to change, or when // there is a problem confirming that the details can be permanently cached. @@ -718,7 +768,7 @@ func (s *Server) findTemplate(templateName string) (*template.Template, error) { s.mu.Lock() defer s.mu.Unlock() var err error - s.templates, err = templates.ParsePageTemplates(s.templateFS) + s.templates, err = templates.ParsePageTemplates(s.templateFS, s.basePath) if err != nil { return nil, fmt.Errorf("error parsing templates: %v", err) } @@ -740,7 +790,7 @@ func executeTemplate(ctx context.Context, templateName string, tmpl *template.Te } func (s *Server) staticHandler() http.Handler { - return http.StripPrefix("/static/", http.FileServer(http.FS(s.staticFS))) + return http.StripPrefix(s.basePath+"/static/", http.FileServer(http.FS(s.staticFS))) } // serveFileFS serves a file from the given filesystem. diff --git a/internal/frontend/templates/templates.go b/internal/frontend/templates/templates.go index 035c5d36b..74b46074e 100644 --- a/internal/frontend/templates/templates.go +++ b/internal/frontend/templates/templates.go @@ -39,15 +39,45 @@ func stripScheme(url string) string { return url } +// funcsWithBasePath 在内置 templateFuncs 之上叠两个 base-path 助手: +// +// - `{{abs "/static/foo.svg"}}` → 静态绝对路径前置 BasePath, +// 站点挂根时输出 `/static/foo.svg`,挂 -base-path=/gogodocs 时输出 +// `/gogodocs/static/foo.svg`。必须以 / 开头;否则原样(让作者改时一目了然)。 +// - `{{basepath}}` → 返回 BasePath 字符串本身(不带尾斜杠),用于模板里 +// 拼动态 path 例如 ``——abs 不能拼带 +// 变量的 path(template 函数实参不能嵌套表达式)。 +// +// templateFuncs 是个全局只读 map,本函数 copy 一份再叠 helper, +// 避免不同 Server 实例(理论上多 BasePath 共存)相互覆盖 funcMap。 +func funcsWithBasePath(basePath string) template.FuncMap { + out := template.FuncMap{} + for k, v := range templateFuncs { + out[k] = v + } + out["abs"] = func(p string) string { + if basePath == "" || !strings.HasPrefix(p, "/") { + return p + } + return basePath + p + } + out["basepath"] = func() string { return basePath } + return out +} + // ParsePageTemplates parses html templates contained in the given filesystem in // order to generate a map of Name->*template.Template. // +// basePath 形如 "/gogodocs" 或空字符串。空 = 站点挂根(pkg.go.dev 行为)。 +// 模板里通过 `{{abs "/static/foo.svg"}}` 输出带 prefix 的绝对 URL。 +// // Separate templates are used so that certain contextual functions (e.g. // templateName) can be bound independently for each page. // // Templates in directories prefixed with an underscore are considered helper // templates and parsed together with the files in each base directory. -func ParsePageTemplates(fsys template.TrustedFS) (map[string]*template.Template, error) { +func ParsePageTemplates(fsys template.TrustedFS, basePath string) (map[string]*template.Template, error) { + funcs := funcsWithBasePath(basePath) templates := make(map[string]*template.Template) htmlSets := [][]string{ {"about"}, @@ -72,7 +102,7 @@ func ParsePageTemplates(fsys template.TrustedFS) (map[string]*template.Template, } for _, set := range htmlSets { - t, err := template.New("frontend.tmpl").Funcs(templateFuncs).ParseFS(fsys, "frontend/*.tmpl") + t, err := template.New("frontend.tmpl").Funcs(funcs).ParseFS(fsys, "frontend/*.tmpl") if err != nil { return nil, fmt.Errorf("ParseFS: %v", err) } diff --git a/internal/godoc/dochtml/dochtml.go b/internal/godoc/dochtml/dochtml.go index 8a80973a7..8407e6f4e 100644 --- a/internal/godoc/dochtml/dochtml.go +++ b/internal/godoc/dochtml/dochtml.go @@ -286,7 +286,10 @@ func renderInfo(ctx context.Context, fset *token.FileSet, p *doc.Package, opt Re if opt.BuildContext.GOOS != "" && opt.BuildContext.GOOS != "all" { search = "?GOOS=" + opt.BuildContext.GOOS } - return "/" + versionedPath + search + // basePath 在站点挂子路径时非空(如 "/gogodocs")——godoc 渲染的 + // cross-reference 链接(如 std lib ``)拼上前缀 + // 才能在反代环境正确路由。 + return basePath + "/" + versionedPath + search }, }) diff --git a/internal/godoc/dochtml/dochtml_test.go b/internal/godoc/dochtml/dochtml_test.go index 88b90a505..4b2ddec7e 100644 --- a/internal/godoc/dochtml/dochtml_test.go +++ b/internal/godoc/dochtml/dochtml_test.go @@ -47,7 +47,7 @@ var testRenderOptions = RenderOptions{ func TestRender(t *testing.T) { ctx := context.Background() - LoadTemplates(templateFS) + LoadTemplates(templateFS, "") for _, pkg := range []string{"everydecl", "comments", "order"} { t.Run(pkg, func(t *testing.T) { fset, d := mustLoadPackage(pkg) @@ -108,7 +108,7 @@ func compareWithGolden(t *testing.T, parts *Parts, name string, update bool) { } func TestImportLink(t *testing.T) { - LoadTemplates(templateFS) + LoadTemplates(templateFS, "") cases := []struct { name string @@ -203,7 +203,7 @@ func F(rng *rand.Rand) {} } func TestExampleRender(t *testing.T) { - LoadTemplates(templateFS) + LoadTemplates(templateFS, "") ctx := context.Background() fset, d := mustLoadPackage("example_test") diff --git a/internal/godoc/dochtml/symbol_test.go b/internal/godoc/dochtml/symbol_test.go index 6f9c558e3..67898836f 100644 --- a/internal/godoc/dochtml/symbol_test.go +++ b/internal/godoc/dochtml/symbol_test.go @@ -12,7 +12,7 @@ import ( ) func TestGetSymbols(t *testing.T) { - LoadTemplates(templateFS) + LoadTemplates(templateFS, "") fset, d := mustLoadPackage("symbols") got, diff --git a/internal/godoc/dochtml/template.go b/internal/godoc/dochtml/template.go index 0e5650ef2..95e869e6d 100644 --- a/internal/godoc/dochtml/template.go +++ b/internal/godoc/dochtml/template.go @@ -21,14 +21,28 @@ var ( // TODO(golang.org/issue/5060): finalize URL scheme and design for notes, // then it becomes more viable to factor out inline CSS style. bodyTemplate, outlineTemplate, sidenavTemplate *template.Template + + // basePath:单进程包级 URL 前缀(如 "/gogodocs"),由 [LoadTemplates] 写入; + // 渲染 godoc cross-reference 时([dochtml.go] 的 PackageURL)拼到链接前。 + // dochtml 包是单实例使用,存包级 var 可接受——pkgsite 单进程只有一个 BasePath。 + basePath string ) +// BasePath 暴露包级 base path(fork 用),供 [render] 包之外的链接生成需要时读。 +// 仅在 [LoadTemplates] 之后读取才有意义;并发安全(一次写入后只读)。 +func BasePath() string { return basePath } + func Templates() []*template.Template { return []*template.Template{bodyTemplate, outlineTemplate, sidenavTemplate} } // LoadTemplates reads and parses the templates used to generate documentation. -func LoadTemplates(fsys template.TrustedFS) { +// +// basePathPrefix 形如 "/gogodocs" 或空字符串。空 = 站点挂根(pkg.go.dev 行为)。 +// 仅在第一次调用生效(loadOnce 之后包级 templates 已 freeze),但 basePath +// 每次调用都会覆盖——单进程 pkgsite 全程只配一个 BasePath,重复写无副作用。 +func LoadTemplates(fsys template.TrustedFS, basePathPrefix string) { + basePath = basePathPrefix const dir = "doc" loadOnce.Do(func() { bodyTemplate = template.Must(template.New("body.tmpl"). diff --git a/static/frontend/_modals.tmpl b/static/frontend/_modals.tmpl index ecf9e071a..428446843 100644 --- a/static/frontend/_modals.tmpl +++ b/static/frontend/_modals.tmpl @@ -20,7 +20,7 @@ class="go-Icon" height="24" width="24" - src="/static/shared/icon/close_gm_grey_24dp.svg" + src="{{abs `/static/shared/icon/close_gm_grey_24dp.svg`}}" alt="" /> @@ -52,7 +52,7 @@ class="go-Icon" height="24" width="24" - src="/static/shared/icon/close_gm_grey_24dp.svg" + src="{{abs `/static/shared/icon/close_gm_grey_24dp.svg`}}" alt="" /> diff --git a/static/frontend/about/about.tmpl b/static/frontend/about/about.tmpl index 41e1beb30..9426c96b3 100644 --- a/static/frontend/about/about.tmpl +++ b/static/frontend/about/about.tmpl @@ -5,7 +5,7 @@ --> {{define "pre-content"}} - + {{end}} {{define "main"}} @@ -42,7 +42,7 @@

Making a request to proxy.golang.org for the module version, to any endpoint specified by the - Module proxy protocol. + Module proxy protocol. For example:
https://proxy.golang.org/example.com/my/module/@v/v1.0.0.info @@ -51,7 +51,7 @@

  • Downloading the package via the - go command. + go command. For example:
    GOPROXY=https://proxy.golang.org GO111MODULE=on go get example.com/my/module@v1.0.0 @@ -108,7 +108,7 @@ You may also see the terms GOOS and GOARCH for the OS and architecture respectively, because those are the names of the environment variables that the go command uses. - (See the go command documentation for more information.) + (See the go command documentation for more information.)

    If a package exists at only one build context, pkg.go.dev displays that build @@ -173,7 +173,7 @@ and is the official dependency management solution for Go. A module version is defined by a tree of source files, with a go.mod file in its root. - More information about the go.mod file. + More information about the go.mod file.

  • diff --git a/static/frontend/badge/badge.tmpl b/static/frontend/badge/badge.tmpl index 2f2d54461..7894e5449 100644 --- a/static/frontend/badge/badge.tmpl +++ b/static/frontend/badge/badge.tmpl @@ -9,19 +9,19 @@ {{end}} {{define "pre-content"}} - + {{end}} {{define "main"}}
    -
    +

    Create a badge

    Create a badge to link to pkg.go.dev from your project website or README file.

    {{else}}
    - The Go Gopher + The Go Gopher

    Type a pkg.go.dev URL above to create a badge link.

    {{end}} diff --git a/static/frontend/error/error.tmpl b/static/frontend/error/error.tmpl index c7cefafcf..4f4306fdb 100644 --- a/static/frontend/error/error.tmpl +++ b/static/frontend/error/error.tmpl @@ -5,7 +5,7 @@ --> {{define "pre-content"}} - + {{end}} {{define "main"}} diff --git a/static/frontend/fetch/fetch.tmpl b/static/frontend/fetch/fetch.tmpl index 87d860575..d18cc8fdf 100644 --- a/static/frontend/fetch/fetch.tmpl +++ b/static/frontend/fetch/fetch.tmpl @@ -7,7 +7,7 @@ {{define "title"}}{{.MessageData}} - Go Packages{{end}} {{define "pre-content"}} - + {{end}} {{define "main"}} @@ -27,7 +27,7 @@

    Check that you entered the URL correctly, - try fetching it following the instructions here, + try fetching it following the instructions here, or request to add “{{.MessageData}}” to pkg.go.dev.

  • @@ -53,7 +53,7 @@
    Or browse local modules:
    {{end}} diff --git a/static/frontend/search-help/search-help.tmpl b/static/frontend/search-help/search-help.tmpl index c6bf54aa6..59c0e9d7d 100644 --- a/static/frontend/search-help/search-help.tmpl +++ b/static/frontend/search-help/search-help.tmpl @@ -5,7 +5,7 @@ --> {{define "pre-content"}} - + {{end}} {{define "main"}} @@ -27,9 +27,9 @@

    You can also search for a symbol by name across all packages. A symbol is a constant, variable, function, type, field, or method.

    Searching by symbol will return a list of packages containing the symbol you specify. You can search by the following:

    diff --git a/static/frontend/search/search.tmpl b/static/frontend/search/search.tmpl index f699dcd82..0ba390427 100644 --- a/static/frontend/search/search.tmpl +++ b/static/frontend/search/search.tmpl @@ -13,7 +13,7 @@ {{end}} {{define "pre-content"}} - + {{end}} {{define "post-content"}} @@ -42,7 +42,7 @@ {{define "search_symbol"}}
    Showing {{len $.Results}} matching {{.SearchModeSymbol}}s. - Search help + Search help
    {{if eq (len .Results) 0}} {{template "search_no_results" .}} @@ -54,7 +54,7 @@ {{define "search_no_results"}} {{template "gopher-airplane" "It looks like there are no matches for your search."}}

    - Need help? Check out tips for searching on pkg.go.dev. + Need help? Check out tips for searching on pkg.go.dev.

    {{end}} @@ -73,7 +73,7 @@ {{.SymbolName}} in - {{$r.PackagePath}} {{with $r.ChipText}}{{.}}{{end}} @@ -88,7 +88,7 @@ {{define "search_package"}}
    - Showing {{len .Results}} modules with matching packages. Search help + Showing {{len .Results}} modules with matching packages. Search help
    {{if eq (len .Results) 0}} {{template "search_no_results" .}} @@ -106,7 +106,7 @@

    - {{$v.Name}} ({{$v.PackagePath}}) @@ -126,7 +126,7 @@ {{.Heading}} {{$numLinks := (len .Links)}} {{range $i, $v := .Links}} - + {{$v.Body}}{{if lt $i (subtract $numLinks 1)}},{{end}} {{end}} @@ -139,7 +139,7 @@ {{range $i, $v := .Links}} {{if and (lt $i 5) (ne $v.Body "")}} {{$v.Body}} @@ -162,7 +162,7 @@ {{define "search_metadata"}}
    - + Imported by {{.NumImportedBy}} | @@ -172,7 +172,7 @@ | {{if .Licenses}} - + {{commaseparate .Licenses}} {{else}} @@ -190,7 +190,7 @@ {{- if and (lt $p.Limit $p.MaxLimit) (eq $p.Limit (len .Results)) -}} Show more results. {{- else -}} - See search help. + See search help. {{- end -}}
    {{end}} @@ -215,12 +215,12 @@
    diff --git a/static/frontend/subrepo/subrepo.tmpl b/static/frontend/subrepo/subrepo.tmpl index 2cf0c6d49..29db14867 100644 --- a/static/frontend/subrepo/subrepo.tmpl +++ b/static/frontend/subrepo/subrepo.tmpl @@ -10,7 +10,7 @@ {{end}} {{define "pre-content"}} - + {{end}} {{define "main"}} @@ -28,91 +28,91 @@

    diff --git a/static/frontend/unit/_header.tmpl b/static/frontend/unit/_header.tmpl index 641678b64..16a3c34c5 100644 --- a/static/frontend/unit/_header.tmpl +++ b/static/frontend/unit/_header.tmpl @@ -42,7 +42,7 @@ class="go-Icon go-Icon--accented" height="24" width="24" - src="/static/shared/icon/content_copy_gm_grey_24dp.svg" + src="{{abs `/static/shared/icon/content_copy_gm_grey_24dp.svg`}}" alt="" > @@ -56,7 +56,7 @@ {{define "unit-header-title"}}

    {{.Title}}

    {{range .PageLabels}} @@ -76,7 +76,7 @@ class="go-Icon go-Icon--accented" height="24" width="24" - src="/static/shared/icon/content_copy_gm_grey_24dp.svg" + src="{{abs `/static/shared/icon/content_copy_gm_grey_24dp.svg`}}" alt="" /> @@ -150,14 +150,14 @@ {{if $i}}, {{end}} {{$e.Type}} {{- end -}} - not legal advice {{end}} {{else}} None detected - not legal advice @@ -199,7 +199,7 @@
    ``` @@ -55,7 +55,7 @@ class="go-Icon" height="24" width="24" - src="/static/shared/icon/search_gm_grey_24dp.svg" + src="{{abs `/static/shared/icon/search_gm_grey_24dp.svg`}}" alt="" /> @@ -82,7 +82,7 @@ class="go-Icon" height="24" width="24" - src="/static/shared/icon/filter_list_gm_grey_24dp.svg" + src="{{abs `/static/shared/icon/filter_list_gm_grey_24dp.svg`}}" alt="" /> @@ -94,7 +94,7 @@ class="go-Icon" height="24" width="24" - src="/static/shared/icon/search_gm_grey_24dp.svg" + src="{{abs `/static/shared/icon/search_gm_grey_24dp.svg`}}" alt="" /> @@ -110,7 +110,7 @@ class="go-Icon" height="24" width="24" - src="/static/shared/icon/filter_list_gm_grey_24dp.svg" + src="{{abs `/static/shared/icon/filter_list_gm_grey_24dp.svg`}}" alt="" /> @@ -119,7 +119,7 @@ class="go-Icon" height="24" width="24" - src="/static/shared/icon/search_gm_grey_24dp.svg" + src="{{abs `/static/shared/icon/search_gm_grey_24dp.svg`}}" alt="" /> diff --git a/static/shared/gopher/gopher.tmpl b/static/shared/gopher/gopher.tmpl index 456b79abd..850e0bfc9 100644 --- a/static/shared/gopher/gopher.tmpl +++ b/static/shared/gopher/gopher.tmpl @@ -6,7 +6,7 @@ {{define "gopher-airplane"}}
    - The Go Gopher {{if .}}

    {{.}}

    {{end}}
    diff --git a/static/shared/header/header.tmpl b/static/shared/header/header.tmpl index 4ae963b25..2978831cf 100644 --- a/static/shared/header/header.tmpl +++ b/static/shared/header/header.tmpl @@ -10,7 +10,7 @@
    @@ -161,42 +161,42 @@ aria-label="Get connected with google-groups (Opens in new window)" title="Get connected with google-groups (Opens in new window)" href="https://groups.google.com/g/golang-nuts"> - + - + - + - + - + - +

    @@ -213,7 +213,7 @@
    @@ -24,7 +24,7 @@ class="go-Icon" height="24" width="24" - src="/static/shared/icon/alert_gm_grey_24dp.svg" + src="{{abs `/static/shared/icon/alert_gm_grey_24dp.svg`}}" alt="Warning" />  Retracted: This version of Syntax has been retracted. @@ -38,7 +38,7 @@ class="go-Icon" height="24" width="24" - src="/static/shared/icon/alert_gm_grey_24dp.svg" + src="{{abs `/static/shared/icon/alert_gm_grey_24dp.svg`}}" alt="Alert" />  Critical error diff --git a/static/shared/modal/modal.md b/static/shared/modal/modal.md index cd1f8d413..473ebc24b 100644 --- a/static/shared/modal/modal.md +++ b/static/shared/modal/modal.md @@ -22,7 +22,7 @@ The size modifer class is optional. The base modal will grow to fit the inner co class="go-Icon" height="24" width="24" - src="/static/shared/icon/close_gm_grey_24dp.svg" + src="{{abs `/static/shared/icon/close_gm_grey_24dp.svg`}}" alt="" /> @@ -56,7 +56,7 @@ The size modifer class is optional. The base modal will grow to fit the inner co class="go-Icon" height="24" width="24" - src="/static/shared/icon/close_gm_grey_24dp.svg" + src="{{abs `/static/shared/icon/close_gm_grey_24dp.svg`}}" alt="" /> @@ -90,7 +90,7 @@ The size modifer class is optional. The base modal will grow to fit the inner co class="go-Icon" height="24" width="24" - src="/static/shared/icon/close_gm_grey_24dp.svg" + src="{{abs `/static/shared/icon/close_gm_grey_24dp.svg`}}" alt="" /> diff --git a/static/shared/tooltip/tooltip.md b/static/shared/tooltip/tooltip.md index 7c6dc2cec..bcd92fa52 100644 --- a/static/shared/tooltip/tooltip.md +++ b/static/shared/tooltip/tooltip.md @@ -10,7 +10,7 @@ class="go-Icon" height="24" width="24" - src="/static/shared/icon/help_gm_grey_24dp.svg" + src="{{abs `/static/shared/icon/help_gm_grey_24dp.svg`}}" alt="" /> diff --git a/static/shared/vuln/vuln.tmpl b/static/shared/vuln/vuln.tmpl index 71df6074e..e4d865469 100644 --- a/static/shared/vuln/vuln.tmpl +++ b/static/shared/vuln/vuln.tmpl @@ -10,10 +10,10 @@ class="go-Icon" height="24" width="24" - src="/static/shared/icon/alert_gm_grey_24dp.svg" + src="{{abs `/static/shared/icon/alert_gm_grey_24dp.svg`}}" alt="Alert" />  - {{.ID}}: {{.Details}} + {{.ID}}: {{.Details}} {{end}} diff --git a/static/worker/excluded.tmpl b/static/worker/excluded.tmpl index 91e8ae7d0..36b1b1543 100644 --- a/static/worker/excluded.tmpl +++ b/static/worker/excluded.tmpl @@ -7,7 +7,7 @@ - + {{.Env}} Worker Excluded diff --git a/static/worker/index.tmpl b/static/worker/index.tmpl index 432177520..033156871 100644 --- a/static/worker/index.tmpl +++ b/static/worker/index.tmpl @@ -7,7 +7,7 @@ - + {{.Env}} Worker @@ -30,39 +30,39 @@

    - Modules | - Traces | - RPCs | - Metrics | - Excluded + Modules | + Traces | + RPCs | + Metrics | + Excluded

    - + -
    +
    -
    +
    -
    +
    -
    + @@ -152,7 +152,7 @@ {{.RequestInfo.State.Load}} {{with .RequestInfo.TraceID}} Logs - Cancel + Cancel {{end}} {{end}} @@ -179,7 +179,7 @@ {{.State.Load}} {{with .TraceID}} Logs - Cancel + Cancel {{end}} {{end}} @@ -224,5 +224,5 @@ s.src = src; document.head.appendChild(s); } - loadScript("/static/worker/worker.js"); + loadScript("{{abs `/static/worker/worker.js`}}"); diff --git a/static/worker/versions.tmpl b/static/worker/versions.tmpl index 1e70bef42..2faa0a0e9 100644 --- a/static/worker/versions.tmpl +++ b/static/worker/versions.tmpl @@ -40,13 +40,13 @@ - + {{.Env}} Worker

    {{.Env}} Worker

    All times in America/New_York.

    -

    Home

    +

    Home

    Statistics

    From 3f26e56aeccd2d7f6e81c1b045c9d8e1f2d34966 Mon Sep 17 00:00:00 2001 From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com> Date: Thu, 7 May 2026 14:57:55 +0800 Subject: [PATCH 02/26] =?UTF-8?q?feat(base-path):=20prefix=20BasePath=20in?= =?UTF-8?q?to=20redirect=20=E5=AD=97=E9=9D=A2=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 5 处硬编码 redirect 接 base path——之前的 commit 处理了模板和 mux 但忽略了 Go 端字面量 redirect URL,反代环境下这些 redirect 会跳出 站点子路径丢失上下文。 - internal/frontend/server.go:/cmd/cgo(C 包重定向)/v1/api(×2) 三处加 s.basePath 前缀 - internal/frontend/fetchserver/fetch.go:FetchServer struct 加 BasePath 字段(caller 不传则空字符串 = 行为同上游) - internal/frontend/fetchserver/404.go:stdlib shortcut redirect 和 nested-modules 搜索 redirect 用 s.BasePath 拼前缀 未处理(暂留 TODO): - internal/middleware/godocredirect.go:godoc.org → pkg.go.dev 跳转, fork 内网部署用不到,host 还是 hardcoded "pkg.go.dev" - internal/frontend/serrors/serrors.go:inline error template 里的 / —— 这两个 template 自带 parse,没注入 abs func;fork 内网这两类 错误页极少触发,待 smoke test 发现真在出现再补 - cmd/frontend/main.go:prod 二进制创建 FetchServer 没传 BasePath。 cmd/pkgsite(dev 二进制 / docker-compose)不用 FetchServer,影响为零。 --- internal/frontend/fetchserver/404.go | 4 ++-- internal/frontend/fetchserver/fetch.go | 4 ++++ internal/frontend/server.go | 6 +++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/internal/frontend/fetchserver/404.go b/internal/frontend/fetchserver/404.go index 7841e2731..6bdce2adc 100644 --- a/internal/frontend/fetchserver/404.go +++ b/internal/frontend/fetchserver/404.go @@ -46,7 +46,7 @@ func (s *FetchServer) ServePathNotFoundPage(w http.ResponseWriter, r *http.Reque log.Error(ctx, err) } if path != "" { - http.Redirect(w, r, fmt.Sprintf("/%s", path), http.StatusFound) + http.Redirect(w, r, fmt.Sprintf("%s/%s", s.BasePath, path), http.StatusFound) return } @@ -132,7 +132,7 @@ func (s *FetchServer) ServePathNotFoundPage(w http.ResponseWriter, r *http.Reque // See https://golang.org/issue/43725 for context. nm, err := db.GetNestedModules(ctx, fullPath) if err == nil && len(nm) > 0 { - http.Redirect(w, r, "/search?q="+url.QueryEscape(fullPath), http.StatusFound) + http.Redirect(w, r, s.BasePath+"/search?q="+url.QueryEscape(fullPath), http.StatusFound) return nil } return &serrors.ServerError{ diff --git a/internal/frontend/fetchserver/fetch.go b/internal/frontend/fetchserver/fetch.go index 27f0fda06..46acacbcd 100644 --- a/internal/frontend/fetchserver/fetch.go +++ b/internal/frontend/fetchserver/fetch.go @@ -89,6 +89,10 @@ var ( type FetchServer struct { Queue queue.Queue TaskIDChangeInterval time.Duration + // BasePath:站点 URL 子路径前缀(如 "/gogodocs")。空 = 挂根。 + // 用于 [ServePathNotFoundPage] 内部 http.Redirect 拼绝对 URL,否则 + // /search?q=... 之类的重定向会跳到 reverse proxy 之外。 + BasePath string } // ServeFetch checks if a requested path and version exists in the database. diff --git a/internal/frontend/server.go b/internal/frontend/server.go index fc0dddacf..59fe187ab 100644 --- a/internal/frontend/server.go +++ b/internal/frontend/server.go @@ -252,7 +252,7 @@ func (s *Server) Install(handle func(string, http.Handler), cacher Cacher, authV handle("GET /C", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Package "C" is a special case: redirect to /cmd/cgo. // (This is what golang.org/C does.) - http.Redirect(w, r, "/cmd/cgo", http.StatusMovedPermanently) + http.Redirect(w, r, s.basePath+"/cmd/cgo", http.StatusMovedPermanently) })) handle("GET /codewiki", http.HandlerFunc(s.handleCodeWikiRedirect)) handle("GET /golang.org/x", s.staticPageHandler("subrepo", "Sub-repositories")) @@ -268,10 +268,10 @@ func (s *Server) Install(handle func(string, http.Handler), cacher Cacher, authV handle("GET /v1/vulns/", s.apiHandler(api.ServeVulnerabilities(s.vulnClient))) handle("GET /v1/api", s.apiDocHandler()) handle("GET /api", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, "/v1/api", http.StatusMovedPermanently) + http.Redirect(w, r, s.basePath+"/v1/api", http.StatusMovedPermanently) })) handle("GET /v1", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, "/v1/api", http.StatusMovedPermanently) + http.Redirect(w, r, s.basePath+"/v1/api", http.StatusMovedPermanently) })) handle("/v1/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { api.ServeError(w, r, api.BadRequest("Unknown API endpoint. For documentation, GET /v1/api at this host and port.")) From ea5ce91a8370e83468de5bfd42106eec9efb4f79 Mon Sep 17 00:00:00 2001 From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com> Date: Thu, 7 May 2026 15:15:03 +0800 Subject: [PATCH 03/26] feat(base-path): TS runtime base path + asset helper + detail handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P1 收尾——让站点真正能在 /gogodocs/ 子路径下浏览(之前 commit 把 框架搭好,但 smoke test 暴露三类残留问题): 1. safehtml/template TrustedResourceURL 校验严格——abs 在 后接 dynamic ?version={{.AppVersionLabel}} 拼接被拒("?version=" 不是合法 URL prefix)。新增 [asset path version] template helper: 一气拼好 base + path + ?version= 返回 safehtml.TrustedResourceURL, 单 segment 单类型让 safehtml 满意。批量 sed 把模板里 `{{abs `path`}}?version={{.AppVersionLabel}}` 改成 `{{asset `path` .AppVersionLabel}}` 共 ~15 处。 2. → JS string escape + // - {{abs ...}} 在文本节点 → HTML escape + // 用 string 而非 safehtml.TrustedResourceURL 是因为后者无法在 {{end}} diff --git a/static/frontend/frontend.js b/static/frontend/frontend.js index 392707036..a37504179 100644 --- a/static/frontend/frontend.js +++ b/static/frontend/frontend.js @@ -1,21 +1,27 @@ -function P(){let n=document.querySelector(".js-header");document.querySelectorAll(".js-desktop-menu-hover").forEach(a=>{a.addEventListener("mouseenter",c=>{let l=c.target,s=document.querySelector(".forced-open");s&&s!==a&&(s.blur(),s.classList.remove("forced-open")),l.classList.remove("forced-closed"),l.classList.add("forced-open")});let u=c=>{var f,p;let l=c.target,s=l==null?void 0:l.classList.contains("forced-open"),o=c.currentTarget;s?(o.removeEventListener("blur",()=>o.classList.remove("forced-open")),o.classList.remove("forced-open"),o.classList.add("forced-closed"),o.blur(),(f=o==null?void 0:o.parentNode)==null||f.addEventListener("mouseout",()=>{o.classList.remove("forced-closed")})):(o.classList.remove("forced-closed"),o.classList.add("forced-open"),o.focus(),o.addEventListener("blur",()=>o.classList.remove("forced-open")),(p=o==null?void 0:o.parentNode)==null||p.removeEventListener("mouseout",()=>{o.classList.remove("forced-closed")})),o.focus()};a.addEventListener("click",u),a.addEventListener("focus",c=>{let l=c.target;l.classList.add("forced-closed"),l.classList.remove("forced-open")});let d=c=>{let l=c,s=c.target;if(l.key==="Escape"){let o=document.querySelector(".forced-open");o&&(o.classList.remove("forced-open"),o.blur(),o.classList.add("forced-closed"),s==null||s.focus())}};document.addEventListener("keydown",d)});let t=document.querySelectorAll(".js-headerMenuButton");t.forEach(a=>{a.addEventListener("click",u=>{u.preventDefault();let d=n==null?void 0:n.classList.contains("is-active");d?v(n):w(n),a.setAttribute("aria-expanded",d?"true":"false")})});let i=document.querySelector(".js-scrim");i==null||i.addEventListener("click",a=>{a.preventDefault(),document.querySelectorAll(".go-NavigationDrawer-submenuItem.is-active").forEach(d=>v(d)),v(n),t.forEach(d=>{d.setAttribute("aria-expanded",n!=null&&n.classList.contains("is-active")?"true":"false")})});let r=a=>{if(!a)return[];let u=Array.from(a.querySelectorAll(":scope > .go-NavigationDrawer-nav > .go-NavigationDrawer-list > .go-NavigationDrawer-listItem > a, :scope > .go-NavigationDrawer-nav > .go-NavigationDrawer-list > .go-NavigationDrawer-listItem > .go-Header-socialIcons > a")||[]),d=a.querySelector(".go-NavigationDrawer-header > a");return d&&u.unshift(d),u},h=a=>{if(a)return a.classList.contains("go-NavigationDrawer-submenuItem")},v=a=>{var c,l;if(!a)return;let u=r(a);a.classList.remove("is-active");let d=(c=a.closest(".go-NavigationDrawer-listItem"))==null?void 0:c.querySelector(":scope > a");d==null||d.focus(),u==null||u.forEach(s=>s==null?void 0:s.setAttribute("tabindex","-1")),u&&u[0]&&(u[0].removeEventListener("keydown",M(a)),u[u.length-1].removeEventListener("keydown",L(a))),a===n&&t&&((l=t[0])==null||l.focus())},w=a=>{let u=r(a);a.classList.add("is-active"),u.forEach(d=>d.setAttribute("tabindex","0")),u[0].focus(),u[0].addEventListener("keydown",M(a)),u[u.length-1].addEventListener("keydown",L(a))},M=a=>u=>{u.key==="Tab"&&u.shiftKey&&(u.preventDefault(),v(a))},L=a=>u=>{u.key==="Tab"&&!u.shiftKey&&(u.preventDefault(),v(a))},b=a=>{var c;let u=h(a),d=r(a);a.addEventListener("keyup",l=>{l.key==="Escape"&&v(a)}),d.forEach(l=>{let s=l.closest("li");if(s&&s.classList.contains("js-mobile-subnav-trigger")){let o=s.querySelector(".go-NavigationDrawer-submenuItem");l.addEventListener("click",()=>{w(o)})}}),u&&(v(a),(c=a==null?void 0:a.querySelector(".go-NavigationDrawer-header"))==null||c.addEventListener("click",l=>{l.preventDefault(),v(a)}))};document.querySelectorAll(".go-NavigationDrawer").forEach(a=>b(a)),v(n)}function U(){let n=document.querySelector(".js-searchForm"),e=document.querySelector(".js-expandSearch"),t=n==null?void 0:n.querySelector("input"),i=document.querySelector(".js-headerLogo"),r=document.querySelector(".js-headerMenuButton");e==null||e.addEventListener("click",()=>{n==null||n.classList.add("go-SearchForm--expanded"),i==null||i.classList.add("go-Header-logo--hidden"),r==null||r.classList.add("go-Header-navOpen--hidden"),t==null||t.focus()}),document==null||document.addEventListener("click",h=>{n!=null&&n.contains(h.target)||(n==null||n.classList.remove("go-SearchForm--expanded"),i==null||i.classList.remove("go-Header-logo--hidden"),r==null||r.classList.remove("go-Header-navOpen--hidden"))})}var k=class{constructor(e){this.el=e;this.setActive=e=>{this.activeIndex=(e+this.slides.length)%this.slides.length,this.el.setAttribute("data-slide-index",String(this.activeIndex));for(let t of this.dots)t.classList.remove("go-Carousel-dot--active");this.dots[this.activeIndex].classList.add("go-Carousel-dot--active");for(let t of this.slides)t.setAttribute("aria-hidden","true");this.slides[this.activeIndex].removeAttribute("aria-hidden"),this.liveRegion.textContent="Slide "+(this.activeIndex+1)+" of "+this.slides.length};var t;this.slides=Array.from(e.querySelectorAll(".go-Carousel-slide")),this.dots=[],this.liveRegion=document.createElement("div"),this.activeIndex=Number((t=e.getAttribute("data-slide-index"))!=null?t:0),this.initSlides(),this.initArrows(),this.initDots(),this.initLiveRegion()}initSlides(){for(let[e,t]of this.slides.entries())e!==this.activeIndex&&t.setAttribute("aria-hidden","true")}initArrows(){var t,i;let e=document.createElement("ul");e.classList.add("go-Carousel-arrows"),e.innerHTML=` +function $(){let t=document.querySelector(".js-header");document.querySelectorAll(".js-desktop-menu-hover").forEach(a=>{a.addEventListener("mouseenter",c=>{let l=c.target,s=document.querySelector(".forced-open");s&&s!==a&&(s.blur(),s.classList.remove("forced-open")),l.classList.remove("forced-closed"),l.classList.add("forced-open")});let u=c=>{var f,p;let l=c.target,s=l==null?void 0:l.classList.contains("forced-open"),o=c.currentTarget;s?(o.removeEventListener("blur",()=>o.classList.remove("forced-open")),o.classList.remove("forced-open"),o.classList.add("forced-closed"),o.blur(),(f=o==null?void 0:o.parentNode)==null||f.addEventListener("mouseout",()=>{o.classList.remove("forced-closed")})):(o.classList.remove("forced-closed"),o.classList.add("forced-open"),o.focus(),o.addEventListener("blur",()=>o.classList.remove("forced-open")),(p=o==null?void 0:o.parentNode)==null||p.removeEventListener("mouseout",()=>{o.classList.remove("forced-closed")})),o.focus()};a.addEventListener("click",u),a.addEventListener("focus",c=>{let l=c.target;l.classList.add("forced-closed"),l.classList.remove("forced-open")});let d=c=>{let l=c,s=c.target;if(l.key==="Escape"){let o=document.querySelector(".forced-open");o&&(o.classList.remove("forced-open"),o.blur(),o.classList.add("forced-closed"),s==null||s.focus())}};document.addEventListener("keydown",d)});let n=document.querySelectorAll(".js-headerMenuButton");n.forEach(a=>{a.addEventListener("click",u=>{u.preventDefault();let d=t==null?void 0:t.classList.contains("is-active");d?v(t):w(t),a.setAttribute("aria-expanded",d?"true":"false")})});let i=document.querySelector(".js-scrim");i==null||i.addEventListener("click",a=>{a.preventDefault(),document.querySelectorAll(".go-NavigationDrawer-submenuItem.is-active").forEach(d=>v(d)),v(t),n.forEach(d=>{d.setAttribute("aria-expanded",t!=null&&t.classList.contains("is-active")?"true":"false")})});let r=a=>{if(!a)return[];let u=Array.from(a.querySelectorAll(":scope > .go-NavigationDrawer-nav > .go-NavigationDrawer-list > .go-NavigationDrawer-listItem > a, :scope > .go-NavigationDrawer-nav > .go-NavigationDrawer-list > .go-NavigationDrawer-listItem > .go-Header-socialIcons > a")||[]),d=a.querySelector(".go-NavigationDrawer-header > a");return d&&u.unshift(d),u},h=a=>{if(a)return a.classList.contains("go-NavigationDrawer-submenuItem")},v=a=>{var c,l;if(!a)return;let u=r(a);a.classList.remove("is-active");let d=(c=a.closest(".go-NavigationDrawer-listItem"))==null?void 0:c.querySelector(":scope > a");d==null||d.focus(),u==null||u.forEach(s=>s==null?void 0:s.setAttribute("tabindex","-1")),u&&u[0]&&(u[0].removeEventListener("keydown",M(a)),u[u.length-1].removeEventListener("keydown",L(a))),a===t&&n&&((l=n[0])==null||l.focus())},w=a=>{let u=r(a);a.classList.add("is-active"),u.forEach(d=>d.setAttribute("tabindex","0")),u[0].focus(),u[0].addEventListener("keydown",M(a)),u[u.length-1].addEventListener("keydown",L(a))},M=a=>u=>{u.key==="Tab"&&u.shiftKey&&(u.preventDefault(),v(a))},L=a=>u=>{u.key==="Tab"&&!u.shiftKey&&(u.preventDefault(),v(a))},b=a=>{var c;let u=h(a),d=r(a);a.addEventListener("keyup",l=>{l.key==="Escape"&&v(a)}),d.forEach(l=>{let s=l.closest("li");if(s&&s.classList.contains("js-mobile-subnav-trigger")){let o=s.querySelector(".go-NavigationDrawer-submenuItem");l.addEventListener("click",()=>{w(o)})}}),u&&(v(a),(c=a==null?void 0:a.querySelector(".go-NavigationDrawer-header"))==null||c.addEventListener("click",l=>{l.preventDefault(),v(a)}))};document.querySelectorAll(".go-NavigationDrawer").forEach(a=>b(a)),v(t)}function W(){let t=document.querySelector(".js-searchForm"),e=document.querySelector(".js-expandSearch"),n=t==null?void 0:t.querySelector("input"),i=document.querySelector(".js-headerLogo"),r=document.querySelector(".js-headerMenuButton");e==null||e.addEventListener("click",()=>{t==null||t.classList.add("go-SearchForm--expanded"),i==null||i.classList.add("go-Header-logo--hidden"),r==null||r.classList.add("go-Header-navOpen--hidden"),n==null||n.focus()}),document==null||document.addEventListener("click",h=>{t!=null&&t.contains(h.target)||(t==null||t.classList.remove("go-SearchForm--expanded"),i==null||i.classList.remove("go-Header-logo--hidden"),r==null||r.classList.remove("go-Header-navOpen--hidden"))})}function O(){var t;return(t=document.documentElement.dataset.basePath)!=null?t:""}function J(t){return t.startsWith("/")?O()+t:t}var k=class{constructor(e){this.el=e;this.setActive=e=>{this.activeIndex=(e+this.slides.length)%this.slides.length,this.el.setAttribute("data-slide-index",String(this.activeIndex));for(let n of this.dots)n.classList.remove("go-Carousel-dot--active");this.dots[this.activeIndex].classList.add("go-Carousel-dot--active");for(let n of this.slides)n.setAttribute("aria-hidden","true");this.slides[this.activeIndex].removeAttribute("aria-hidden"),this.liveRegion.textContent="Slide "+(this.activeIndex+1)+" of "+this.slides.length};var n;this.slides=Array.from(e.querySelectorAll(".go-Carousel-slide")),this.dots=[],this.liveRegion=document.createElement("div"),this.activeIndex=Number((n=e.getAttribute("data-slide-index"))!=null?n:0),this.initSlides(),this.initArrows(),this.initDots(),this.initLiveRegion()}initSlides(){for(let[e,n]of this.slides.entries())e!==this.activeIndex&&n.setAttribute("aria-hidden","true")}initArrows(){var i,r;let e=document.createElement("ul");e.classList.add("go-Carousel-arrows");let n=O();e.innerHTML=`
  • - `,(t=e.querySelector(".go-Carousel-prevSlide"))==null||t.addEventListener("click",()=>this.setActive(this.activeIndex-1)),(i=e.querySelector(".go-Carousel-nextSlide"))==null||i.addEventListener("click",()=>this.setActive(this.activeIndex+1)),this.el.append(e)}initDots(){let e=document.createElement("ul");e.classList.add("go-Carousel-dots");for(let t=0;tSlide ${t+1}`,r.addEventListener("click",()=>this.setActive(t)),i.append(r),e.append(i),this.dots.push(r)}this.el.append(e)}initLiveRegion(){this.liveRegion.setAttribute("aria-live","polite"),this.liveRegion.setAttribute("aria-atomic","true"),this.liveRegion.setAttribute("class","go-Carousel-obscured"),this.liveRegion.textContent=`Slide ${this.activeIndex+1} of ${this.slides.length}`,this.el.appendChild(this.liveRegion)}};var A=class{constructor(e){this.el=e;var t,i,r,h,v;this.data=(t=e.dataset.toCopy)!=null?t:e.innerText,!this.data&&((i=e.parentElement)!=null&&i.classList.contains("go-InputGroup"))&&(this.data=(v=this.data||((h=(r=e.parentElement)==null?void 0:r.querySelector("input"))==null?void 0:h.value))!=null?v:""),e.addEventListener("click",w=>this.handleCopyClick(w))}handleCopyClick(e){e.preventDefault();let t=1e3;if(!navigator.clipboard){this.showTooltipText("Unable to copy",t);return}navigator.clipboard.writeText(this.data).then(()=>{this.showTooltipText("Copied!",t)}).catch(()=>{this.showTooltipText("Unable to copy",t)})}showTooltipText(e,t){this.el.setAttribute("data-tooltip",e),setTimeout(()=>this.el.setAttribute("data-tooltip",""),t)}};var x=class{constructor(e){this.el=e;document.addEventListener("click",t=>{this.el.contains(t.target)||this.el.removeAttribute("open")}),this.el.addEventListener("keydown",t=>{t.key==="Escape"&&(this.el.open=!1)})}};var C=class{constructor(e){this.el=e;this.el.addEventListener("change",t=>{let i=t.target,r=i.value;i.value.startsWith("/")||(r="/"+r),window.location.href=r})}};var q=class{constructor(e){this.el=e;window.dialogPolyfill&&window.dialogPolyfill.registerDialog(e),this.init()}init(){let e=document.querySelector(`[aria-controls="${this.el.id}"]`);e&&e.addEventListener("click",()=>{var t;this.el.showModal?this.el.showModal():this.el.setAttribute("opened","true"),(t=this.el.querySelector("input"))==null||t.focus()});for(let t of this.el.querySelectorAll("[data-modal-close]"))t.addEventListener("click",()=>{this.el.close?this.el.close():this.el.removeAttribute("opened")})}};function I(n,e,t,i){var r;(r=window.dataLayer)!=null||(window.dataLayer=[]),typeof n=="string"?window.dataLayer.push({event:n,event_category:e,event_action:t,event_label:i}):window.dataLayer.push(n)}function W(n){var e;(e=window.dataLayer)!=null||(window.dataLayer=[]),window.dataLayer.push(n)}var O=class{constructor(){this.handlers={},document.addEventListener("keydown",e=>this.handleKeyPress(e))}on(e,t,i,r){var h,v;return(v=(h=this.handlers)[e])!=null||(h[e]=new Set),this.handlers[e].add({description:t,callback:i,...r}),this}handleKeyPress(e){var t;for(let i of(t=this.handlers[e.key.toLowerCase()])!=null?t:new Set){if(i.target&&i.target!==e.target)return;let r=e.target;if(!i.target&&((r==null?void 0:r.tagName)==="INPUT"||(r==null?void 0:r.tagName)==="SELECT"||(r==null?void 0:r.tagName)==="TEXTAREA")||r!=null&&r.isContentEditable||i.withMeta&&!(e.ctrlKey||e.metaKey)||!i.withMeta&&(e.ctrlKey||e.metaKey))return;I("keypress","hotkeys",`${e.key} pressed`,i.description),i.callback(e)}}},H=new O;function $(){var l;let n=document.querySelector(".JumpDialog"),e=n==null?void 0:n.querySelector(".JumpDialog-body"),t=n==null?void 0:n.querySelector(".JumpDialog-list"),i=n==null?void 0:n.querySelector(".JumpDialog-input"),r=document.querySelector(".js-documentation"),h;function v(){let s=[];if(r){for(let o of r.querySelectorAll("[data-kind]"))s.push(w(o));for(let o of s)o.link.addEventListener("click",function(){n==null||n.close()});return s.sort(function(o,f){return o.lower.localeCompare(f.lower)}),s}}function w(s){var E;let o=document.createElement("a"),f=s.getAttribute("id");o.setAttribute("href","#"+f),o.setAttribute("tabindex","-1"),o.setAttribute("data-gtmc","jump to link");let p=s.getAttribute("data-kind");return{link:o,name:f!=null?f:"",kind:p!=null?p:"",lower:(E=f==null?void 0:f.toLowerCase())!=null?E:""}}let M,L=-1;function b(s){for(M=s,h||(h=v()),a(-1);t!=null&&t.firstChild;)t.firstChild.remove();if(s){let o=s.toLowerCase(),f=[],p=[],E=[],S=(g,y,T)=>g.name.substring(0,y)+""+g.name.substring(y,T)+""+g.name.substring(T);for(let g of h!=null?h:[]){let y=g.name.toLowerCase();if(y===o)g.link.innerHTML=S(g,0,g.name.length),f.push(g);else if(y.startsWith(o))g.link.innerHTML=S(g,0,s.length),p.push(g);else{let T=y.indexOf(o);T>-1&&(g.link.innerHTML=S(g,T,T+s.length),E.push(g))}}for(let g of f.concat(p).concat(E))t==null||t.appendChild(g.link)}else{if(!h||h.length===0){let o=document.createElement("i");o.innerHTML="There are no symbols on this page.",t==null||t.appendChild(o)}for(let o of h!=null?h:[])o.link.innerHTML=o.name+" "+o.kind+"",t==null||t.appendChild(o.link)}e&&(e.scrollTop=0),h!=null&&h.length&&t&&t.children.length>0&&a(0)}function a(s){let o=t==null?void 0:t.children;if(!(!o||!e)){if(L>=0&&o[L].classList.remove("JumpDialog-active"),s>=o.length&&(s=o.length-1),s>=0){o[s].classList.add("JumpDialog-active");let f=o[s].offsetTop-o[0].offsetTop,p=f+o[s].clientHeight;fe.scrollTop+e.clientHeight&&(e.scrollTop=p-e.clientHeight)}L=s}}function u(s){if(L<0)return;let o=L+s;o<0&&(o=0),a(o)}i==null||i.addEventListener("keyup",function(){i.value.toUpperCase()!=M.toUpperCase()&&b(i.value)}),i==null||i.addEventListener("keydown",function(s){switch(s.which){case 38:u(-1),s.preventDefault();break;case 40:u(1),s.preventDefault();break;case 13:L>=0&&t&&(t.children[L].click(),s.preventDefault());break}});let d=document.querySelector(".ShortcutsDialog");H.on("f","open jump to modal",s=>{var o;n!=null&&n.open||d!=null&&d.open||(s.preventDefault(),i&&(i.value=""),(o=n==null?void 0:n.showModal)==null||o.call(n),i==null||i.focus(),b(""))}).on("?","open shortcuts modal",()=>{var s;n!=null&&n.open||d!=null&&d.open||(s=d==null?void 0:d.showModal)==null||s.call(d)});let c=document.querySelector(".js-jumpToInput");c&&c.addEventListener("click",()=>{var s;i&&(i.value=""),b(""),!(n!=null&&n.open||d!=null&&d.open)&&((s=n==null?void 0:n.showModal)==null||s.call(n),i==null||i.focus())}),(l=document.querySelector(".js-openShortcuts"))==null||l.addEventListener("click",()=>{var s;(s=d==null?void 0:d.showModal)==null||s.call(d)})}var G=async function(){if(!["/about"].includes(window.location.pathname))return;let e="h2, h3, h4",t=".LeftNav a",i=document.querySelector(".LeftNav"),r=document.querySelector(".go-Content"),h=!1;function v(c="",l={},...s){if(!c)throw new Error("Provide `type` to create document element.");let o=Object.assign(document.createElement(c),l);return s.forEach(f=>{typeof f=="string"?o.appendChild(document.createTextNode(f)):Array.isArray(f)?f.forEach(p=>o.appendChild(p)):f instanceof HTMLElement&&o.appendChild(f)}),o}function w(){return new Promise((c,l)=>{var f,p,E,S,g,y,T,J,R,_;let s=[],o=[];if(!r||!i)return l(".SiteContent not found.");if(i instanceof HTMLElement&&!((f=i==null?void 0:i.dataset)!=null&&f.hydrate))return c(!0);for(let m of r.querySelectorAll(e))if(m instanceof HTMLElement&&!((p=m==null?void 0:m.dataset)!=null&&p.ignore))switch(m.tagName){case"H2":s=[...s,{id:m.id,label:(E=m==null?void 0:m.dataset)!=null&&E.title?m.dataset.title:(S=m.textContent)!=null?S:""}];break;case"H3":case"H4":(g=s[s.length-1])!=null&&g.subnav?s[s.length-1].subnav&&((_=s[s.length-1].subnav)==null||_.push({id:m.id,label:(J=m==null?void 0:m.dataset)!=null&&J.title?m.dataset.title:(R=m.textContent)!=null?R:""})):s[s.length-1].subnav=[{id:m.id,label:(y=m==null?void 0:m.dataset)!=null&&y.title?m.dataset.title:(T=m.textContent)!=null?T:""}];break}for(let m of s){let V=v("a",{href:"#"+m.id},v("span",{},m.label));if(o=[...o,V],m!=null&&m.subnav){let N=[];for(let K of m.subnav){let z=v("li",{},v("a",{href:"#"+K.id},v("img",{src:"/static/frontend/about/dot.svg",width:"5",height:"5"}),v("span",{},K.label)));N=[...N,z]}let X=v("ul",{className:"LeftSubnav"},N);o=[...o,X]}}return o.forEach(m=>i.appendChild(m)),c(!0)})}function M(){return new Promise(c=>{if(!document.querySelectorAll(t))return c(!0);for(let l of document.querySelectorAll(t))if(l instanceof HTMLAnchorElement&&l.href===location.href){b(l);break}c(!0)})}function L(){return new Promise(c=>{if(!document.querySelectorAll(t))return c(!0);for(let l of document.querySelectorAll(t))l.classList.remove("active");c(!0)})}function b(c){c instanceof HTMLAnchorElement&&L().then(()=>{var s,o,f;c.classList.add("active");let l=(s=c==null?void 0:c.parentNode)==null?void 0:s.parentNode;l instanceof HTMLElement&&((o=l==null?void 0:l.classList)!=null&&o.contains("LeftSubnav"))&&((f=l.previousElementSibling)==null||f.classList.add("active"))})}function a(){u();let c=document.querySelector('[href="'+location.hash+'"]');c instanceof HTMLAnchorElement&&b(c)}function u(){h=!0,setTimeout(()=>{h=!1},200)}function d(){var c;if(window.addEventListener("hashchange",a),r!=null&&r.querySelectorAll(e)){let l=o=>{if(!h&&Array.isArray(o)&&o.length>0){for(let f of o)if(f.isIntersecting&&f.target instanceof HTMLElement){let{id:p}=f.target,E=document.querySelector('[href="#'+p+'"]');E instanceof HTMLAnchorElement&&b(E);break}}},s=new IntersectionObserver(l,{threshold:0,rootMargin:"0px 0px -50% 0px"});for(let o of r.querySelectorAll(e))o instanceof HTMLElement&&!((c=o==null?void 0:o.dataset)!=null&&c.ignore)&&s.observe(o)}}try{await w(),await M(),location.hash&&u(),d()}catch(c){c instanceof Error?console.error(c.message):console.error(c)}};window.addEventListener("load",()=>{var n;for(let e of document.querySelectorAll(".js-clipboard"))new A(e);for(let e of document.querySelectorAll(".js-modal"))new q(e);for(let e of document.querySelectorAll(".js-tooltip"))new x(e);for(let e of document.querySelectorAll(".js-selectNav"))new C(e);for(let e of document.querySelectorAll(".js-carousel"))new k(e);for(let e of document.querySelectorAll(".js-toggleTheme"))e.addEventListener("click",()=>{Y()});(n=document.querySelector(".js-gtmID"))!=null&&n.dataset.gtmid&&window.dataLayer?W(function(){B()}):B(),P(),U(),$(),G(),Z()});H.on("/","focus search",n=>{let e=Array.from(document.querySelectorAll(".js-searchFocus")).pop();e&&!window.navigator.userAgent.includes("Firefox")&&(n.preventDefault(),e.focus())});H.on("y","set canonical url",()=>{var e;let n=(e=document.querySelector(".js-canonicalURLPath"))==null?void 0:e.dataset.canonicalUrlPath;if(n&&n!==""){let t=window.location.hash;t&&(n+=t),window.history.replaceState(null,"",n)}});(function(){I({"gtm.start":new Date().getTime(),event:"gtm.js"})})();function B(){let n=new URLSearchParams(window.location.search),e=n.get("utm_source");if(e!=="gopls"&&e!=="godoc"&&e!=="pkggodev")return;let t=new URL(window.location.href);n.delete("utm_source"),t.search=n.toString(),window.history.replaceState(null,"",t.toString())}function Y(){let n="dark",e=document.documentElement.getAttribute("data-theme");e==="dark"?n="light":e==="light"&&(n="auto");let t="";(location.hostname==="go.dev"||location.hostname.endsWith(".go.dev"))&&(t="domain=.go.dev;"),document.documentElement.setAttribute("data-theme",n),document.cookie=`prefers-color-scheme=${n};${t}path=/;max-age=31536000;`}function Z(){if(!document.cookie.match(/cookie-consent=true/)){let e=document.querySelector(".js-cookieNotice"),t=e==null?void 0:e.querySelector("button");e==null||e.classList.add("Cookie-notice--visible"),t==null||t.addEventListener("click",()=>{let i="";(location.hostname==="go.dev"||location.hostname.endsWith(".go.dev"))&&(i="domain=.go.dev;"),document.cookie=`cookie-consent=true;${i}path=/;max-age=31536000`,e==null||e.remove()})}} + `,(i=e.querySelector(".go-Carousel-prevSlide"))==null||i.addEventListener("click",()=>this.setActive(this.activeIndex-1)),(r=e.querySelector(".go-Carousel-nextSlide"))==null||r.addEventListener("click",()=>this.setActive(this.activeIndex+1)),this.el.append(e)}initDots(){let e=document.createElement("ul");e.classList.add("go-Carousel-dots");for(let n=0;nSlide ${n+1}`,r.addEventListener("click",()=>this.setActive(n)),i.append(r),e.append(i),this.dots.push(r)}this.el.append(e)}initLiveRegion(){this.liveRegion.setAttribute("aria-live","polite"),this.liveRegion.setAttribute("aria-atomic","true"),this.liveRegion.setAttribute("class","go-Carousel-obscured"),this.liveRegion.textContent=`Slide ${this.activeIndex+1} of ${this.slides.length}`,this.el.appendChild(this.liveRegion)}};var A=class{constructor(e){this.el=e;var n,i,r,h,v;this.data=(n=e.dataset.toCopy)!=null?n:e.innerText,!this.data&&((i=e.parentElement)!=null&&i.classList.contains("go-InputGroup"))&&(this.data=(v=this.data||((h=(r=e.parentElement)==null?void 0:r.querySelector("input"))==null?void 0:h.value))!=null?v:""),e.addEventListener("click",w=>this.handleCopyClick(w))}handleCopyClick(e){e.preventDefault();let n=1e3;if(!navigator.clipboard){this.showTooltipText("Unable to copy",n);return}navigator.clipboard.writeText(this.data).then(()=>{this.showTooltipText("Copied!",n)}).catch(()=>{this.showTooltipText("Unable to copy",n)})}showTooltipText(e,n){this.el.setAttribute("data-tooltip",e),setTimeout(()=>this.el.setAttribute("data-tooltip",""),n)}};var x=class{constructor(e){this.el=e;document.addEventListener("click",n=>{this.el.contains(n.target)||this.el.removeAttribute("open")}),this.el.addEventListener("keydown",n=>{n.key==="Escape"&&(this.el.open=!1)})}};var C=class{constructor(e){this.el=e;this.el.addEventListener("change",n=>{let i=n.target,r=i.value;i.value.startsWith("/")||(r="/"+r),window.location.href=r})}};var q=class{constructor(e){this.el=e;window.dialogPolyfill&&window.dialogPolyfill.registerDialog(e),this.init()}init(){let e=document.querySelector(`[aria-controls="${this.el.id}"]`);e&&e.addEventListener("click",()=>{var n;this.el.showModal?this.el.showModal():this.el.setAttribute("opened","true"),(n=this.el.querySelector("input"))==null||n.focus()});for(let n of this.el.querySelectorAll("[data-modal-close]"))n.addEventListener("click",()=>{this.el.close?this.el.close():this.el.removeAttribute("opened")})}};function I(t,e,n,i){var r;(r=window.dataLayer)!=null||(window.dataLayer=[]),typeof t=="string"?window.dataLayer.push({event:t,event_category:e,event_action:n,event_label:i}):window.dataLayer.push(t)}function B(t){var e;(e=window.dataLayer)!=null||(window.dataLayer=[]),window.dataLayer.push(t)}var R=class{constructor(){this.handlers={},document.addEventListener("keydown",e=>this.handleKeyPress(e))}on(e,n,i,r){var h,v;return(v=(h=this.handlers)[e])!=null||(h[e]=new Set),this.handlers[e].add({description:n,callback:i,...r}),this}handleKeyPress(e){var n;for(let i of(n=this.handlers[e.key.toLowerCase()])!=null?n:new Set){if(i.target&&i.target!==e.target)return;let r=e.target;if(!i.target&&((r==null?void 0:r.tagName)==="INPUT"||(r==null?void 0:r.tagName)==="SELECT"||(r==null?void 0:r.tagName)==="TEXTAREA")||r!=null&&r.isContentEditable||i.withMeta&&!(e.ctrlKey||e.metaKey)||!i.withMeta&&(e.ctrlKey||e.metaKey))return;I("keypress","hotkeys",`${e.key} pressed`,i.description),i.callback(e)}}},H=new R;function G(){var l;let t=document.querySelector(".JumpDialog"),e=t==null?void 0:t.querySelector(".JumpDialog-body"),n=t==null?void 0:t.querySelector(".JumpDialog-list"),i=t==null?void 0:t.querySelector(".JumpDialog-input"),r=document.querySelector(".js-documentation"),h;function v(){let s=[];if(r){for(let o of r.querySelectorAll("[data-kind]"))s.push(w(o));for(let o of s)o.link.addEventListener("click",function(){t==null||t.close()});return s.sort(function(o,f){return o.lower.localeCompare(f.lower)}),s}}function w(s){var E;let o=document.createElement("a"),f=s.getAttribute("id");o.setAttribute("href","#"+f),o.setAttribute("tabindex","-1"),o.setAttribute("data-gtmc","jump to link");let p=s.getAttribute("data-kind");return{link:o,name:f!=null?f:"",kind:p!=null?p:"",lower:(E=f==null?void 0:f.toLowerCase())!=null?E:""}}let M,L=-1;function b(s){for(M=s,h||(h=v()),a(-1);n!=null&&n.firstChild;)n.firstChild.remove();if(s){let o=s.toLowerCase(),f=[],p=[],E=[],S=(g,y,T)=>g.name.substring(0,y)+""+g.name.substring(y,T)+""+g.name.substring(T);for(let g of h!=null?h:[]){let y=g.name.toLowerCase();if(y===o)g.link.innerHTML=S(g,0,g.name.length),f.push(g);else if(y.startsWith(o))g.link.innerHTML=S(g,0,s.length),p.push(g);else{let T=y.indexOf(o);T>-1&&(g.link.innerHTML=S(g,T,T+s.length),E.push(g))}}for(let g of f.concat(p).concat(E))n==null||n.appendChild(g.link)}else{if(!h||h.length===0){let o=document.createElement("i");o.innerHTML="There are no symbols on this page.",n==null||n.appendChild(o)}for(let o of h!=null?h:[])o.link.innerHTML=o.name+" "+o.kind+"",n==null||n.appendChild(o.link)}e&&(e.scrollTop=0),h!=null&&h.length&&n&&n.children.length>0&&a(0)}function a(s){let o=n==null?void 0:n.children;if(!(!o||!e)){if(L>=0&&o[L].classList.remove("JumpDialog-active"),s>=o.length&&(s=o.length-1),s>=0){o[s].classList.add("JumpDialog-active");let f=o[s].offsetTop-o[0].offsetTop,p=f+o[s].clientHeight;fe.scrollTop+e.clientHeight&&(e.scrollTop=p-e.clientHeight)}L=s}}function u(s){if(L<0)return;let o=L+s;o<0&&(o=0),a(o)}i==null||i.addEventListener("keyup",function(){i.value.toUpperCase()!=M.toUpperCase()&&b(i.value)}),i==null||i.addEventListener("keydown",function(s){switch(s.which){case 38:u(-1),s.preventDefault();break;case 40:u(1),s.preventDefault();break;case 13:L>=0&&n&&(n.children[L].click(),s.preventDefault());break}});let d=document.querySelector(".ShortcutsDialog");H.on("f","open jump to modal",s=>{var o;t!=null&&t.open||d!=null&&d.open||(s.preventDefault(),i&&(i.value=""),(o=t==null?void 0:t.showModal)==null||o.call(t),i==null||i.focus(),b(""))}).on("?","open shortcuts modal",()=>{var s;t!=null&&t.open||d!=null&&d.open||(s=d==null?void 0:d.showModal)==null||s.call(d)});let c=document.querySelector(".js-jumpToInput");c&&c.addEventListener("click",()=>{var s;i&&(i.value=""),b(""),!(t!=null&&t.open||d!=null&&d.open)&&((s=t==null?void 0:t.showModal)==null||s.call(t),i==null||i.focus())}),(l=document.querySelector(".js-openShortcuts"))==null||l.addEventListener("click",()=>{var s;(s=d==null?void 0:d.showModal)==null||s.call(d)})}var V=async function(){if(![J("/about")].includes(window.location.pathname))return;let e="h2, h3, h4",n=".LeftNav a",i=document.querySelector(".LeftNav"),r=document.querySelector(".go-Content"),h=!1;function v(c="",l={},...s){if(!c)throw new Error("Provide `type` to create document element.");let o=Object.assign(document.createElement(c),l);return s.forEach(f=>{typeof f=="string"?o.appendChild(document.createTextNode(f)):Array.isArray(f)?f.forEach(p=>o.appendChild(p)):f instanceof HTMLElement&&o.appendChild(f)}),o}function w(){return new Promise((c,l)=>{var f,p,E,S,g,y,T,P,_,K;let s=[],o=[];if(!r||!i)return l(".SiteContent not found.");if(i instanceof HTMLElement&&!((f=i==null?void 0:i.dataset)!=null&&f.hydrate))return c(!0);for(let m of r.querySelectorAll(e))if(m instanceof HTMLElement&&!((p=m==null?void 0:m.dataset)!=null&&p.ignore))switch(m.tagName){case"H2":s=[...s,{id:m.id,label:(E=m==null?void 0:m.dataset)!=null&&E.title?m.dataset.title:(S=m.textContent)!=null?S:""}];break;case"H3":case"H4":(g=s[s.length-1])!=null&&g.subnav?s[s.length-1].subnav&&((K=s[s.length-1].subnav)==null||K.push({id:m.id,label:(P=m==null?void 0:m.dataset)!=null&&P.title?m.dataset.title:(_=m.textContent)!=null?_:""})):s[s.length-1].subnav=[{id:m.id,label:(y=m==null?void 0:m.dataset)!=null&&y.title?m.dataset.title:(T=m.textContent)!=null?T:""}];break}for(let m of s){let z=v("a",{href:"#"+m.id},v("span",{},m.label));if(o=[...o,z],m!=null&&m.subnav){let N=[];for(let U of m.subnav){let Y=v("li",{},v("a",{href:"#"+U.id},v("img",{src:J("/static/frontend/about/dot.svg"),width:"5",height:"5"}),v("span",{},U.label)));N=[...N,Y]}let Q=v("ul",{className:"LeftSubnav"},N);o=[...o,Q]}}return o.forEach(m=>i.appendChild(m)),c(!0)})}function M(){return new Promise(c=>{if(!document.querySelectorAll(n))return c(!0);for(let l of document.querySelectorAll(n))if(l instanceof HTMLAnchorElement&&l.href===location.href){b(l);break}c(!0)})}function L(){return new Promise(c=>{if(!document.querySelectorAll(n))return c(!0);for(let l of document.querySelectorAll(n))l.classList.remove("active");c(!0)})}function b(c){c instanceof HTMLAnchorElement&&L().then(()=>{var s,o,f;c.classList.add("active");let l=(s=c==null?void 0:c.parentNode)==null?void 0:s.parentNode;l instanceof HTMLElement&&((o=l==null?void 0:l.classList)!=null&&o.contains("LeftSubnav"))&&((f=l.previousElementSibling)==null||f.classList.add("active"))})}function a(){u();let c=document.querySelector('[href="'+location.hash+'"]');c instanceof HTMLAnchorElement&&b(c)}function u(){h=!0,setTimeout(()=>{h=!1},200)}function d(){var c;if(window.addEventListener("hashchange",a),r!=null&&r.querySelectorAll(e)){let l=o=>{if(!h&&Array.isArray(o)&&o.length>0){for(let f of o)if(f.isIntersecting&&f.target instanceof HTMLElement){let{id:p}=f.target,E=document.querySelector('[href="#'+p+'"]');E instanceof HTMLAnchorElement&&b(E);break}}},s=new IntersectionObserver(l,{threshold:0,rootMargin:"0px 0px -50% 0px"});for(let o of r.querySelectorAll(e))o instanceof HTMLElement&&!((c=o==null?void 0:o.dataset)!=null&&c.ignore)&&s.observe(o)}}try{await w(),await M(),location.hash&&u(),d()}catch(c){c instanceof Error?console.error(c.message):console.error(c)}};window.addEventListener("load",()=>{var t;for(let e of document.querySelectorAll(".js-clipboard"))new A(e);for(let e of document.querySelectorAll(".js-modal"))new q(e);for(let e of document.querySelectorAll(".js-tooltip"))new x(e);for(let e of document.querySelectorAll(".js-selectNav"))new C(e);for(let e of document.querySelectorAll(".js-carousel"))new k(e);for(let e of document.querySelectorAll(".js-toggleTheme"))e.addEventListener("click",()=>{D()});(t=document.querySelector(".js-gtmID"))!=null&&t.dataset.gtmid&&window.dataLayer?B(function(){X()}):X(),$(),W(),G(),V(),F()});H.on("/","focus search",t=>{let e=Array.from(document.querySelectorAll(".js-searchFocus")).pop();e&&!window.navigator.userAgent.includes("Firefox")&&(t.preventDefault(),e.focus())});H.on("y","set canonical url",()=>{var e;let t=(e=document.querySelector(".js-canonicalURLPath"))==null?void 0:e.dataset.canonicalUrlPath;if(t&&t!==""){let n=window.location.hash;n&&(t+=n),window.history.replaceState(null,"",t)}});(function(){I({"gtm.start":new Date().getTime(),event:"gtm.js"})})();function X(){let t=new URLSearchParams(window.location.search),e=t.get("utm_source");if(e!=="gopls"&&e!=="godoc"&&e!=="pkggodev")return;let n=new URL(window.location.href);t.delete("utm_source"),n.search=t.toString(),window.history.replaceState(null,"",n.toString())}function D(){let t="dark",e=document.documentElement.getAttribute("data-theme");e==="dark"?t="light":e==="light"&&(t="auto");let n="";(location.hostname==="go.dev"||location.hostname.endsWith(".go.dev"))&&(n="domain=.go.dev;"),document.documentElement.setAttribute("data-theme",t),document.cookie=`prefers-color-scheme=${t};${n}path=/;max-age=31536000;`}function F(){if(!document.cookie.match(/cookie-consent=true/)){let e=document.querySelector(".js-cookieNotice"),n=e==null?void 0:e.querySelector("button");e==null||e.classList.add("Cookie-notice--visible"),n==null||n.addEventListener("click",()=>{let i="";(location.hostname==="go.dev"||location.hostname.endsWith(".go.dev"))&&(i="domain=.go.dev;"),document.cookie=`cookie-consent=true;${i}path=/;max-age=31536000`,e==null||e.remove()})}} /** * @license * Copyright 2021 The Go Authors. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. */ +/** + * @license + * Copyright 2024 The Go Authors. All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ /*! * @license * Copyright 2019-2020 The Go Authors. All rights reserved. diff --git a/static/frontend/frontend.js.map b/static/frontend/frontend.js.map index aad914663..dca67f047 100644 --- a/static/frontend/frontend.js.map +++ b/static/frontend/frontend.js.map @@ -1,7 +1,7 @@ { "version": 3, - "sources": ["../shared/header/header.ts", "../shared/carousel/carousel.ts", "../shared/clipboard/clipboard.ts", "../shared/tooltip/tooltip.ts", "../shared/outline/select.ts", "../shared/modal/modal.ts", "../shared/analytics/analytics.ts", "../shared/keyboard/keyboard.ts", "../shared/jump/jump.ts", "about/index.ts", "frontend.ts"], - "sourcesContent": ["/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nexport function registerHeaderListeners(): void {\n const header = document.querySelector('.js-header') as HTMLElement;\n\n // Desktop menu hover state\n const menuItemHovers = document.querySelectorAll('.js-desktop-menu-hover');\n menuItemHovers.forEach(menuItemHover => {\n // when user clicks on the dropdown menu item on desktop or mobile,\n // force the menu to stay open until the user clicks off of it.\n menuItemHover.addEventListener('mouseenter', e => {\n const target = e.target as HTMLElement;\n const forced = document.querySelector('.forced-open') as HTMLElement;\n if (forced && forced !== menuItemHover) {\n forced.blur();\n forced.classList.remove('forced-open');\n }\n // prevents menus that have been tabbed into from staying open\n // when you hover over another menu\n target.classList.remove('forced-closed');\n target.classList.add('forced-open');\n });\n\n const toggleForcedOpen = (e: Event) => {\n const target = e.target as HTMLElement;\n const isForced = target?.classList.contains('forced-open');\n const currentTarget = e.currentTarget as HTMLElement;\n if (isForced) {\n currentTarget.removeEventListener('blur', () =>\n currentTarget.classList.remove('forced-open')\n );\n currentTarget.classList.remove('forced-open');\n currentTarget.classList.add('forced-closed');\n currentTarget.blur();\n currentTarget?.parentNode?.addEventListener('mouseout', () => {\n currentTarget.classList.remove('forced-closed');\n });\n } else {\n currentTarget.classList.remove('forced-closed');\n currentTarget.classList.add('forced-open');\n currentTarget.focus();\n currentTarget.addEventListener('blur', () => currentTarget.classList.remove('forced-open'));\n currentTarget?.parentNode?.removeEventListener('mouseout', () => {\n currentTarget.classList.remove('forced-closed');\n });\n }\n currentTarget.focus();\n };\n menuItemHover.addEventListener('click', toggleForcedOpen);\n menuItemHover.addEventListener('focus', e => {\n const target = e.target as HTMLElement;\n target.classList.add('forced-closed');\n target.classList.remove('forced-open');\n });\n\n // ensure desktop submenus are closed when esc is pressed\n const closeSubmenuOnEsc = (e: Event) => {\n const event = e as KeyboardEvent;\n const target = e.target as HTMLElement;\n if (event.key === 'Escape') {\n const forcedOpenItem = document.querySelector('.forced-open') as HTMLElement;\n if (forcedOpenItem) {\n forcedOpenItem.classList.remove('forced-open');\n forcedOpenItem.blur();\n forcedOpenItem.classList.add('forced-closed');\n target?.focus();\n }\n }\n };\n document.addEventListener('keydown', closeSubmenuOnEsc);\n });\n\n // Mobile menu subnav menus\n const headerbuttons = document.querySelectorAll('.js-headerMenuButton');\n headerbuttons.forEach(button => {\n button.addEventListener('click', e => {\n e.preventDefault();\n const isActive = header?.classList.contains('is-active');\n if (isActive) {\n handleNavigationDrawerInactive(header);\n } else {\n handleNavigationDrawerActive(header);\n }\n button.setAttribute('aria-expanded', isActive ? 'true' : 'false');\n });\n });\n\n const scrim = document.querySelector('.js-scrim');\n scrim?.addEventListener('click', e => {\n e.preventDefault();\n\n // find any active submenus and close them\n const activeSubnavs = document.querySelectorAll('.go-NavigationDrawer-submenuItem.is-active');\n activeSubnavs.forEach(subnav => handleNavigationDrawerInactive(subnav as HTMLElement));\n\n handleNavigationDrawerInactive(header);\n\n headerbuttons.forEach(button => {\n button.setAttribute(\n 'aria-expanded',\n header?.classList.contains('is-active') ? 'true' : 'false'\n );\n });\n });\n\n const getNavigationDrawerMenuItems = (navigationDrawer: HTMLElement): HTMLElement[] => {\n if (!navigationDrawer) {\n return [];\n }\n\n const menuItems = Array.from(\n navigationDrawer.querySelectorAll(\n ':scope > .go-NavigationDrawer-nav > .go-NavigationDrawer-list > .go-NavigationDrawer-listItem > a, :scope > .go-NavigationDrawer-nav > .go-NavigationDrawer-list > .go-NavigationDrawer-listItem > .go-Header-socialIcons > a'\n ) || []\n );\n\n const anchorEl = navigationDrawer.querySelector('.go-NavigationDrawer-header > a');\n if (anchorEl) {\n menuItems.unshift(anchorEl);\n }\n return menuItems as HTMLElement[];\n };\n\n const getNavigationDrawerIsSubnav = (navigationDrawer: HTMLElement) => {\n if (!navigationDrawer) {\n return;\n }\n return navigationDrawer.classList.contains('go-NavigationDrawer-submenuItem');\n };\n\n const handleNavigationDrawerInactive = (navigationDrawer: HTMLElement) => {\n if (!navigationDrawer) {\n return;\n }\n const menuItems = getNavigationDrawerMenuItems(navigationDrawer);\n navigationDrawer.classList.remove('is-active');\n const parentMenuItem = navigationDrawer\n .closest('.go-NavigationDrawer-listItem')\n ?.querySelector(':scope > a') as HTMLElement;\n parentMenuItem?.focus();\n menuItems?.forEach(item => item?.setAttribute('tabindex', '-1'));\n if (menuItems && menuItems[0]) {\n menuItems[0].removeEventListener('keydown', handleMenuItemTabLeftFactory(navigationDrawer));\n menuItems[menuItems.length - 1].removeEventListener(\n 'keydown',\n handleMenuItemTabRightFactory(navigationDrawer)\n );\n }\n\n if (navigationDrawer === header) {\n headerbuttons && (headerbuttons[0] as HTMLElement)?.focus();\n }\n };\n\n const handleNavigationDrawerActive = (navigationDrawer: HTMLElement) => {\n const menuItems = getNavigationDrawerMenuItems(navigationDrawer);\n\n navigationDrawer.classList.add('is-active');\n menuItems.forEach(item => item.setAttribute('tabindex', '0'));\n menuItems[0].focus();\n\n menuItems[0].addEventListener('keydown', handleMenuItemTabLeftFactory(navigationDrawer));\n menuItems[menuItems.length - 1].addEventListener(\n 'keydown',\n handleMenuItemTabRightFactory(navigationDrawer)\n );\n };\n\n const handleMenuItemTabLeftFactory = (navigationDrawer: HTMLElement) => {\n return (e: KeyboardEvent) => {\n if (e.key === 'Tab' && e.shiftKey) {\n e.preventDefault();\n handleNavigationDrawerInactive(navigationDrawer);\n }\n };\n };\n\n const handleMenuItemTabRightFactory = (navigationDrawer: HTMLElement) => {\n return (e: KeyboardEvent) => {\n if (e.key === 'Tab' && !e.shiftKey) {\n e.preventDefault();\n handleNavigationDrawerInactive(navigationDrawer);\n }\n };\n };\n\n const prepMobileNavigationDrawer = (navigationDrawer: HTMLElement) => {\n const isSubnav = getNavigationDrawerIsSubnav(navigationDrawer);\n const menuItems = getNavigationDrawerMenuItems(navigationDrawer);\n navigationDrawer.addEventListener('keyup', e => {\n if (e.key === 'Escape') {\n handleNavigationDrawerInactive(navigationDrawer);\n }\n });\n\n menuItems.forEach(item => {\n const parentLi = item.closest('li');\n if (parentLi && parentLi.classList.contains('js-mobile-subnav-trigger')) {\n const submenu = parentLi.querySelector('.go-NavigationDrawer-submenuItem') as HTMLElement;\n item.addEventListener('click', () => {\n handleNavigationDrawerActive(submenu);\n });\n }\n });\n if (isSubnav) {\n handleNavigationDrawerInactive(navigationDrawer);\n navigationDrawer\n ?.querySelector('.go-NavigationDrawer-header')\n ?.addEventListener('click', e => {\n e.preventDefault();\n handleNavigationDrawerInactive(navigationDrawer);\n });\n }\n };\n\n document\n .querySelectorAll('.go-NavigationDrawer')\n .forEach(drawer => prepMobileNavigationDrawer(drawer as HTMLElement));\n\n handleNavigationDrawerInactive(header);\n}\n\nexport function registerSearchFormListeners(): void {\n const searchForm = document.querySelector('.js-searchForm');\n const expandSearch = document.querySelector('.js-expandSearch');\n const input = searchForm?.querySelector('input');\n const headerLogo = document.querySelector('.js-headerLogo');\n const menuButton = document.querySelector('.js-headerMenuButton');\n expandSearch?.addEventListener('click', () => {\n searchForm?.classList.add('go-SearchForm--expanded');\n headerLogo?.classList.add('go-Header-logo--hidden');\n menuButton?.classList.add('go-Header-navOpen--hidden');\n input?.focus();\n });\n document?.addEventListener('click', e => {\n if (!searchForm?.contains(e.target as Node)) {\n searchForm?.classList.remove('go-SearchForm--expanded');\n headerLogo?.classList.remove('go-Header-logo--hidden');\n menuButton?.classList.remove('go-Header-navOpen--hidden');\n }\n });\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * Carousel Controller adds event listeners, accessibility enhancements, and\n * control elements to a carousel component.\n */\nexport class CarouselController {\n /**\n * slides is a collection of slides in the carousel.\n */\n private slides: HTMLLIElement[];\n /**\n * dots is a collection of dot navigation controls, added to the carousel\n * by this controller.\n */\n private dots: HTMLElement[];\n /**\n * liveRegion is a visually hidden element that notifies assitive devices\n * of visual changes to the carousel. They are added to the carousel by\n * this controller.\n */\n private liveRegion: HTMLElement;\n /**\n * activeIndex is the 0-index of the currently active slide.\n */\n private activeIndex: number;\n\n constructor(private el: HTMLElement) {\n this.slides = Array.from(el.querySelectorAll('.go-Carousel-slide'));\n this.dots = [];\n this.liveRegion = document.createElement('div');\n this.activeIndex = Number(el.getAttribute('data-slide-index') ?? 0);\n\n this.initSlides();\n this.initArrows();\n this.initDots();\n this.initLiveRegion();\n }\n\n private initSlides() {\n for (const [i, v] of this.slides.entries()) {\n if (i === this.activeIndex) continue;\n v.setAttribute('aria-hidden', 'true');\n }\n }\n\n private initArrows() {\n const arrows = document.createElement('ul');\n arrows.classList.add('go-Carousel-arrows');\n arrows.innerHTML = `\n
  • \n \n
  • \n
  • \n \n
  • \n `;\n arrows\n .querySelector('.go-Carousel-prevSlide')\n ?.addEventListener('click', () => this.setActive(this.activeIndex - 1));\n arrows\n .querySelector('.go-Carousel-nextSlide')\n ?.addEventListener('click', () => this.setActive(this.activeIndex + 1));\n this.el.append(arrows);\n }\n\n private initDots() {\n const dots = document.createElement('ul');\n dots.classList.add('go-Carousel-dots');\n for (let i = 0; i < this.slides.length; i++) {\n const li = document.createElement('li');\n const button = document.createElement('button');\n button.classList.add('go-Carousel-dot');\n if (i === this.activeIndex) {\n button.classList.add('go-Carousel-dot--active');\n }\n button.innerHTML = `Slide ${i + 1}`;\n button.addEventListener('click', () => this.setActive(i));\n li.append(button);\n dots.append(li);\n this.dots.push(button);\n }\n this.el.append(dots);\n }\n\n private initLiveRegion() {\n this.liveRegion.setAttribute('aria-live', 'polite');\n this.liveRegion.setAttribute('aria-atomic', 'true');\n this.liveRegion.setAttribute('class', 'go-Carousel-obscured');\n this.liveRegion.textContent = `Slide ${this.activeIndex + 1} of ${this.slides.length}`;\n this.el.appendChild(this.liveRegion);\n }\n\n private setActive = (index: number) => {\n this.activeIndex = (index + this.slides.length) % this.slides.length;\n this.el.setAttribute('data-slide-index', String(this.activeIndex));\n for (const d of this.dots) {\n d.classList.remove('go-Carousel-dot--active');\n }\n this.dots[this.activeIndex].classList.add('go-Carousel-dot--active');\n for (const s of this.slides) {\n s.setAttribute('aria-hidden', 'true');\n }\n this.slides[this.activeIndex].removeAttribute('aria-hidden');\n this.liveRegion.textContent = 'Slide ' + (this.activeIndex + 1) + ' of ' + this.slides.length;\n };\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * This class decorates an element to copy arbitrary data attached via a data-\n * attribute to the clipboard.\n */\nexport class ClipboardController {\n /**\n * The data to be copied to the clipboard.\n */\n private data: string;\n\n /**\n * @param el The element that will trigger copying text to the clipboard. The text is\n * expected to be within its data-to-copy attribute.\n */\n constructor(private el: HTMLButtonElement) {\n this.data = el.dataset['toCopy'] ?? el.innerText;\n // if data-to-copy is empty and the button is part of an input group\n // capture the value of the input.\n if (!this.data && el.parentElement?.classList.contains('go-InputGroup')) {\n this.data = (this.data || el.parentElement?.querySelector('input')?.value) ?? '';\n }\n el.addEventListener('click', e => this.handleCopyClick(e));\n }\n\n /**\n * Handles when the primary element is clicked.\n */\n handleCopyClick(e: MouseEvent): void {\n e.preventDefault();\n const TOOLTIP_SHOW_DURATION_MS = 1000;\n\n // This API is not available on iOS.\n if (!navigator.clipboard) {\n this.showTooltipText('Unable to copy', TOOLTIP_SHOW_DURATION_MS);\n return;\n }\n navigator.clipboard\n .writeText(this.data)\n .then(() => {\n this.showTooltipText('Copied!', TOOLTIP_SHOW_DURATION_MS);\n })\n .catch(() => {\n this.showTooltipText('Unable to copy', TOOLTIP_SHOW_DURATION_MS);\n });\n }\n\n /**\n * Shows the given text in a tooltip for a specified amount of time, in milliseconds.\n */\n showTooltipText(text: string, durationMs: number): void {\n this.el.setAttribute('data-tooltip', text);\n setTimeout(() => this.el.setAttribute('data-tooltip', ''), durationMs);\n }\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * ToolTipController handles closing tooltips on external clicks.\n */\nexport class ToolTipController {\n constructor(private el: HTMLDetailsElement) {\n document.addEventListener('click', e => {\n const insideTooltip = this.el.contains(e.target as Element);\n if (!insideTooltip) {\n this.el.removeAttribute('open');\n }\n });\n\n // Add event listener for \"Escape\" keydown to close tooltip\n this.el.addEventListener('keydown', e => {\n if (e.key === 'Escape') {\n this.el.open = false;\n }\n });\n }\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { TreeNavController } from './tree.js';\n\nexport class SelectNavController {\n constructor(private el: Element) {\n this.el.addEventListener('change', e => {\n const target = e.target as HTMLSelectElement;\n let href = target.value;\n if (!target.value.startsWith('/')) {\n href = '/' + href;\n }\n window.location.href = href;\n });\n }\n}\n\nexport function makeSelectNav(tree: TreeNavController): HTMLLabelElement {\n const label = document.createElement('label');\n label.classList.add('go-Label');\n label.setAttribute('aria-label', 'Menu');\n const select = document.createElement('select');\n select.classList.add('go-Select', 'js-selectNav');\n label.appendChild(select);\n const outline = document.createElement('optgroup');\n outline.label = 'Outline';\n select.appendChild(outline);\n const groupMap: Record = {};\n let group: HTMLOptGroupElement;\n for (const t of tree.treeitems) {\n if (Number(t.depth) > 4) continue;\n if (t.groupTreeitem) {\n group = groupMap[t.groupTreeitem.label];\n if (!group) {\n group = groupMap[t.groupTreeitem.label] = document.createElement('optgroup');\n group.label = t.groupTreeitem.label;\n select.appendChild(group);\n }\n } else {\n group = outline;\n }\n const o = document.createElement('option');\n o.label = t.label;\n o.textContent = t.label;\n o.value = (t.el as HTMLAnchorElement).href.replace(window.location.origin, '').replace('/', '');\n group.appendChild(o);\n }\n tree.addObserver(t => {\n const hash = (t.el as HTMLAnchorElement).hash;\n const value = select.querySelector(`[value$=\"${hash}\"]`)?.value;\n if (value) {\n select.value = value;\n }\n }, 50);\n return label;\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\ninterface Window {\n dialogPolyfill?: {\n registerDialog: (el: HTMLDialogElement) => void;\n };\n}\n\ndeclare const window: Window;\n\n/**\n * ModalController registers a dialog element with the polyfill if\n * necessary for the current browser, add adds event listeners to\n * close and open modals.\n */\nexport class ModalController {\n constructor(private el: HTMLDialogElement) {\n if (window.dialogPolyfill) {\n window.dialogPolyfill.registerDialog(el);\n }\n this.init();\n }\n\n init() {\n const button = document.querySelector(`[aria-controls=\"${this.el.id}\"]`);\n if (button) {\n button.addEventListener('click', () => {\n if (this.el.showModal) {\n this.el.showModal();\n } else {\n this.el.setAttribute('opened', 'true');\n }\n this.el.querySelector('input')?.focus();\n });\n }\n for (const btn of this.el.querySelectorAll('[data-modal-close]')) {\n btn.addEventListener('click', () => {\n if (this.el.close) {\n this.el.close();\n } else {\n this.el.removeAttribute('opened');\n }\n });\n }\n }\n}\n", "interface TagManagerEvent {\n /**\n * event is the name of the event, used to filter events in\n * Google Analytics.\n */\n event: string;\n\n /**\n * event_category is a name that you supply as a way to group objects\n * that to analyze. Typically, you will use the same category name\n * multiple times over related UI elements (buttons, links, etc).\n */\n event_category?: string;\n\n /**\n * event_action is used to name the type of event or interaction you\n * want to measure for a particular web object. For example, with a\n * single \"form\" category, you can analyze a number of specific events\n * with this parameter, such as: form entered, form submitted.\n */\n event_action?: string;\n\n /**\n * event_label provide additional information for events that you want\n * to analyze, such as the text label of a link.\n */\n event_label?: string;\n\n /**\n * gtm.start is used to initialize Google Tag Manager.\n */\n 'gtm.start'?: number;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\ndeclare global {\n interface Window {\n dataLayer?: (TagManagerEvent | VoidFunction)[];\n ga?: unknown;\n }\n}\n\n/**\n * track sends events to Google Tag Manager.\n */\nexport function track(\n event: string | TagManagerEvent,\n category?: string,\n action?: string,\n label?: string\n): void {\n window.dataLayer ??= [];\n if (typeof event === 'string') {\n window.dataLayer.push({\n event,\n event_category: category,\n event_action: action,\n event_label: label,\n });\n } else {\n window.dataLayer.push(event);\n }\n}\n\n/**\n * func adds functions to run sequentionally after\n * Google Tag Manager is ready.\n */\nexport function func(fn: () => void): void {\n window.dataLayer ??= [];\n window.dataLayer.push(fn);\n}\n", "/*!\n * @license\n * Copyright 2019-2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { track } from '../analytics/analytics';\n\n/**\n * Options are keyhandler callback options.\n */\ninterface Options {\n /**\n * target is the element the key event should filter on. The\n * default target is the document.\n */\n target?: Element;\n\n /**\n * withMeta specifies if the event callback should fire when\n * the key is pressed with a meta key (ctrl, alt, etc). By\n * default meta keypresses are ignored.\n */\n withMeta?: boolean;\n}\n\n/**\n * KeyHandler is the config for a keyboard event callback.\n */\ninterface KeyHandler extends Options {\n description: string;\n callback: (e: KeyboardEvent) => void;\n}\n\n/**\n * KeyboardController controls event callbacks for sitewide\n * keyboard events. Multiple callbacks can be registered for\n * a single key and by default the controller ignores events\n * for text input targets.\n */\nclass KeyboardController {\n handlers: Record>;\n\n constructor() {\n this.handlers = {};\n document.addEventListener('keydown', e => this.handleKeyPress(e));\n }\n\n /**\n * on registers keyboard event callbacks.\n * @param key the key to register.\n * @param description name of the event.\n * @param callback event callback.\n * @param options set target and withMeta options to override the default behaviors.\n */\n on(key: string, description: string, callback: (e: KeyboardEvent) => void, options?: Options) {\n this.handlers[key] ??= new Set();\n this.handlers[key].add({ description, callback, ...options });\n return this;\n }\n\n private handleKeyPress(e: KeyboardEvent) {\n for (const handler of this.handlers[e.key.toLowerCase()] ?? new Set()) {\n if (handler.target && handler.target !== e.target) {\n return;\n }\n const t = e.target as HTMLElement | null;\n if (\n !handler.target &&\n (t?.tagName === 'INPUT' || t?.tagName === 'SELECT' || t?.tagName === 'TEXTAREA')\n ) {\n return;\n }\n if (t?.isContentEditable) {\n return;\n }\n if (\n (handler.withMeta && !(e.ctrlKey || e.metaKey)) ||\n (!handler.withMeta && (e.ctrlKey || e.metaKey))\n ) {\n return;\n }\n track('keypress', 'hotkeys', `${e.key} pressed`, handler.description);\n handler.callback(e);\n }\n }\n}\n\nexport const keyboard = new KeyboardController();\n", "/*!\n * @license\n * Copyright 2019-2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n// This file implements the behavior of the \"jump to symbol\" dialog for Go\n// package documentation, as well as the simple dialog that displays keyboard\n// shortcuts.\n\n// The DOM for the dialogs is at the bottom of static/frontend/unit/main/_modals.tmpl.\n// The CSS is in static/frontend/unit/main/_modals.css.\n\n// The dialog is activated by pressing the 'f' key. It presents a list\n// (#JumpDialog-list) of all Go symbols displayed in the documentation.\n// Entering text in the dialog's text box (#JumpDialog-filter) restricts the\n// list to symbols containing the text. Clicking on an symbol jumps to\n// its documentation.\n\n// This code is based on\n// https://go.googlesource.com/gddo/+/refs/heads/master/gddo-server/assets/site.js.\n// It was modified to remove the dependence on jquery and bootstrap.\n\nimport { keyboard } from '../keyboard/keyboard';\n\nexport function initModals(): void {\n const jumpDialog = document.querySelector('.JumpDialog');\n const jumpBody = jumpDialog?.querySelector('.JumpDialog-body');\n const jumpList = jumpDialog?.querySelector('.JumpDialog-list');\n const jumpFilter = jumpDialog?.querySelector('.JumpDialog-input');\n const doc = document.querySelector('.js-documentation');\n\n interface JumpListItem {\n link: HTMLAnchorElement;\n name: string;\n kind: string;\n lower: string;\n }\n\n let jumpListItems: JumpListItem[] | undefined; // All the symbols in the doc; computed only once.\n\n // collectJumpListItems returns a list of items, one for each symbol in the\n // documentation on the current page.\n //\n // It uses the data-kind attribute generated in the documentation HTML to find\n // the symbols and their id attributes.\n //\n // If there are no data-kind attributes, then we have older doc; fall back to\n // a less precise method.\n function collectJumpListItems() {\n const items = [];\n if (!doc) return;\n for (const el of doc.querySelectorAll('[data-kind]')) {\n items.push(newJumpListItem(el));\n }\n\n // Clicking on any of the links closes the dialog.\n for (const item of items) {\n item.link.addEventListener('click', function () {\n jumpDialog?.close();\n });\n }\n // Sort case-insensitively by symbol name.\n items.sort(function (a, b) {\n return a.lower.localeCompare(b.lower);\n });\n return items;\n }\n\n // newJumpListItem creates a new item for the DOM element el.\n // An item is an object with:\n // - name: the element's id (which is the symbol name)\n // - kind: the element's kind (function, variable, etc.),\n // - link: a link ('a' tag) to the element\n // - lower: the name in lower case, just for sorting\n function newJumpListItem(el: Element): JumpListItem {\n const a = document.createElement('a');\n const name = el.getAttribute('id');\n a.setAttribute('href', '#' + name);\n a.setAttribute('tabindex', '-1');\n a.setAttribute('data-gtmc', 'jump to link');\n const kind = el.getAttribute('data-kind');\n return {\n link: a,\n name: name ?? '',\n kind: kind ?? '',\n lower: name?.toLowerCase() ?? '', // for sorting\n };\n }\n\n let lastFilterValue: string; // The last contents of the filter text box.\n let activeJumpItem = -1; // The index of the currently active item in the list.\n\n // updateJumpList sets the elements of the dialog list to\n // everything whose name contains filter.\n function updateJumpList(filter: string) {\n lastFilterValue = filter;\n if (!jumpListItems) {\n jumpListItems = collectJumpListItems();\n }\n setActiveJumpItem(-1);\n\n // Remove all children from list.\n while (jumpList?.firstChild) {\n jumpList.firstChild.remove();\n }\n\n if (filter) {\n // A filter is set. We treat the filter as a substring that can appear in\n // an item name (case insensitive), and find the following matches - in\n // order of priority:\n //\n // 1. Exact matches (the filter matches the item's name exactly)\n // 2. Prefix matches (the item's name starts with filter)\n // 3. Infix matches (the filter is a substring of the item's name)\n const filterLowerCase = filter.toLowerCase();\n\n const exactMatches = [];\n const prefixMatches = [];\n const infixMatches = [];\n\n // makeLinkHtml creates the link name HTML for a list item. item is the DOM\n // item. item.name.substr(boldStart, boldEnd) will be bolded.\n const makeLinkHtml = (item: JumpListItem, boldStart: number, boldEnd: number) => {\n return (\n item.name.substring(0, boldStart) +\n '' +\n item.name.substring(boldStart, boldEnd) +\n '' +\n item.name.substring(boldEnd)\n );\n };\n\n for (const item of jumpListItems ?? []) {\n const nameLowerCase = item.name.toLowerCase();\n\n if (nameLowerCase === filterLowerCase) {\n item.link.innerHTML = makeLinkHtml(item, 0, item.name.length);\n exactMatches.push(item);\n } else if (nameLowerCase.startsWith(filterLowerCase)) {\n item.link.innerHTML = makeLinkHtml(item, 0, filter.length);\n prefixMatches.push(item);\n } else {\n const index = nameLowerCase.indexOf(filterLowerCase);\n if (index > -1) {\n item.link.innerHTML = makeLinkHtml(item, index, index + filter.length);\n infixMatches.push(item);\n }\n }\n }\n\n for (const item of exactMatches.concat(prefixMatches).concat(infixMatches)) {\n jumpList?.appendChild(item.link);\n }\n } else {\n if (!jumpListItems || jumpListItems.length === 0) {\n const msg = document.createElement('i');\n msg.innerHTML = 'There are no symbols on this page.';\n jumpList?.appendChild(msg);\n }\n // No filter set; display all items in their existing order.\n for (const item of jumpListItems ?? []) {\n item.link.innerHTML = item.name + ' ' + item.kind + '';\n jumpList?.appendChild(item.link);\n }\n }\n\n if (jumpBody) {\n jumpBody.scrollTop = 0;\n }\n if (jumpListItems?.length && jumpList && jumpList.children.length > 0) {\n setActiveJumpItem(0);\n }\n }\n\n // Set the active jump item to n.\n function setActiveJumpItem(n: number) {\n const cs = jumpList?.children as HTMLCollectionOf | null | undefined;\n if (!cs || !jumpBody) {\n return;\n }\n if (activeJumpItem >= 0) {\n cs[activeJumpItem].classList.remove('JumpDialog-active');\n }\n if (n >= cs.length) {\n n = cs.length - 1;\n }\n if (n >= 0) {\n cs[n].classList.add('JumpDialog-active');\n\n // Scroll so the active item is visible.\n // For some reason cs[n].scrollIntoView() doesn't behave as I'd expect:\n // it moves the entire dialog box in the viewport.\n\n // Get the top and bottom of the active item relative to jumpBody.\n const activeTop = cs[n].offsetTop - cs[0].offsetTop;\n const activeBottom = activeTop + cs[n].clientHeight;\n if (activeTop < jumpBody.scrollTop) {\n // Off the top; scroll up.\n jumpBody.scrollTop = activeTop;\n } else if (activeBottom > jumpBody.scrollTop + jumpBody.clientHeight) {\n // Off the bottom; scroll down.\n jumpBody.scrollTop = activeBottom - jumpBody.clientHeight;\n }\n }\n activeJumpItem = n;\n }\n\n // Increment the activeJumpItem by delta.\n function incActiveJumpItem(delta: number) {\n if (activeJumpItem < 0) {\n return;\n }\n let n = activeJumpItem + delta;\n if (n < 0) {\n n = 0;\n }\n setActiveJumpItem(n);\n }\n\n // Pressing a key in the filter updates the list (if the filter actually changed).\n jumpFilter?.addEventListener('keyup', function () {\n if (jumpFilter.value.toUpperCase() != lastFilterValue.toUpperCase()) {\n updateJumpList(jumpFilter.value);\n }\n });\n\n // Pressing enter in the filter selects the first element in the list.\n jumpFilter?.addEventListener('keydown', function (event) {\n const upArrow = 38;\n const downArrow = 40;\n const enterKey = 13;\n switch (event.which) {\n case upArrow:\n incActiveJumpItem(-1);\n event.preventDefault();\n break;\n case downArrow:\n incActiveJumpItem(1);\n event.preventDefault();\n break;\n case enterKey:\n if (activeJumpItem >= 0) {\n if (jumpList) {\n (jumpList.children[activeJumpItem] as HTMLElement).click();\n event.preventDefault();\n }\n }\n break;\n }\n });\n\n const shortcutsDialog = document.querySelector('.ShortcutsDialog');\n\n // - Pressing 'f' or 'F' opens the jump-to-symbol dialog.\n // - Pressing '?' opens up the shortcut dialog.\n // Ignore a keypress if a dialog is already open, or if it is pressed on a\n // component that wants to consume it.\n keyboard\n .on('f', 'open jump to modal', e => {\n if (jumpDialog?.open || shortcutsDialog?.open) {\n return;\n }\n e.preventDefault();\n if (jumpFilter) {\n jumpFilter.value = '';\n }\n jumpDialog?.showModal?.();\n jumpFilter?.focus();\n updateJumpList('');\n })\n .on('?', 'open shortcuts modal', () => {\n if (jumpDialog?.open || shortcutsDialog?.open) {\n return;\n }\n shortcutsDialog?.showModal?.();\n });\n\n const jumpOutlineInput = document.querySelector('.js-jumpToInput');\n if (jumpOutlineInput) {\n jumpOutlineInput.addEventListener('click', () => {\n if (jumpFilter) {\n jumpFilter.value = '';\n }\n updateJumpList('');\n if (jumpDialog?.open || shortcutsDialog?.open) {\n return;\n }\n jumpDialog?.showModal?.();\n jumpFilter?.focus();\n });\n }\n\n document.querySelector('.js-openShortcuts')?.addEventListener('click', () => {\n shortcutsDialog?.showModal?.();\n });\n}\n", "/**\n * @license\n * Copyright 2022 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * Left Navigation.\n */\nexport const initJumpLinks = async function () {\n const pagesWithJumpLinks = ['/about'];\n if (!pagesWithJumpLinks.includes(window.location.pathname)) {\n // stop the file from doing anything else if the page doesn't have jumplinks\n return;\n }\n\n // these might be generated or not so don't grab references to the elements until actually need them.\n const titles = 'h2, h3, h4';\n const nav = '.LeftNav a';\n // these are always in the dom so we can get them now and throw errors if they're not.\n const leftNav = document.querySelector('.LeftNav');\n const siteContent = document.querySelector('.go-Content');\n let isObserverDisabled = false;\n\n /**\n * El function\n * @example el('h1', {className: 'title'}, 'Welcome to the site');\n * @example el('ul', {className: 'list'}, el('li', {}, 'Item one'), el('li', {}, 'Item two'), el('li', {}, 'Item three'));\n * @example el('img', {src: '/url.svg'});\n */\n function el(\n type = '',\n props: { [key: string]: string } = {},\n ...children: (HTMLElement | HTMLElement[] | string | undefined)[]\n ) {\n // Error, no type declared.\n if (!type) {\n throw new Error('Provide `type` to create document element.');\n }\n\n // Create element with optional attribute props\n const docEl = Object.assign(document.createElement(type), props);\n\n // Children: array containing strings or elements\n children.forEach(child => {\n if (typeof child === 'string') {\n docEl.appendChild(document.createTextNode(child));\n } else if (Array.isArray(child)) {\n child.forEach(c => docEl.appendChild(c));\n } else if (child instanceof HTMLElement) {\n docEl.appendChild(child);\n }\n });\n\n return docEl;\n }\n /** Build Nav if data hydrate is present. */\n function buildNav() {\n return new Promise((resolve, reject) => {\n let navItems: { id: string; label: string; subnav?: { id: string; label: string }[] }[] = [];\n let elements: HTMLElement[] = [];\n\n if (!siteContent || !leftNav) {\n return reject('.SiteContent not found.');\n }\n if (leftNav instanceof HTMLElement && !leftNav?.dataset?.hydrate) {\n return resolve(true);\n }\n\n for (const title of siteContent.querySelectorAll(titles)) {\n if (title instanceof HTMLElement && !title?.dataset?.ignore) {\n switch (title.tagName) {\n case 'H2':\n navItems = [\n ...navItems,\n {\n id: title.id,\n label: title?.dataset?.title ? title.dataset.title : title.textContent ?? '',\n },\n ];\n break;\n\n case 'H3':\n case 'H4':\n if (!navItems[navItems.length - 1]?.subnav) {\n navItems[navItems.length - 1].subnav = [\n {\n id: title.id,\n label: title?.dataset?.title ? title.dataset.title : title.textContent ?? '',\n },\n ];\n } else if (navItems[navItems.length - 1].subnav) {\n navItems[navItems.length - 1].subnav?.push({\n id: title.id,\n label: title?.dataset?.title ? title.dataset.title : title.textContent ?? '',\n });\n }\n break;\n }\n }\n }\n\n for (const navItem of navItems) {\n const link = el('a', { href: '#' + navItem.id }, el('span', {}, navItem.label));\n elements = [...elements, link];\n if (navItem?.subnav) {\n let subLinks: HTMLElement[] = [];\n for (const subnavItem of navItem.subnav) {\n const subItem = el(\n 'li',\n {},\n el(\n 'a',\n { href: '#' + subnavItem.id },\n el('img', { src: '/static/frontend/about/dot.svg', width: '5', height: '5' }),\n el('span', {}, subnavItem.label)\n )\n );\n subLinks = [...subLinks, subItem];\n }\n const list = el('ul', { className: 'LeftSubnav' }, subLinks);\n elements = [...elements, list];\n }\n }\n\n elements.forEach(element => leftNav.appendChild(element));\n\n return resolve(true);\n });\n }\n /**\n * Set the correct active element.\n */\n function setNav() {\n return new Promise(resolve => {\n if (!document.querySelectorAll(nav)) return resolve(true);\n for (const a of document.querySelectorAll(nav)) {\n if (a instanceof HTMLAnchorElement && a.href === location.href) {\n setElementActive(a);\n break;\n }\n }\n resolve(true);\n });\n }\n /** resetNav: removes all .active from nav elements */\n function resetNav() {\n return new Promise(resolve => {\n if (!document.querySelectorAll(nav)) return resolve(true);\n for (const a of document.querySelectorAll(nav)) {\n a.classList.remove('active');\n }\n resolve(true);\n });\n }\n /** setElementActive: controls resetting nav and highlighting the appropriate nav items */\n function setElementActive(element: HTMLAnchorElement) {\n if (element instanceof HTMLAnchorElement) {\n resetNav().then(() => {\n element.classList.add('active');\n const parent = element?.parentNode?.parentNode;\n if (parent instanceof HTMLElement && parent?.classList?.contains('LeftSubnav')) {\n parent.previousElementSibling?.classList.add('active');\n }\n });\n }\n }\n /** setLinkManually: disables observer and selects the clicked nav item. */\n function setLinkManually() {\n delayObserver();\n const link = document.querySelector('[href=\"' + location.hash + '\"]');\n if (link instanceof HTMLAnchorElement) {\n setElementActive(link);\n }\n }\n /** delayObserver: Quick on off switch for intersection observer. */\n function delayObserver() {\n isObserverDisabled = true;\n setTimeout(() => {\n isObserverDisabled = false;\n }, 200);\n }\n /** observeSections: kicks off observation of titles as well as manual clicks with hashchange */\n function observeSections() {\n window.addEventListener('hashchange', setLinkManually);\n\n if (siteContent?.querySelectorAll(titles)) {\n const callback: IntersectionObserverCallback = entries => {\n if (!isObserverDisabled && Array.isArray(entries) && entries.length > 0) {\n for (const entry of entries) {\n if (entry.isIntersecting && entry.target instanceof HTMLElement) {\n const { id } = entry.target;\n const link = document.querySelector('[href=\"#' + id + '\"]');\n if (link instanceof HTMLAnchorElement) {\n setElementActive(link);\n }\n break;\n }\n }\n }\n };\n // rootMargin is important when multiple sections are in the observable area **on page load**.\n // they will still be highlighted on scroll because of the root margin.\n const ob = new IntersectionObserver(callback, {\n threshold: 0,\n rootMargin: '0px 0px -50% 0px',\n });\n for (const title of siteContent.querySelectorAll(titles)) {\n if (title instanceof HTMLElement && !title?.dataset?.ignore) {\n ob.observe(title);\n }\n }\n }\n }\n\n try {\n await buildNav();\n await setNav();\n if (location.hash) {\n delayObserver();\n }\n observeSections();\n } catch (e) {\n if (e instanceof Error) {\n console.error(e.message);\n } else {\n console.error(e);\n }\n }\n};\n", "/**\n * @license\n * Copyright 2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { registerHeaderListeners, registerSearchFormListeners } from 'static/shared/header/header';\nimport { CarouselController } from 'static/shared/carousel/carousel';\nimport { ClipboardController } from 'static/shared/clipboard/clipboard';\nimport { ToolTipController } from 'static/shared/tooltip/tooltip';\nimport { SelectNavController } from 'static/shared/outline/select';\nimport { ModalController } from 'static/shared/modal/modal';\nimport { initModals } from 'static/shared/jump/jump';\n\nimport { keyboard } from 'static/shared/keyboard/keyboard';\nimport * as analytics from 'static/shared/analytics/analytics';\nimport { initJumpLinks } from './about/index';\n\nwindow.addEventListener('load', () => {\n for (const el of document.querySelectorAll('.js-clipboard')) {\n new ClipboardController(el);\n }\n\n for (const el of document.querySelectorAll('.js-modal')) {\n new ModalController(el);\n }\n\n for (const t of document.querySelectorAll('.js-tooltip')) {\n new ToolTipController(t);\n }\n\n for (const el of document.querySelectorAll('.js-selectNav')) {\n new SelectNavController(el);\n }\n\n for (const el of document.querySelectorAll('.js-carousel')) {\n new CarouselController(el);\n }\n\n for (const el of document.querySelectorAll('.js-toggleTheme')) {\n el.addEventListener('click', () => {\n toggleTheme();\n });\n }\n\n if (document.querySelector('.js-gtmID')?.dataset.gtmid && window.dataLayer) {\n analytics.func(function () {\n removeUTMSource();\n });\n } else {\n removeUTMSource();\n }\n\n registerHeaderListeners();\n registerSearchFormListeners();\n initModals();\n initJumpLinks();\n registerCookieNotice();\n});\n\n// Pressing '/' focuses the search box\nkeyboard.on('/', 'focus search', e => {\n const searchInput = Array.from(\n document.querySelectorAll('.js-searchFocus')\n ).pop();\n // Favoring the Firefox quick find feature over search input\n // focus. See: https://github.com/golang/go/issues/41093.\n if (searchInput && !window.navigator.userAgent.includes('Firefox')) {\n e.preventDefault();\n searchInput.focus();\n }\n});\n\n// Pressing 'y' changes the browser URL to the canonical URL\n// without triggering a reload.\nkeyboard.on('y', 'set canonical url', () => {\n let canonicalURLPath = document.querySelector('.js-canonicalURLPath')?.dataset[\n 'canonicalUrlPath'\n ];\n if (canonicalURLPath && canonicalURLPath !== '') {\n const fragment = window.location.hash;\n if (fragment) {\n canonicalURLPath += fragment;\n }\n window.history.replaceState(null, '', canonicalURLPath);\n }\n});\n\n/**\n * setupGoogleTagManager initializes Google Tag Manager.\n */\n(function setupGoogleTagManager() {\n analytics.track({\n 'gtm.start': new Date().getTime(),\n event: 'gtm.js',\n });\n})();\n\n/**\n * removeUTMSource removes the utm_source GET parameter if present.\n * This is done using JavaScript, so that the utm_source is still\n * captured by Google Analytics.\n */\nfunction removeUTMSource() {\n const urlParams = new URLSearchParams(window.location.search);\n const utmSource = urlParams.get('utm_source');\n if (utmSource !== 'gopls' && utmSource !== 'godoc' && utmSource !== 'pkggodev') {\n return;\n }\n\n /** Strip the utm_source query parameter and replace the URL. **/\n const newURL = new URL(window.location.href);\n urlParams.delete('utm_source');\n newURL.search = urlParams.toString();\n window.history.replaceState(null, '', newURL.toString());\n}\n\n/**\n * toggleTheme switches the preferred color scheme between auto, light, and dark.\n */\nfunction toggleTheme() {\n let nextTheme = 'dark';\n const theme = document.documentElement.getAttribute('data-theme');\n if (theme === 'dark') {\n nextTheme = 'light';\n } else if (theme === 'light') {\n nextTheme = 'auto';\n }\n let domain = '';\n if (location.hostname === 'go.dev' || location.hostname.endsWith('.go.dev')) {\n domain = 'domain=.go.dev;';\n }\n document.documentElement.setAttribute('data-theme', nextTheme);\n document.cookie = `prefers-color-scheme=${nextTheme};${domain}path=/;max-age=31536000;`;\n}\n\n/**\n * registerCookieNotice makes the cookie notice visible and adds listeners to dismiss it\n * if it has not yet been acknowledge by the user.\n */\nfunction registerCookieNotice() {\n const themeCookie = document.cookie.match(/cookie-consent=true/);\n if (!themeCookie) {\n const notice = document.querySelector('.js-cookieNotice');\n const button = notice?.querySelector('button');\n notice?.classList.add('Cookie-notice--visible');\n button?.addEventListener('click', () => {\n let domain = '';\n if (location.hostname === 'go.dev' || location.hostname.endsWith('.go.dev')) {\n // Apply the cookie to *.go.dev.\n domain = 'domain=.go.dev;';\n }\n document.cookie = `cookie-consent=true;${domain}path=/;max-age=31536000`;\n notice?.remove();\n });\n }\n}\n"], - "mappings": "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", - "names": ["registerHeaderListeners", "header", "menuItemHover", "e", "target", "forced", "toggleForcedOpen", "_a", "_b", "isForced", "currentTarget", "closeSubmenuOnEsc", "event", "forcedOpenItem", "headerbuttons", "button", "isActive", "handleNavigationDrawerInactive", "handleNavigationDrawerActive", "scrim", "subnav", "getNavigationDrawerMenuItems", "navigationDrawer", "menuItems", "anchorEl", "getNavigationDrawerIsSubnav", "parentMenuItem", "item", "handleMenuItemTabLeftFactory", "handleMenuItemTabRightFactory", "prepMobileNavigationDrawer", "isSubnav", "parentLi", "submenu", "drawer", "registerSearchFormListeners", "searchForm", "expandSearch", "input", "headerLogo", "menuButton", "CarouselController", "el", "index", "d", "s", "_a", "i", "v", "_b", "arrows", "dots", "li", "button", "ClipboardController", "el", "_a", "_b", "_c", "_d", "_e", "e", "TOOLTIP_SHOW_DURATION_MS", "text", "durationMs", "ToolTipController", "el", "e", "SelectNavController", "el", "e", "target", "href", "ModalController", "el", "button", "_a", "btn", "track", "event", "category", "action", "label", "_a", "func", "fn", "KeyboardController", "key", "description", "callback", "options", "_a", "_b", "handler", "t", "track", "keyboard", "initModals", "_a", "jumpDialog", "jumpBody", "jumpList", "jumpFilter", "doc", "jumpListItems", "collectJumpListItems", "items", "el", "newJumpListItem", "item", "a", "b", "name", "kind", "lastFilterValue", "activeJumpItem", "updateJumpList", "filter", "setActiveJumpItem", "filterLowerCase", "exactMatches", "prefixMatches", "infixMatches", "makeLinkHtml", "boldStart", "boldEnd", "nameLowerCase", "index", "msg", "n", "cs", "activeTop", "activeBottom", "incActiveJumpItem", "delta", "event", "shortcutsDialog", "keyboard", "e", "jumpOutlineInput", "initJumpLinks", "titles", "nav", "leftNav", "siteContent", "isObserverDisabled", "el", "type", "props", "children", "docEl", "child", "c", "buildNav", "resolve", "reject", "_a", "_b", "_c", "_d", "_e", "_f", "_g", "_h", "_i", "_j", "navItems", "elements", "title", "navItem", "link", "subLinks", "subnavItem", "subItem", "list", "element", "setNav", "a", "setElementActive", "resetNav", "parent", "setLinkManually", "delayObserver", "observeSections", "callback", "entries", "entry", "id", "ob", "e", "_a", "el", "ClipboardController", "ModalController", "t", "ToolTipController", "SelectNavController", "CarouselController", "toggleTheme", "func", "removeUTMSource", "registerHeaderListeners", "registerSearchFormListeners", "initModals", "initJumpLinks", "registerCookieNotice", "keyboard", "e", "searchInput", "canonicalURLPath", "fragment", "track", "urlParams", "utmSource", "newURL", "nextTheme", "theme", "domain", "notice", "button"] + "sources": ["../shared/header/header.ts", "../shared/base-path/base-path.ts", "../shared/carousel/carousel.ts", "../shared/clipboard/clipboard.ts", "../shared/tooltip/tooltip.ts", "../shared/outline/select.ts", "../shared/modal/modal.ts", "../shared/analytics/analytics.ts", "../shared/keyboard/keyboard.ts", "../shared/jump/jump.ts", "about/index.ts", "frontend.ts"], + "sourcesContent": ["/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nexport function registerHeaderListeners(): void {\n const header = document.querySelector('.js-header') as HTMLElement;\n\n // Desktop menu hover state\n const menuItemHovers = document.querySelectorAll('.js-desktop-menu-hover');\n menuItemHovers.forEach(menuItemHover => {\n // when user clicks on the dropdown menu item on desktop or mobile,\n // force the menu to stay open until the user clicks off of it.\n menuItemHover.addEventListener('mouseenter', e => {\n const target = e.target as HTMLElement;\n const forced = document.querySelector('.forced-open') as HTMLElement;\n if (forced && forced !== menuItemHover) {\n forced.blur();\n forced.classList.remove('forced-open');\n }\n // prevents menus that have been tabbed into from staying open\n // when you hover over another menu\n target.classList.remove('forced-closed');\n target.classList.add('forced-open');\n });\n\n const toggleForcedOpen = (e: Event) => {\n const target = e.target as HTMLElement;\n const isForced = target?.classList.contains('forced-open');\n const currentTarget = e.currentTarget as HTMLElement;\n if (isForced) {\n currentTarget.removeEventListener('blur', () =>\n currentTarget.classList.remove('forced-open')\n );\n currentTarget.classList.remove('forced-open');\n currentTarget.classList.add('forced-closed');\n currentTarget.blur();\n currentTarget?.parentNode?.addEventListener('mouseout', () => {\n currentTarget.classList.remove('forced-closed');\n });\n } else {\n currentTarget.classList.remove('forced-closed');\n currentTarget.classList.add('forced-open');\n currentTarget.focus();\n currentTarget.addEventListener('blur', () => currentTarget.classList.remove('forced-open'));\n currentTarget?.parentNode?.removeEventListener('mouseout', () => {\n currentTarget.classList.remove('forced-closed');\n });\n }\n currentTarget.focus();\n };\n menuItemHover.addEventListener('click', toggleForcedOpen);\n menuItemHover.addEventListener('focus', e => {\n const target = e.target as HTMLElement;\n target.classList.add('forced-closed');\n target.classList.remove('forced-open');\n });\n\n // ensure desktop submenus are closed when esc is pressed\n const closeSubmenuOnEsc = (e: Event) => {\n const event = e as KeyboardEvent;\n const target = e.target as HTMLElement;\n if (event.key === 'Escape') {\n const forcedOpenItem = document.querySelector('.forced-open') as HTMLElement;\n if (forcedOpenItem) {\n forcedOpenItem.classList.remove('forced-open');\n forcedOpenItem.blur();\n forcedOpenItem.classList.add('forced-closed');\n target?.focus();\n }\n }\n };\n document.addEventListener('keydown', closeSubmenuOnEsc);\n });\n\n // Mobile menu subnav menus\n const headerbuttons = document.querySelectorAll('.js-headerMenuButton');\n headerbuttons.forEach(button => {\n button.addEventListener('click', e => {\n e.preventDefault();\n const isActive = header?.classList.contains('is-active');\n if (isActive) {\n handleNavigationDrawerInactive(header);\n } else {\n handleNavigationDrawerActive(header);\n }\n button.setAttribute('aria-expanded', isActive ? 'true' : 'false');\n });\n });\n\n const scrim = document.querySelector('.js-scrim');\n scrim?.addEventListener('click', e => {\n e.preventDefault();\n\n // find any active submenus and close them\n const activeSubnavs = document.querySelectorAll('.go-NavigationDrawer-submenuItem.is-active');\n activeSubnavs.forEach(subnav => handleNavigationDrawerInactive(subnav as HTMLElement));\n\n handleNavigationDrawerInactive(header);\n\n headerbuttons.forEach(button => {\n button.setAttribute(\n 'aria-expanded',\n header?.classList.contains('is-active') ? 'true' : 'false'\n );\n });\n });\n\n const getNavigationDrawerMenuItems = (navigationDrawer: HTMLElement): HTMLElement[] => {\n if (!navigationDrawer) {\n return [];\n }\n\n const menuItems = Array.from(\n navigationDrawer.querySelectorAll(\n ':scope > .go-NavigationDrawer-nav > .go-NavigationDrawer-list > .go-NavigationDrawer-listItem > a, :scope > .go-NavigationDrawer-nav > .go-NavigationDrawer-list > .go-NavigationDrawer-listItem > .go-Header-socialIcons > a'\n ) || []\n );\n\n const anchorEl = navigationDrawer.querySelector('.go-NavigationDrawer-header > a');\n if (anchorEl) {\n menuItems.unshift(anchorEl);\n }\n return menuItems as HTMLElement[];\n };\n\n const getNavigationDrawerIsSubnav = (navigationDrawer: HTMLElement) => {\n if (!navigationDrawer) {\n return;\n }\n return navigationDrawer.classList.contains('go-NavigationDrawer-submenuItem');\n };\n\n const handleNavigationDrawerInactive = (navigationDrawer: HTMLElement) => {\n if (!navigationDrawer) {\n return;\n }\n const menuItems = getNavigationDrawerMenuItems(navigationDrawer);\n navigationDrawer.classList.remove('is-active');\n const parentMenuItem = navigationDrawer\n .closest('.go-NavigationDrawer-listItem')\n ?.querySelector(':scope > a') as HTMLElement;\n parentMenuItem?.focus();\n menuItems?.forEach(item => item?.setAttribute('tabindex', '-1'));\n if (menuItems && menuItems[0]) {\n menuItems[0].removeEventListener('keydown', handleMenuItemTabLeftFactory(navigationDrawer));\n menuItems[menuItems.length - 1].removeEventListener(\n 'keydown',\n handleMenuItemTabRightFactory(navigationDrawer)\n );\n }\n\n if (navigationDrawer === header) {\n headerbuttons && (headerbuttons[0] as HTMLElement)?.focus();\n }\n };\n\n const handleNavigationDrawerActive = (navigationDrawer: HTMLElement) => {\n const menuItems = getNavigationDrawerMenuItems(navigationDrawer);\n\n navigationDrawer.classList.add('is-active');\n menuItems.forEach(item => item.setAttribute('tabindex', '0'));\n menuItems[0].focus();\n\n menuItems[0].addEventListener('keydown', handleMenuItemTabLeftFactory(navigationDrawer));\n menuItems[menuItems.length - 1].addEventListener(\n 'keydown',\n handleMenuItemTabRightFactory(navigationDrawer)\n );\n };\n\n const handleMenuItemTabLeftFactory = (navigationDrawer: HTMLElement) => {\n return (e: KeyboardEvent) => {\n if (e.key === 'Tab' && e.shiftKey) {\n e.preventDefault();\n handleNavigationDrawerInactive(navigationDrawer);\n }\n };\n };\n\n const handleMenuItemTabRightFactory = (navigationDrawer: HTMLElement) => {\n return (e: KeyboardEvent) => {\n if (e.key === 'Tab' && !e.shiftKey) {\n e.preventDefault();\n handleNavigationDrawerInactive(navigationDrawer);\n }\n };\n };\n\n const prepMobileNavigationDrawer = (navigationDrawer: HTMLElement) => {\n const isSubnav = getNavigationDrawerIsSubnav(navigationDrawer);\n const menuItems = getNavigationDrawerMenuItems(navigationDrawer);\n navigationDrawer.addEventListener('keyup', e => {\n if (e.key === 'Escape') {\n handleNavigationDrawerInactive(navigationDrawer);\n }\n });\n\n menuItems.forEach(item => {\n const parentLi = item.closest('li');\n if (parentLi && parentLi.classList.contains('js-mobile-subnav-trigger')) {\n const submenu = parentLi.querySelector('.go-NavigationDrawer-submenuItem') as HTMLElement;\n item.addEventListener('click', () => {\n handleNavigationDrawerActive(submenu);\n });\n }\n });\n if (isSubnav) {\n handleNavigationDrawerInactive(navigationDrawer);\n navigationDrawer\n ?.querySelector('.go-NavigationDrawer-header')\n ?.addEventListener('click', e => {\n e.preventDefault();\n handleNavigationDrawerInactive(navigationDrawer);\n });\n }\n };\n\n document\n .querySelectorAll('.go-NavigationDrawer')\n .forEach(drawer => prepMobileNavigationDrawer(drawer as HTMLElement));\n\n handleNavigationDrawerInactive(header);\n}\n\nexport function registerSearchFormListeners(): void {\n const searchForm = document.querySelector('.js-searchForm');\n const expandSearch = document.querySelector('.js-expandSearch');\n const input = searchForm?.querySelector('input');\n const headerLogo = document.querySelector('.js-headerLogo');\n const menuButton = document.querySelector('.js-headerMenuButton');\n expandSearch?.addEventListener('click', () => {\n searchForm?.classList.add('go-SearchForm--expanded');\n headerLogo?.classList.add('go-Header-logo--hidden');\n menuButton?.classList.add('go-Header-navOpen--hidden');\n input?.focus();\n });\n document?.addEventListener('click', e => {\n if (!searchForm?.contains(e.target as Node)) {\n searchForm?.classList.remove('go-SearchForm--expanded');\n headerLogo?.classList.remove('go-Header-logo--hidden');\n menuButton?.classList.remove('go-Header-navOpen--hidden');\n }\n });\n}\n", "/**\n * @license\n * Copyright 2024 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * \u7AD9\u70B9 URL \u5B50\u8DEF\u5F84\u524D\u7F00\uFF0C\u4ECE \u8BFB\u51FA\u3002\n *\n * fork \u7528 -base-path=/gogodocs \u542F\u52A8\u65F6\uFF0Cserver \u7AEF\u6A21\u677F\u628A attribute \u5199\u6210\n * \"/gogodocs\"\uFF1B\u4E0A\u6E38\u516C\u7F51 pkg.go.dev \u9ED8\u8BA4\u6302\u6839\uFF0Cattribute \u4E3A\u7A7A\u5B57\u7B26\u4E32\u3002\n *\n * \u5728 \u4E4B\u5916\u7684\u811A\u672C\u521D\u59CB\u5316\u9636\u6BB5\u8C03\u7528\u90FD\u5B89\u5168\u2014\u2014documentElement \u4E00\u5B9A\u5B58\u5728\u3002\n */\nexport function getBasePath(): string {\n return document.documentElement.dataset.basePath ?? '';\n}\n\n/**\n * \u7ED9\u4EE5 / \u5F00\u5934\u7684\u7EDD\u5BF9\u8DEF\u5F84\u52A0 BasePath \u524D\u7F00\u3002\n *\n * \u7528\u4E8E fetch URL / innerHTML src / location \u6BD4\u8F83\u7B49\u4EFB\u4F55\u9700\u8981\u62FC\u7AD9\u70B9\u7EDD\u5BF9 path\n * \u7684\u5730\u65B9\u3002\u975E\u7EDD\u5BF9\u8DEF\u5F84\uFF08\u4E0D\u4EE5 / \u5F00\u5934\uFF09\u539F\u6837\u8FD4\u56DE\u2014\u2014caller \u81EA\u5DF1\u4FDD\u8BC1\u8BED\u4E49\u3002\n *\n * \u4F8B\uFF1A\n * abs('/play/share') // \u2192 '/gogodocs/play/share' \u6216 '/play/share'\n * abs('/static/foo.svg') // \u2192 '/gogodocs/static/foo.svg' \u6216 '/static/foo.svg'\n * abs('relative/x') // \u2192 'relative/x'\uFF08\u4E0D\u52A8\uFF09\n */\nexport function abs(p: string): string {\n if (!p.startsWith('/')) return p;\n return getBasePath() + p;\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { getBasePath } from '../base-path/base-path';\n\n/**\n * Carousel Controller adds event listeners, accessibility enhancements, and\n * control elements to a carousel component.\n */\nexport class CarouselController {\n /**\n * slides is a collection of slides in the carousel.\n */\n private slides: HTMLLIElement[];\n /**\n * dots is a collection of dot navigation controls, added to the carousel\n * by this controller.\n */\n private dots: HTMLElement[];\n /**\n * liveRegion is a visually hidden element that notifies assitive devices\n * of visual changes to the carousel. They are added to the carousel by\n * this controller.\n */\n private liveRegion: HTMLElement;\n /**\n * activeIndex is the 0-index of the currently active slide.\n */\n private activeIndex: number;\n\n constructor(private el: HTMLElement) {\n this.slides = Array.from(el.querySelectorAll('.go-Carousel-slide'));\n this.dots = [];\n this.liveRegion = document.createElement('div');\n this.activeIndex = Number(el.getAttribute('data-slide-index') ?? 0);\n\n this.initSlides();\n this.initArrows();\n this.initDots();\n this.initLiveRegion();\n }\n\n private initSlides() {\n for (const [i, v] of this.slides.entries()) {\n if (i === this.activeIndex) continue;\n v.setAttribute('aria-hidden', 'true');\n }\n }\n\n private initArrows() {\n const arrows = document.createElement('ul');\n arrows.classList.add('go-Carousel-arrows');\n // base path \u901A\u8FC7 [getBasePath]() \u6CE8\u5165\uFF1B\u6302\u6839\u65F6\u8FD4\u7A7A\u5B57\u7B26\u4E32\uFF0C\u5BF9\u4E0A\u6E38\u96F6\u5DEE\u5F02\u3002\n const bp = getBasePath();\n arrows.innerHTML = `\n
  • \n \n
  • \n
  • \n \n
  • \n `;\n arrows\n .querySelector('.go-Carousel-prevSlide')\n ?.addEventListener('click', () => this.setActive(this.activeIndex - 1));\n arrows\n .querySelector('.go-Carousel-nextSlide')\n ?.addEventListener('click', () => this.setActive(this.activeIndex + 1));\n this.el.append(arrows);\n }\n\n private initDots() {\n const dots = document.createElement('ul');\n dots.classList.add('go-Carousel-dots');\n for (let i = 0; i < this.slides.length; i++) {\n const li = document.createElement('li');\n const button = document.createElement('button');\n button.classList.add('go-Carousel-dot');\n if (i === this.activeIndex) {\n button.classList.add('go-Carousel-dot--active');\n }\n button.innerHTML = `Slide ${i + 1}`;\n button.addEventListener('click', () => this.setActive(i));\n li.append(button);\n dots.append(li);\n this.dots.push(button);\n }\n this.el.append(dots);\n }\n\n private initLiveRegion() {\n this.liveRegion.setAttribute('aria-live', 'polite');\n this.liveRegion.setAttribute('aria-atomic', 'true');\n this.liveRegion.setAttribute('class', 'go-Carousel-obscured');\n this.liveRegion.textContent = `Slide ${this.activeIndex + 1} of ${this.slides.length}`;\n this.el.appendChild(this.liveRegion);\n }\n\n private setActive = (index: number) => {\n this.activeIndex = (index + this.slides.length) % this.slides.length;\n this.el.setAttribute('data-slide-index', String(this.activeIndex));\n for (const d of this.dots) {\n d.classList.remove('go-Carousel-dot--active');\n }\n this.dots[this.activeIndex].classList.add('go-Carousel-dot--active');\n for (const s of this.slides) {\n s.setAttribute('aria-hidden', 'true');\n }\n this.slides[this.activeIndex].removeAttribute('aria-hidden');\n this.liveRegion.textContent = 'Slide ' + (this.activeIndex + 1) + ' of ' + this.slides.length;\n };\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * This class decorates an element to copy arbitrary data attached via a data-\n * attribute to the clipboard.\n */\nexport class ClipboardController {\n /**\n * The data to be copied to the clipboard.\n */\n private data: string;\n\n /**\n * @param el The element that will trigger copying text to the clipboard. The text is\n * expected to be within its data-to-copy attribute.\n */\n constructor(private el: HTMLButtonElement) {\n this.data = el.dataset['toCopy'] ?? el.innerText;\n // if data-to-copy is empty and the button is part of an input group\n // capture the value of the input.\n if (!this.data && el.parentElement?.classList.contains('go-InputGroup')) {\n this.data = (this.data || el.parentElement?.querySelector('input')?.value) ?? '';\n }\n el.addEventListener('click', e => this.handleCopyClick(e));\n }\n\n /**\n * Handles when the primary element is clicked.\n */\n handleCopyClick(e: MouseEvent): void {\n e.preventDefault();\n const TOOLTIP_SHOW_DURATION_MS = 1000;\n\n // This API is not available on iOS.\n if (!navigator.clipboard) {\n this.showTooltipText('Unable to copy', TOOLTIP_SHOW_DURATION_MS);\n return;\n }\n navigator.clipboard\n .writeText(this.data)\n .then(() => {\n this.showTooltipText('Copied!', TOOLTIP_SHOW_DURATION_MS);\n })\n .catch(() => {\n this.showTooltipText('Unable to copy', TOOLTIP_SHOW_DURATION_MS);\n });\n }\n\n /**\n * Shows the given text in a tooltip for a specified amount of time, in milliseconds.\n */\n showTooltipText(text: string, durationMs: number): void {\n this.el.setAttribute('data-tooltip', text);\n setTimeout(() => this.el.setAttribute('data-tooltip', ''), durationMs);\n }\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * ToolTipController handles closing tooltips on external clicks.\n */\nexport class ToolTipController {\n constructor(private el: HTMLDetailsElement) {\n document.addEventListener('click', e => {\n const insideTooltip = this.el.contains(e.target as Element);\n if (!insideTooltip) {\n this.el.removeAttribute('open');\n }\n });\n\n // Add event listener for \"Escape\" keydown to close tooltip\n this.el.addEventListener('keydown', e => {\n if (e.key === 'Escape') {\n this.el.open = false;\n }\n });\n }\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { TreeNavController } from './tree.js';\n\nexport class SelectNavController {\n constructor(private el: Element) {\n this.el.addEventListener('change', e => {\n const target = e.target as HTMLSelectElement;\n let href = target.value;\n if (!target.value.startsWith('/')) {\n href = '/' + href;\n }\n window.location.href = href;\n });\n }\n}\n\nexport function makeSelectNav(tree: TreeNavController): HTMLLabelElement {\n const label = document.createElement('label');\n label.classList.add('go-Label');\n label.setAttribute('aria-label', 'Menu');\n const select = document.createElement('select');\n select.classList.add('go-Select', 'js-selectNav');\n label.appendChild(select);\n const outline = document.createElement('optgroup');\n outline.label = 'Outline';\n select.appendChild(outline);\n const groupMap: Record = {};\n let group: HTMLOptGroupElement;\n for (const t of tree.treeitems) {\n if (Number(t.depth) > 4) continue;\n if (t.groupTreeitem) {\n group = groupMap[t.groupTreeitem.label];\n if (!group) {\n group = groupMap[t.groupTreeitem.label] = document.createElement('optgroup');\n group.label = t.groupTreeitem.label;\n select.appendChild(group);\n }\n } else {\n group = outline;\n }\n const o = document.createElement('option');\n o.label = t.label;\n o.textContent = t.label;\n o.value = (t.el as HTMLAnchorElement).href.replace(window.location.origin, '').replace('/', '');\n group.appendChild(o);\n }\n tree.addObserver(t => {\n const hash = (t.el as HTMLAnchorElement).hash;\n const value = select.querySelector(`[value$=\"${hash}\"]`)?.value;\n if (value) {\n select.value = value;\n }\n }, 50);\n return label;\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\ninterface Window {\n dialogPolyfill?: {\n registerDialog: (el: HTMLDialogElement) => void;\n };\n}\n\ndeclare const window: Window;\n\n/**\n * ModalController registers a dialog element with the polyfill if\n * necessary for the current browser, add adds event listeners to\n * close and open modals.\n */\nexport class ModalController {\n constructor(private el: HTMLDialogElement) {\n if (window.dialogPolyfill) {\n window.dialogPolyfill.registerDialog(el);\n }\n this.init();\n }\n\n init() {\n const button = document.querySelector(`[aria-controls=\"${this.el.id}\"]`);\n if (button) {\n button.addEventListener('click', () => {\n if (this.el.showModal) {\n this.el.showModal();\n } else {\n this.el.setAttribute('opened', 'true');\n }\n this.el.querySelector('input')?.focus();\n });\n }\n for (const btn of this.el.querySelectorAll('[data-modal-close]')) {\n btn.addEventListener('click', () => {\n if (this.el.close) {\n this.el.close();\n } else {\n this.el.removeAttribute('opened');\n }\n });\n }\n }\n}\n", "interface TagManagerEvent {\n /**\n * event is the name of the event, used to filter events in\n * Google Analytics.\n */\n event: string;\n\n /**\n * event_category is a name that you supply as a way to group objects\n * that to analyze. Typically, you will use the same category name\n * multiple times over related UI elements (buttons, links, etc).\n */\n event_category?: string;\n\n /**\n * event_action is used to name the type of event or interaction you\n * want to measure for a particular web object. For example, with a\n * single \"form\" category, you can analyze a number of specific events\n * with this parameter, such as: form entered, form submitted.\n */\n event_action?: string;\n\n /**\n * event_label provide additional information for events that you want\n * to analyze, such as the text label of a link.\n */\n event_label?: string;\n\n /**\n * gtm.start is used to initialize Google Tag Manager.\n */\n 'gtm.start'?: number;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\ndeclare global {\n interface Window {\n dataLayer?: (TagManagerEvent | VoidFunction)[];\n ga?: unknown;\n }\n}\n\n/**\n * track sends events to Google Tag Manager.\n */\nexport function track(\n event: string | TagManagerEvent,\n category?: string,\n action?: string,\n label?: string\n): void {\n window.dataLayer ??= [];\n if (typeof event === 'string') {\n window.dataLayer.push({\n event,\n event_category: category,\n event_action: action,\n event_label: label,\n });\n } else {\n window.dataLayer.push(event);\n }\n}\n\n/**\n * func adds functions to run sequentionally after\n * Google Tag Manager is ready.\n */\nexport function func(fn: () => void): void {\n window.dataLayer ??= [];\n window.dataLayer.push(fn);\n}\n", "/*!\n * @license\n * Copyright 2019-2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { track } from '../analytics/analytics';\n\n/**\n * Options are keyhandler callback options.\n */\ninterface Options {\n /**\n * target is the element the key event should filter on. The\n * default target is the document.\n */\n target?: Element;\n\n /**\n * withMeta specifies if the event callback should fire when\n * the key is pressed with a meta key (ctrl, alt, etc). By\n * default meta keypresses are ignored.\n */\n withMeta?: boolean;\n}\n\n/**\n * KeyHandler is the config for a keyboard event callback.\n */\ninterface KeyHandler extends Options {\n description: string;\n callback: (e: KeyboardEvent) => void;\n}\n\n/**\n * KeyboardController controls event callbacks for sitewide\n * keyboard events. Multiple callbacks can be registered for\n * a single key and by default the controller ignores events\n * for text input targets.\n */\nclass KeyboardController {\n handlers: Record>;\n\n constructor() {\n this.handlers = {};\n document.addEventListener('keydown', e => this.handleKeyPress(e));\n }\n\n /**\n * on registers keyboard event callbacks.\n * @param key the key to register.\n * @param description name of the event.\n * @param callback event callback.\n * @param options set target and withMeta options to override the default behaviors.\n */\n on(key: string, description: string, callback: (e: KeyboardEvent) => void, options?: Options) {\n this.handlers[key] ??= new Set();\n this.handlers[key].add({ description, callback, ...options });\n return this;\n }\n\n private handleKeyPress(e: KeyboardEvent) {\n for (const handler of this.handlers[e.key.toLowerCase()] ?? new Set()) {\n if (handler.target && handler.target !== e.target) {\n return;\n }\n const t = e.target as HTMLElement | null;\n if (\n !handler.target &&\n (t?.tagName === 'INPUT' || t?.tagName === 'SELECT' || t?.tagName === 'TEXTAREA')\n ) {\n return;\n }\n if (t?.isContentEditable) {\n return;\n }\n if (\n (handler.withMeta && !(e.ctrlKey || e.metaKey)) ||\n (!handler.withMeta && (e.ctrlKey || e.metaKey))\n ) {\n return;\n }\n track('keypress', 'hotkeys', `${e.key} pressed`, handler.description);\n handler.callback(e);\n }\n }\n}\n\nexport const keyboard = new KeyboardController();\n", "/*!\n * @license\n * Copyright 2019-2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n// This file implements the behavior of the \"jump to symbol\" dialog for Go\n// package documentation, as well as the simple dialog that displays keyboard\n// shortcuts.\n\n// The DOM for the dialogs is at the bottom of static/frontend/unit/main/_modals.tmpl.\n// The CSS is in static/frontend/unit/main/_modals.css.\n\n// The dialog is activated by pressing the 'f' key. It presents a list\n// (#JumpDialog-list) of all Go symbols displayed in the documentation.\n// Entering text in the dialog's text box (#JumpDialog-filter) restricts the\n// list to symbols containing the text. Clicking on an symbol jumps to\n// its documentation.\n\n// This code is based on\n// https://go.googlesource.com/gddo/+/refs/heads/master/gddo-server/assets/site.js.\n// It was modified to remove the dependence on jquery and bootstrap.\n\nimport { keyboard } from '../keyboard/keyboard';\n\nexport function initModals(): void {\n const jumpDialog = document.querySelector('.JumpDialog');\n const jumpBody = jumpDialog?.querySelector('.JumpDialog-body');\n const jumpList = jumpDialog?.querySelector('.JumpDialog-list');\n const jumpFilter = jumpDialog?.querySelector('.JumpDialog-input');\n const doc = document.querySelector('.js-documentation');\n\n interface JumpListItem {\n link: HTMLAnchorElement;\n name: string;\n kind: string;\n lower: string;\n }\n\n let jumpListItems: JumpListItem[] | undefined; // All the symbols in the doc; computed only once.\n\n // collectJumpListItems returns a list of items, one for each symbol in the\n // documentation on the current page.\n //\n // It uses the data-kind attribute generated in the documentation HTML to find\n // the symbols and their id attributes.\n //\n // If there are no data-kind attributes, then we have older doc; fall back to\n // a less precise method.\n function collectJumpListItems() {\n const items = [];\n if (!doc) return;\n for (const el of doc.querySelectorAll('[data-kind]')) {\n items.push(newJumpListItem(el));\n }\n\n // Clicking on any of the links closes the dialog.\n for (const item of items) {\n item.link.addEventListener('click', function () {\n jumpDialog?.close();\n });\n }\n // Sort case-insensitively by symbol name.\n items.sort(function (a, b) {\n return a.lower.localeCompare(b.lower);\n });\n return items;\n }\n\n // newJumpListItem creates a new item for the DOM element el.\n // An item is an object with:\n // - name: the element's id (which is the symbol name)\n // - kind: the element's kind (function, variable, etc.),\n // - link: a link ('a' tag) to the element\n // - lower: the name in lower case, just for sorting\n function newJumpListItem(el: Element): JumpListItem {\n const a = document.createElement('a');\n const name = el.getAttribute('id');\n a.setAttribute('href', '#' + name);\n a.setAttribute('tabindex', '-1');\n a.setAttribute('data-gtmc', 'jump to link');\n const kind = el.getAttribute('data-kind');\n return {\n link: a,\n name: name ?? '',\n kind: kind ?? '',\n lower: name?.toLowerCase() ?? '', // for sorting\n };\n }\n\n let lastFilterValue: string; // The last contents of the filter text box.\n let activeJumpItem = -1; // The index of the currently active item in the list.\n\n // updateJumpList sets the elements of the dialog list to\n // everything whose name contains filter.\n function updateJumpList(filter: string) {\n lastFilterValue = filter;\n if (!jumpListItems) {\n jumpListItems = collectJumpListItems();\n }\n setActiveJumpItem(-1);\n\n // Remove all children from list.\n while (jumpList?.firstChild) {\n jumpList.firstChild.remove();\n }\n\n if (filter) {\n // A filter is set. We treat the filter as a substring that can appear in\n // an item name (case insensitive), and find the following matches - in\n // order of priority:\n //\n // 1. Exact matches (the filter matches the item's name exactly)\n // 2. Prefix matches (the item's name starts with filter)\n // 3. Infix matches (the filter is a substring of the item's name)\n const filterLowerCase = filter.toLowerCase();\n\n const exactMatches = [];\n const prefixMatches = [];\n const infixMatches = [];\n\n // makeLinkHtml creates the link name HTML for a list item. item is the DOM\n // item. item.name.substr(boldStart, boldEnd) will be bolded.\n const makeLinkHtml = (item: JumpListItem, boldStart: number, boldEnd: number) => {\n return (\n item.name.substring(0, boldStart) +\n '' +\n item.name.substring(boldStart, boldEnd) +\n '' +\n item.name.substring(boldEnd)\n );\n };\n\n for (const item of jumpListItems ?? []) {\n const nameLowerCase = item.name.toLowerCase();\n\n if (nameLowerCase === filterLowerCase) {\n item.link.innerHTML = makeLinkHtml(item, 0, item.name.length);\n exactMatches.push(item);\n } else if (nameLowerCase.startsWith(filterLowerCase)) {\n item.link.innerHTML = makeLinkHtml(item, 0, filter.length);\n prefixMatches.push(item);\n } else {\n const index = nameLowerCase.indexOf(filterLowerCase);\n if (index > -1) {\n item.link.innerHTML = makeLinkHtml(item, index, index + filter.length);\n infixMatches.push(item);\n }\n }\n }\n\n for (const item of exactMatches.concat(prefixMatches).concat(infixMatches)) {\n jumpList?.appendChild(item.link);\n }\n } else {\n if (!jumpListItems || jumpListItems.length === 0) {\n const msg = document.createElement('i');\n msg.innerHTML = 'There are no symbols on this page.';\n jumpList?.appendChild(msg);\n }\n // No filter set; display all items in their existing order.\n for (const item of jumpListItems ?? []) {\n item.link.innerHTML = item.name + ' ' + item.kind + '';\n jumpList?.appendChild(item.link);\n }\n }\n\n if (jumpBody) {\n jumpBody.scrollTop = 0;\n }\n if (jumpListItems?.length && jumpList && jumpList.children.length > 0) {\n setActiveJumpItem(0);\n }\n }\n\n // Set the active jump item to n.\n function setActiveJumpItem(n: number) {\n const cs = jumpList?.children as HTMLCollectionOf | null | undefined;\n if (!cs || !jumpBody) {\n return;\n }\n if (activeJumpItem >= 0) {\n cs[activeJumpItem].classList.remove('JumpDialog-active');\n }\n if (n >= cs.length) {\n n = cs.length - 1;\n }\n if (n >= 0) {\n cs[n].classList.add('JumpDialog-active');\n\n // Scroll so the active item is visible.\n // For some reason cs[n].scrollIntoView() doesn't behave as I'd expect:\n // it moves the entire dialog box in the viewport.\n\n // Get the top and bottom of the active item relative to jumpBody.\n const activeTop = cs[n].offsetTop - cs[0].offsetTop;\n const activeBottom = activeTop + cs[n].clientHeight;\n if (activeTop < jumpBody.scrollTop) {\n // Off the top; scroll up.\n jumpBody.scrollTop = activeTop;\n } else if (activeBottom > jumpBody.scrollTop + jumpBody.clientHeight) {\n // Off the bottom; scroll down.\n jumpBody.scrollTop = activeBottom - jumpBody.clientHeight;\n }\n }\n activeJumpItem = n;\n }\n\n // Increment the activeJumpItem by delta.\n function incActiveJumpItem(delta: number) {\n if (activeJumpItem < 0) {\n return;\n }\n let n = activeJumpItem + delta;\n if (n < 0) {\n n = 0;\n }\n setActiveJumpItem(n);\n }\n\n // Pressing a key in the filter updates the list (if the filter actually changed).\n jumpFilter?.addEventListener('keyup', function () {\n if (jumpFilter.value.toUpperCase() != lastFilterValue.toUpperCase()) {\n updateJumpList(jumpFilter.value);\n }\n });\n\n // Pressing enter in the filter selects the first element in the list.\n jumpFilter?.addEventListener('keydown', function (event) {\n const upArrow = 38;\n const downArrow = 40;\n const enterKey = 13;\n switch (event.which) {\n case upArrow:\n incActiveJumpItem(-1);\n event.preventDefault();\n break;\n case downArrow:\n incActiveJumpItem(1);\n event.preventDefault();\n break;\n case enterKey:\n if (activeJumpItem >= 0) {\n if (jumpList) {\n (jumpList.children[activeJumpItem] as HTMLElement).click();\n event.preventDefault();\n }\n }\n break;\n }\n });\n\n const shortcutsDialog = document.querySelector('.ShortcutsDialog');\n\n // - Pressing 'f' or 'F' opens the jump-to-symbol dialog.\n // - Pressing '?' opens up the shortcut dialog.\n // Ignore a keypress if a dialog is already open, or if it is pressed on a\n // component that wants to consume it.\n keyboard\n .on('f', 'open jump to modal', e => {\n if (jumpDialog?.open || shortcutsDialog?.open) {\n return;\n }\n e.preventDefault();\n if (jumpFilter) {\n jumpFilter.value = '';\n }\n jumpDialog?.showModal?.();\n jumpFilter?.focus();\n updateJumpList('');\n })\n .on('?', 'open shortcuts modal', () => {\n if (jumpDialog?.open || shortcutsDialog?.open) {\n return;\n }\n shortcutsDialog?.showModal?.();\n });\n\n const jumpOutlineInput = document.querySelector('.js-jumpToInput');\n if (jumpOutlineInput) {\n jumpOutlineInput.addEventListener('click', () => {\n if (jumpFilter) {\n jumpFilter.value = '';\n }\n updateJumpList('');\n if (jumpDialog?.open || shortcutsDialog?.open) {\n return;\n }\n jumpDialog?.showModal?.();\n jumpFilter?.focus();\n });\n }\n\n document.querySelector('.js-openShortcuts')?.addEventListener('click', () => {\n shortcutsDialog?.showModal?.();\n });\n}\n", "/**\n * @license\n * Copyright 2022 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { abs } from '../../shared/base-path/base-path';\n\n/**\n * Left Navigation.\n */\nexport const initJumpLinks = async function () {\n // pathname \u5728\u6302 -base-path=/gogodocs \u65F6\u662F \"/gogodocs/about\"\u2014\u2014\u6BD4\u8F83\u5217\u8868\u4E5F\u5F97\u5E26\u524D\u7F00\n const pagesWithJumpLinks = [abs('/about')];\n if (!pagesWithJumpLinks.includes(window.location.pathname)) {\n // stop the file from doing anything else if the page doesn't have jumplinks\n return;\n }\n\n // these might be generated or not so don't grab references to the elements until actually need them.\n const titles = 'h2, h3, h4';\n const nav = '.LeftNav a';\n // these are always in the dom so we can get them now and throw errors if they're not.\n const leftNav = document.querySelector('.LeftNav');\n const siteContent = document.querySelector('.go-Content');\n let isObserverDisabled = false;\n\n /**\n * El function\n * @example el('h1', {className: 'title'}, 'Welcome to the site');\n * @example el('ul', {className: 'list'}, el('li', {}, 'Item one'), el('li', {}, 'Item two'), el('li', {}, 'Item three'));\n * @example el('img', {src: '/url.svg'});\n */\n function el(\n type = '',\n props: { [key: string]: string } = {},\n ...children: (HTMLElement | HTMLElement[] | string | undefined)[]\n ) {\n // Error, no type declared.\n if (!type) {\n throw new Error('Provide `type` to create document element.');\n }\n\n // Create element with optional attribute props\n const docEl = Object.assign(document.createElement(type), props);\n\n // Children: array containing strings or elements\n children.forEach(child => {\n if (typeof child === 'string') {\n docEl.appendChild(document.createTextNode(child));\n } else if (Array.isArray(child)) {\n child.forEach(c => docEl.appendChild(c));\n } else if (child instanceof HTMLElement) {\n docEl.appendChild(child);\n }\n });\n\n return docEl;\n }\n /** Build Nav if data hydrate is present. */\n function buildNav() {\n return new Promise((resolve, reject) => {\n let navItems: { id: string; label: string; subnav?: { id: string; label: string }[] }[] = [];\n let elements: HTMLElement[] = [];\n\n if (!siteContent || !leftNav) {\n return reject('.SiteContent not found.');\n }\n if (leftNav instanceof HTMLElement && !leftNav?.dataset?.hydrate) {\n return resolve(true);\n }\n\n for (const title of siteContent.querySelectorAll(titles)) {\n if (title instanceof HTMLElement && !title?.dataset?.ignore) {\n switch (title.tagName) {\n case 'H2':\n navItems = [\n ...navItems,\n {\n id: title.id,\n label: title?.dataset?.title ? title.dataset.title : title.textContent ?? '',\n },\n ];\n break;\n\n case 'H3':\n case 'H4':\n if (!navItems[navItems.length - 1]?.subnav) {\n navItems[navItems.length - 1].subnav = [\n {\n id: title.id,\n label: title?.dataset?.title ? title.dataset.title : title.textContent ?? '',\n },\n ];\n } else if (navItems[navItems.length - 1].subnav) {\n navItems[navItems.length - 1].subnav?.push({\n id: title.id,\n label: title?.dataset?.title ? title.dataset.title : title.textContent ?? '',\n });\n }\n break;\n }\n }\n }\n\n for (const navItem of navItems) {\n const link = el('a', { href: '#' + navItem.id }, el('span', {}, navItem.label));\n elements = [...elements, link];\n if (navItem?.subnav) {\n let subLinks: HTMLElement[] = [];\n for (const subnavItem of navItem.subnav) {\n const subItem = el(\n 'li',\n {},\n el(\n 'a',\n { href: '#' + subnavItem.id },\n el('img', { src: abs('/static/frontend/about/dot.svg'), width: '5', height: '5' }),\n el('span', {}, subnavItem.label)\n )\n );\n subLinks = [...subLinks, subItem];\n }\n const list = el('ul', { className: 'LeftSubnav' }, subLinks);\n elements = [...elements, list];\n }\n }\n\n elements.forEach(element => leftNav.appendChild(element));\n\n return resolve(true);\n });\n }\n /**\n * Set the correct active element.\n */\n function setNav() {\n return new Promise(resolve => {\n if (!document.querySelectorAll(nav)) return resolve(true);\n for (const a of document.querySelectorAll(nav)) {\n if (a instanceof HTMLAnchorElement && a.href === location.href) {\n setElementActive(a);\n break;\n }\n }\n resolve(true);\n });\n }\n /** resetNav: removes all .active from nav elements */\n function resetNav() {\n return new Promise(resolve => {\n if (!document.querySelectorAll(nav)) return resolve(true);\n for (const a of document.querySelectorAll(nav)) {\n a.classList.remove('active');\n }\n resolve(true);\n });\n }\n /** setElementActive: controls resetting nav and highlighting the appropriate nav items */\n function setElementActive(element: HTMLAnchorElement) {\n if (element instanceof HTMLAnchorElement) {\n resetNav().then(() => {\n element.classList.add('active');\n const parent = element?.parentNode?.parentNode;\n if (parent instanceof HTMLElement && parent?.classList?.contains('LeftSubnav')) {\n parent.previousElementSibling?.classList.add('active');\n }\n });\n }\n }\n /** setLinkManually: disables observer and selects the clicked nav item. */\n function setLinkManually() {\n delayObserver();\n const link = document.querySelector('[href=\"' + location.hash + '\"]');\n if (link instanceof HTMLAnchorElement) {\n setElementActive(link);\n }\n }\n /** delayObserver: Quick on off switch for intersection observer. */\n function delayObserver() {\n isObserverDisabled = true;\n setTimeout(() => {\n isObserverDisabled = false;\n }, 200);\n }\n /** observeSections: kicks off observation of titles as well as manual clicks with hashchange */\n function observeSections() {\n window.addEventListener('hashchange', setLinkManually);\n\n if (siteContent?.querySelectorAll(titles)) {\n const callback: IntersectionObserverCallback = entries => {\n if (!isObserverDisabled && Array.isArray(entries) && entries.length > 0) {\n for (const entry of entries) {\n if (entry.isIntersecting && entry.target instanceof HTMLElement) {\n const { id } = entry.target;\n const link = document.querySelector('[href=\"#' + id + '\"]');\n if (link instanceof HTMLAnchorElement) {\n setElementActive(link);\n }\n break;\n }\n }\n }\n };\n // rootMargin is important when multiple sections are in the observable area **on page load**.\n // they will still be highlighted on scroll because of the root margin.\n const ob = new IntersectionObserver(callback, {\n threshold: 0,\n rootMargin: '0px 0px -50% 0px',\n });\n for (const title of siteContent.querySelectorAll(titles)) {\n if (title instanceof HTMLElement && !title?.dataset?.ignore) {\n ob.observe(title);\n }\n }\n }\n }\n\n try {\n await buildNav();\n await setNav();\n if (location.hash) {\n delayObserver();\n }\n observeSections();\n } catch (e) {\n if (e instanceof Error) {\n console.error(e.message);\n } else {\n console.error(e);\n }\n }\n};\n", "/**\n * @license\n * Copyright 2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { registerHeaderListeners, registerSearchFormListeners } from 'static/shared/header/header';\nimport { CarouselController } from 'static/shared/carousel/carousel';\nimport { ClipboardController } from 'static/shared/clipboard/clipboard';\nimport { ToolTipController } from 'static/shared/tooltip/tooltip';\nimport { SelectNavController } from 'static/shared/outline/select';\nimport { ModalController } from 'static/shared/modal/modal';\nimport { initModals } from 'static/shared/jump/jump';\n\nimport { keyboard } from 'static/shared/keyboard/keyboard';\nimport * as analytics from 'static/shared/analytics/analytics';\nimport { initJumpLinks } from './about/index';\n\nwindow.addEventListener('load', () => {\n for (const el of document.querySelectorAll('.js-clipboard')) {\n new ClipboardController(el);\n }\n\n for (const el of document.querySelectorAll('.js-modal')) {\n new ModalController(el);\n }\n\n for (const t of document.querySelectorAll('.js-tooltip')) {\n new ToolTipController(t);\n }\n\n for (const el of document.querySelectorAll('.js-selectNav')) {\n new SelectNavController(el);\n }\n\n for (const el of document.querySelectorAll('.js-carousel')) {\n new CarouselController(el);\n }\n\n for (const el of document.querySelectorAll('.js-toggleTheme')) {\n el.addEventListener('click', () => {\n toggleTheme();\n });\n }\n\n if (document.querySelector('.js-gtmID')?.dataset.gtmid && window.dataLayer) {\n analytics.func(function () {\n removeUTMSource();\n });\n } else {\n removeUTMSource();\n }\n\n registerHeaderListeners();\n registerSearchFormListeners();\n initModals();\n initJumpLinks();\n registerCookieNotice();\n});\n\n// Pressing '/' focuses the search box\nkeyboard.on('/', 'focus search', e => {\n const searchInput = Array.from(\n document.querySelectorAll('.js-searchFocus')\n ).pop();\n // Favoring the Firefox quick find feature over search input\n // focus. See: https://github.com/golang/go/issues/41093.\n if (searchInput && !window.navigator.userAgent.includes('Firefox')) {\n e.preventDefault();\n searchInput.focus();\n }\n});\n\n// Pressing 'y' changes the browser URL to the canonical URL\n// without triggering a reload.\nkeyboard.on('y', 'set canonical url', () => {\n let canonicalURLPath = document.querySelector('.js-canonicalURLPath')?.dataset[\n 'canonicalUrlPath'\n ];\n if (canonicalURLPath && canonicalURLPath !== '') {\n const fragment = window.location.hash;\n if (fragment) {\n canonicalURLPath += fragment;\n }\n window.history.replaceState(null, '', canonicalURLPath);\n }\n});\n\n/**\n * setupGoogleTagManager initializes Google Tag Manager.\n */\n(function setupGoogleTagManager() {\n analytics.track({\n 'gtm.start': new Date().getTime(),\n event: 'gtm.js',\n });\n})();\n\n/**\n * removeUTMSource removes the utm_source GET parameter if present.\n * This is done using JavaScript, so that the utm_source is still\n * captured by Google Analytics.\n */\nfunction removeUTMSource() {\n const urlParams = new URLSearchParams(window.location.search);\n const utmSource = urlParams.get('utm_source');\n if (utmSource !== 'gopls' && utmSource !== 'godoc' && utmSource !== 'pkggodev') {\n return;\n }\n\n /** Strip the utm_source query parameter and replace the URL. **/\n const newURL = new URL(window.location.href);\n urlParams.delete('utm_source');\n newURL.search = urlParams.toString();\n window.history.replaceState(null, '', newURL.toString());\n}\n\n/**\n * toggleTheme switches the preferred color scheme between auto, light, and dark.\n */\nfunction toggleTheme() {\n let nextTheme = 'dark';\n const theme = document.documentElement.getAttribute('data-theme');\n if (theme === 'dark') {\n nextTheme = 'light';\n } else if (theme === 'light') {\n nextTheme = 'auto';\n }\n let domain = '';\n if (location.hostname === 'go.dev' || location.hostname.endsWith('.go.dev')) {\n domain = 'domain=.go.dev;';\n }\n document.documentElement.setAttribute('data-theme', nextTheme);\n document.cookie = `prefers-color-scheme=${nextTheme};${domain}path=/;max-age=31536000;`;\n}\n\n/**\n * registerCookieNotice makes the cookie notice visible and adds listeners to dismiss it\n * if it has not yet been acknowledge by the user.\n */\nfunction registerCookieNotice() {\n const themeCookie = document.cookie.match(/cookie-consent=true/);\n if (!themeCookie) {\n const notice = document.querySelector('.js-cookieNotice');\n const button = notice?.querySelector('button');\n notice?.classList.add('Cookie-notice--visible');\n button?.addEventListener('click', () => {\n let domain = '';\n if (location.hostname === 'go.dev' || location.hostname.endsWith('.go.dev')) {\n // Apply the cookie to *.go.dev.\n domain = 'domain=.go.dev;';\n }\n document.cookie = `cookie-consent=true;${domain}path=/;max-age=31536000`;\n notice?.remove();\n });\n }\n}\n"], + "mappings": "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", + "names": ["registerHeaderListeners", "header", "menuItemHover", "e", "target", "forced", "toggleForcedOpen", "_a", "_b", "isForced", "currentTarget", "closeSubmenuOnEsc", "event", "forcedOpenItem", "headerbuttons", "button", "isActive", "handleNavigationDrawerInactive", "handleNavigationDrawerActive", "scrim", "subnav", "getNavigationDrawerMenuItems", "navigationDrawer", "menuItems", "anchorEl", "getNavigationDrawerIsSubnav", "parentMenuItem", "item", "handleMenuItemTabLeftFactory", "handleMenuItemTabRightFactory", "prepMobileNavigationDrawer", "isSubnav", "parentLi", "submenu", "drawer", "registerSearchFormListeners", "searchForm", "expandSearch", "input", "headerLogo", "menuButton", "getBasePath", "_a", "abs", "p", "CarouselController", "el", "index", "d", "s", "_a", "i", "v", "_b", "arrows", "bp", "getBasePath", "dots", "li", "button", "ClipboardController", "el", "_a", "_b", "_c", "_d", "_e", "e", "TOOLTIP_SHOW_DURATION_MS", "text", "durationMs", "ToolTipController", "el", "e", "SelectNavController", "el", "e", "target", "href", "ModalController", "el", "button", "_a", "btn", "track", "event", "category", "action", "label", "_a", "func", "fn", "KeyboardController", "key", "description", "callback", "options", "_a", "_b", "handler", "t", "track", "keyboard", "initModals", "_a", "jumpDialog", "jumpBody", "jumpList", "jumpFilter", "doc", "jumpListItems", "collectJumpListItems", "items", "el", "newJumpListItem", "item", "a", "b", "name", "kind", "lastFilterValue", "activeJumpItem", "updateJumpList", "filter", "setActiveJumpItem", "filterLowerCase", "exactMatches", "prefixMatches", "infixMatches", "makeLinkHtml", "boldStart", "boldEnd", "nameLowerCase", "index", "msg", "n", "cs", "activeTop", "activeBottom", "incActiveJumpItem", "delta", "event", "shortcutsDialog", "keyboard", "e", "jumpOutlineInput", "initJumpLinks", "abs", "titles", "nav", "leftNav", "siteContent", "isObserverDisabled", "el", "type", "props", "children", "docEl", "child", "c", "buildNav", "resolve", "reject", "_a", "_b", "_c", "_d", "_e", "_f", "_g", "_h", "_i", "_j", "navItems", "elements", "title", "navItem", "link", "subLinks", "subnavItem", "subItem", "list", "element", "setNav", "a", "setElementActive", "resetNav", "parent", "setLinkManually", "delayObserver", "observeSections", "callback", "entries", "entry", "id", "ob", "e", "_a", "el", "ClipboardController", "ModalController", "t", "ToolTipController", "SelectNavController", "CarouselController", "toggleTheme", "func", "removeUTMSource", "registerHeaderListeners", "registerSearchFormListeners", "initModals", "initJumpLinks", "registerCookieNotice", "keyboard", "e", "searchInput", "canonicalURLPath", "fragment", "track", "urlParams", "utmSource", "newURL", "nextTheme", "theme", "domain", "notice", "button"] } diff --git a/static/frontend/frontend.tmpl b/static/frontend/frontend.tmpl index ab980500e..ec3e2f7d7 100644 --- a/static/frontend/frontend.tmpl +++ b/static/frontend/frontend.tmpl @@ -5,7 +5,7 @@ --> - + {{template "header" .}} {{template "main" .}} diff --git a/static/frontend/homepage/homepage.tmpl b/static/frontend/homepage/homepage.tmpl index 3a203028a..37818e3c2 100644 --- a/static/frontend/homepage/homepage.tmpl +++ b/static/frontend/homepage/homepage.tmpl @@ -5,7 +5,7 @@ --> {{define "pre-content"}} - + {{end}} {{define "main"}} diff --git a/static/frontend/search-help/search-help.tmpl b/static/frontend/search-help/search-help.tmpl index 59c0e9d7d..98b31c565 100644 --- a/static/frontend/search-help/search-help.tmpl +++ b/static/frontend/search-help/search-help.tmpl @@ -5,7 +5,7 @@ --> {{define "pre-content"}} - + {{end}} {{define "main"}} diff --git a/static/frontend/search/search.tmpl b/static/frontend/search/search.tmpl index 0ba390427..7dc1692b6 100644 --- a/static/frontend/search/search.tmpl +++ b/static/frontend/search/search.tmpl @@ -13,7 +13,7 @@ {{end}} {{define "pre-content"}} - + {{end}} {{define "post-content"}} diff --git a/static/frontend/subrepo/subrepo.tmpl b/static/frontend/subrepo/subrepo.tmpl index 29db14867..f01171bd0 100644 --- a/static/frontend/subrepo/subrepo.tmpl +++ b/static/frontend/subrepo/subrepo.tmpl @@ -10,7 +10,7 @@ {{end}} {{define "pre-content"}} - + {{end}} {{define "main"}} diff --git a/static/frontend/unit/importedby/importedby.tmpl b/static/frontend/unit/importedby/importedby.tmpl index 0d8300f3b..1523dbc39 100644 --- a/static/frontend/unit/importedby/importedby.tmpl +++ b/static/frontend/unit/importedby/importedby.tmpl @@ -9,7 +9,7 @@ {{end}} {{define "main-styles"}} - + {{end}} {{define "main-header"}} diff --git a/static/frontend/unit/imports/imports.tmpl b/static/frontend/unit/imports/imports.tmpl index 52d936aa0..4a9140755 100644 --- a/static/frontend/unit/imports/imports.tmpl +++ b/static/frontend/unit/imports/imports.tmpl @@ -9,7 +9,7 @@ {{end}} {{define "main-styles"}} - + {{end}} {{define "main-header"}} diff --git a/static/frontend/unit/licenses/licenses.tmpl b/static/frontend/unit/licenses/licenses.tmpl index 105b9c9d2..ebf2d307e 100644 --- a/static/frontend/unit/licenses/licenses.tmpl +++ b/static/frontend/unit/licenses/licenses.tmpl @@ -9,7 +9,7 @@ {{end}} {{define "main-styles"}} - + {{end}} {{define "main-header"}} diff --git a/static/frontend/unit/main/main.js b/static/frontend/unit/main/main.js index 5d249203d..29f7e656a 100644 --- a/static/frontend/unit/main/main.js +++ b/static/frontend/unit/main/main.js @@ -1,9 +1,15 @@ -var d={PLAY_HREF:".js-exampleHref",PLAY_CONTAINER:".js-exampleContainer",EXAMPLE_INPUT:".Documentation-exampleCode",EXAMPLE_OUTPUT:".Documentation-exampleOutput",EXAMPLE_OUTPUT_CONTAINER:".js-exampleOutputContainer",EXAMPLE_ERROR:".Documentation-exampleError",PLAY_BUTTON:".Documentation-examplePlayButton",SHARE_BUTTON:".Documentation-exampleShareButton",FORMAT_BUTTON:".Documentation-exampleFormatButton",RUN_BUTTON:".Documentation-exampleRunButton"},b=class{constructor(e){this.exampleEl=e;var t,i,s,r;this.exampleEl=e,this.anchorEl=e.querySelector("a"),this.errorEl=e.querySelector(d.EXAMPLE_ERROR),this.playButtonEl=e.querySelector(d.PLAY_BUTTON),this.shareButtonEl=e.querySelector(d.SHARE_BUTTON),this.formatButtonEl=e.querySelector(d.FORMAT_BUTTON),this.runButtonEl=e.querySelector(d.RUN_BUTTON),this.inputEl=this.makeTextArea(e.querySelector(d.EXAMPLE_INPUT)),this.outputEl=e.querySelector(d.EXAMPLE_OUTPUT),this.outputContainerEl=e.querySelector(d.EXAMPLE_OUTPUT_CONTAINER),(t=this.playButtonEl)==null||t.addEventListener("click",()=>this.handleShareButtonClick()),(i=this.shareButtonEl)==null||i.addEventListener("click",()=>this.handleShareButtonClick()),(s=this.formatButtonEl)==null||s.addEventListener("click",()=>this.handleFormatButtonClick()),(r=this.runButtonEl)==null||r.addEventListener("click",()=>this.handleRunButtonClick()),this.inputEl&&(this.resize(),this.inputEl.addEventListener("keyup",()=>this.resize()),this.inputEl.addEventListener("keydown",l=>this.onKeydown(l)))}makeTextArea(e){var i,s;let t=document.createElement("textarea");return t.classList.add("Documentation-exampleCode","code"),t.spellcheck=!1,t.value=(i=e==null?void 0:e.textContent)!=null?i:"",(s=e==null?void 0:e.parentElement)==null||s.replaceChild(t,e),t}getAnchorHash(){var e;return(e=this.anchorEl)==null?void 0:e.hash}expand(){this.exampleEl.open=!0}resize(){var e;if((e=this.inputEl)!=null&&e.value){let t=(this.inputEl.value.match(/\n/g)||[]).length;this.inputEl.style.height=`${(20+t*20+12+2)/16}rem`}}onKeydown(e){e.key==="Tab"&&(document.execCommand("insertText",!1," "),e.preventDefault())}setInputText(e){this.inputEl&&(this.inputEl.value=e)}setOutputText(e){this.outputEl&&(this.outputEl.textContent=e),this.outputContainerEl&&(this.outputContainerEl.hidden=!1)}appendToOutputText(e){this.outputEl&&(this.outputEl.textContent+=e),this.outputContainerEl&&(this.outputContainerEl.hidden=!1)}setOutputHTML(e){this.outputEl&&(this.outputEl.innerHTML=e)}setErrorText(e){this.errorEl&&(this.errorEl.textContent=e),this.setOutputText("An error has occurred\u2026")}getCodeWithModFile(){var i,s,r,l;let e=(s=(i=this.inputEl)==null?void 0:i.value)!=null?s:"",t=(l=(r=document.querySelector(".js-playgroundVars"))==null?void 0:r.dataset)!=null?l:{};return t.modulepath!=="std"&&(e=e.concat(` +function H(){var l;return(l=document.documentElement.dataset.basePath)!=null?l:""}function p(l){return l.startsWith("/")?H()+l:l}var d={PLAY_HREF:".js-exampleHref",PLAY_CONTAINER:".js-exampleContainer",EXAMPLE_INPUT:".Documentation-exampleCode",EXAMPLE_OUTPUT:".Documentation-exampleOutput",EXAMPLE_OUTPUT_CONTAINER:".js-exampleOutputContainer",EXAMPLE_ERROR:".Documentation-exampleError",PLAY_BUTTON:".Documentation-examplePlayButton",SHARE_BUTTON:".Documentation-exampleShareButton",FORMAT_BUTTON:".Documentation-exampleFormatButton",RUN_BUTTON:".Documentation-exampleRunButton"},v=class{constructor(e){this.exampleEl=e;var t,i,s,r;this.exampleEl=e,this.anchorEl=e.querySelector("a"),this.errorEl=e.querySelector(d.EXAMPLE_ERROR),this.playButtonEl=e.querySelector(d.PLAY_BUTTON),this.shareButtonEl=e.querySelector(d.SHARE_BUTTON),this.formatButtonEl=e.querySelector(d.FORMAT_BUTTON),this.runButtonEl=e.querySelector(d.RUN_BUTTON),this.inputEl=this.makeTextArea(e.querySelector(d.EXAMPLE_INPUT)),this.outputEl=e.querySelector(d.EXAMPLE_OUTPUT),this.outputContainerEl=e.querySelector(d.EXAMPLE_OUTPUT_CONTAINER),(t=this.playButtonEl)==null||t.addEventListener("click",()=>this.handleShareButtonClick()),(i=this.shareButtonEl)==null||i.addEventListener("click",()=>this.handleShareButtonClick()),(s=this.formatButtonEl)==null||s.addEventListener("click",()=>this.handleFormatButtonClick()),(r=this.runButtonEl)==null||r.addEventListener("click",()=>this.handleRunButtonClick()),this.inputEl&&(this.resize(),this.inputEl.addEventListener("keyup",()=>this.resize()),this.inputEl.addEventListener("keydown",n=>this.onKeydown(n)))}makeTextArea(e){var i,s;let t=document.createElement("textarea");return t.classList.add("Documentation-exampleCode","code"),t.spellcheck=!1,t.value=(i=e==null?void 0:e.textContent)!=null?i:"",(s=e==null?void 0:e.parentElement)==null||s.replaceChild(t,e),t}getAnchorHash(){var e;return(e=this.anchorEl)==null?void 0:e.hash}expand(){this.exampleEl.open=!0}resize(){var e;if((e=this.inputEl)!=null&&e.value){let t=(this.inputEl.value.match(/\n/g)||[]).length;this.inputEl.style.height=`${(20+t*20+12+2)/16}rem`}}onKeydown(e){e.key==="Tab"&&(document.execCommand("insertText",!1," "),e.preventDefault())}setInputText(e){this.inputEl&&(this.inputEl.value=e)}setOutputText(e){this.outputEl&&(this.outputEl.textContent=e),this.outputContainerEl&&(this.outputContainerEl.hidden=!1)}appendToOutputText(e){this.outputEl&&(this.outputEl.textContent+=e),this.outputContainerEl&&(this.outputContainerEl.hidden=!1)}setOutputHTML(e){this.outputEl&&(this.outputEl.innerHTML=e)}setErrorText(e){this.errorEl&&(this.errorEl.textContent=e),this.setOutputText("An error has occurred\u2026")}getCodeWithModFile(){var i,s,r,n;let e=(s=(i=this.inputEl)==null?void 0:i.value)!=null?s:"",t=(n=(r=document.querySelector(".js-playgroundVars"))==null?void 0:r.dataset)!=null?n:{};return t.modulepath!=="std"&&(e=e.concat(` -- go.mod -- module play.ground require ${t.modulepath} ${t.version} -`)),e}handleShareButtonClick(){let e="https://play.golang.org/p/";this.setOutputText("Waiting for remote server\u2026"),fetch("/play/share",{method:"POST",body:this.getCodeWithModFile()}).then(t=>t.text()).then(t=>{let i=e+t;this.setOutputHTML(`
    ${i}`),window.open(i)}).catch(t=>{this.setErrorText(t)})}handleFormatButtonClick(){var t,i;this.setOutputText("Waiting for remote server\u2026");let e=new FormData;e.append("body",(i=(t=this.inputEl)==null?void 0:t.value)!=null?i:""),fetch("/play/fmt",{method:"POST",body:e}).then(s=>s.json()).then(({Body:s,Error:r})=>{this.setOutputText(r||"Done."),s&&(this.setInputText(s),this.resize())}).catch(s=>{this.setErrorText(s)})}handleRunButtonClick(){this.setOutputText("Waiting for remote server\u2026"),fetch("/play/compile",{method:"POST",body:JSON.stringify({body:this.getCodeWithModFile(),version:2})}).then(e=>e.json()).then(async({Events:e,Errors:t})=>{this.setOutputText(t||"");for(let i of e||[])this.appendToOutputText(i.Message),await new Promise(s=>setTimeout(s,i.Delay/1e6))}).catch(e=>{this.setErrorText(e)})}};function L(){let n=location.hash.match(/^#(example-.*)$/);if(n){let i=document.getElementById(n[1]);i&&(i.open=!0)}let e=[...document.querySelectorAll(d.PLAY_HREF)],t=i=>e.find(s=>s.hash===i.getAnchorHash());for(let i of document.querySelectorAll(d.PLAY_CONTAINER)){let s=new b(i),r=t(s);r?r.addEventListener("click",()=>{s.expand()}):console.warn("example href not found")}}var p=class{constructor(e){this.el=e;this.el.addEventListener("change",t=>{let i=t.target,s=i.value;i.value.startsWith("/")||(s="/"+s),window.location.href=s})}};function I(n){let e=document.createElement("label");e.classList.add("go-Label"),e.setAttribute("aria-label","Menu");let t=document.createElement("select");t.classList.add("go-Select","js-selectNav"),e.appendChild(t);let i=document.createElement("optgroup");i.label="Outline",t.appendChild(i);let s={},r;for(let l of n.treeitems){if(Number(l.depth)>4)continue;l.groupTreeitem?(r=s[l.groupTreeitem.label],r||(r=s[l.groupTreeitem.label]=document.createElement("optgroup"),r.label=l.groupTreeitem.label,t.appendChild(r))):r=i;let a=document.createElement("option");a.label=l.label,a.textContent=l.label,a.value=l.el.href.replace(window.location.origin,"").replace("/",""),r.appendChild(a)}return n.addObserver(l=>{var c;let a=l.el.hash,u=(c=t.querySelector(`[value$="${a}"]`))==null?void 0:c.value;u&&(t.value=u)},50),e}var f=class{constructor(e){this.el=e;this.handleResize=()=>{this.el.style.setProperty("--js-tree-height","100vh"),this.el.style.setProperty("--js-tree-height",this.el.clientHeight+"px")};this.treeitems=[],this.firstChars=[],this.firstTreeitem=null,this.lastTreeitem=null,this.observerCallbacks=[],this.init()}init(){this.handleResize(),window.addEventListener("resize",this.handleResize),this.findTreeItems(),this.updateVisibleTreeitems(),this.observeTargets(),this.firstTreeitem&&(this.firstTreeitem.el.tabIndex=0)}observeTargets(){this.addObserver(i=>{this.expandTreeitem(i),this.setSelected(i)});let e=new Map,t=new IntersectionObserver(i=>{for(let s of i)e.set(s.target.id,s.isIntersecting||s.intersectionRatio===1);for(let[s,r]of e)if(r){let l=this.treeitems.find(a=>{var u;return(u=a.el)==null?void 0:u.href.endsWith(`#${s}`)});if(l)for(let a of this.observerCallbacks)a(l);break}},{threshold:1,rootMargin:"-60px 0px 0px 0px"});for(let i of this.treeitems.map(s=>s.el.getAttribute("href")))if(i){let s=i.replace(window.location.origin,"").replace("/","").replace("#",""),r=document.getElementById(s);r&&!s.startsWith("example-")&&t.observe(r)}}addObserver(e,t=200){this.observerCallbacks.push(M(e,t))}setFocusToNextItem(e){let t=null;for(let i=e.index+1;i-1;i--){let s=this.treeitems[i];if(s.isVisible){t=s;break}}t&&this.setFocusToItem(t)}setFocusToParentItem(e){e.groupTreeitem&&this.setFocusToItem(e.groupTreeitem)}setFocusToFirstItem(){this.firstTreeitem&&this.setFocusToItem(this.firstTreeitem)}setFocusToLastItem(){this.lastTreeitem&&this.setFocusToItem(this.lastTreeitem)}setSelected(e){var t;for(let i of this.el.querySelectorAll('[aria-expanded="true"]'))i!==e.el&&((t=i.nextElementSibling)!=null&&t.contains(e.el)||i.setAttribute("aria-expanded","false"));for(let i of this.el.querySelectorAll("[aria-selected]"))i!==e.el&&i.setAttribute("aria-selected","false");e.el.setAttribute("aria-selected","true"),this.updateVisibleTreeitems(),this.setFocusToItem(e,!1)}expandTreeitem(e){let t=e;for(;t;)t.isExpandable&&t.el.setAttribute("aria-expanded","true"),t=t.groupTreeitem;this.updateVisibleTreeitems()}expandAllSiblingItems(e){for(let t of this.treeitems)t.groupTreeitem===e.groupTreeitem&&t.isExpandable&&this.expandTreeitem(t)}collapseTreeitem(e){let t=null;e.isExpanded()?t=e:t=e.groupTreeitem,t&&(t.el.setAttribute("aria-expanded","false"),this.updateVisibleTreeitems(),this.setFocusToItem(t))}setFocusByFirstCharacter(e,t){let i,s;t=t.toLowerCase(),i=e.index+1,i===this.treeitems.length&&(i=0),s=this.getIndexFirstChars(i,t),s===-1&&(s=this.getIndexFirstChars(0,t)),s>-1&&this.setFocusToItem(this.treeitems[s])}findTreeItems(){let e=(t,i)=>{let s=i,r=t.firstElementChild;for(;r;)(r.tagName==="A"||r.tagName==="SPAN")&&(s=new v(r,this,i),this.treeitems.push(s),this.firstChars.push(s.label.substring(0,1).toLowerCase())),r.firstElementChild&&e(r,s),r=r.nextElementSibling};e(this.el,null),this.treeitems.map((t,i)=>t.index=i)}updateVisibleTreeitems(){this.firstTreeitem=this.treeitems[0];for(let e of this.treeitems){let t=e.groupTreeitem;for(e.isVisible=!0;t&&t.el!==this.el;)t.isExpanded()||(e.isVisible=!1),t=t.groupTreeitem;e.isVisible&&(this.lastTreeitem=e)}}setFocusToItem(e,t=!0){e.el.tabIndex=0,t&&e.el.focus();for(let i of this.treeitems)i!==e&&(i.el.tabIndex=-1)}getIndexFirstChars(e,t){for(let i=e;i{let s=()=>{t=null,n(...i)};t&&clearTimeout(t),t=setTimeout(s,e)}}var E=class{constructor(e,t){this.table=e;this.toggleAll=t;this.expandAllItems=()=>{this.toggles.map(e=>e.setAttribute("aria-expanded","true")),this.update()};this.collapseAllItems=()=>{this.toggles.map(e=>e.setAttribute("aria-expanded","false")),this.update()};this.update=()=>{this.updateVisibleItems(),setTimeout(()=>this.updateGlobalToggle())};this.rows=Array.from(e.querySelectorAll("[data-aria-controls]")),this.toggles=Array.from(this.table.querySelectorAll("[aria-expanded]")),this.setAttributes(),this.attachEventListeners(),this.update()}setAttributes(){for(let e of["data-aria-controls","data-aria-labelledby","data-id"])this.table.querySelectorAll(`[${e}]`).forEach(t=>{var i;t.setAttribute(e.replace("data-",""),(i=t.getAttribute(e))!=null?i:""),t.removeAttribute(e)})}attachEventListeners(){var e;this.rows.forEach(t=>{t.addEventListener("click",i=>{this.handleToggleClick(i)})}),(e=this.toggleAll)==null||e.addEventListener("click",()=>{this.expandAllItems()}),document.addEventListener("keydown",t=>{(t.ctrlKey||t.metaKey)&&t.key==="f"&&this.expandAllItems()})}handleToggleClick(e){let t=e.currentTarget;t!=null&&t.hasAttribute("aria-expanded")||(t=this.table.querySelector(`button[aria-controls="${t==null?void 0:t.getAttribute("aria-controls")}"]`));let i=(t==null?void 0:t.getAttribute("aria-expanded"))==="true";t==null||t.setAttribute("aria-expanded",i?"false":"true"),e.stopPropagation(),this.update()}updateVisibleItems(){this.rows.map(e=>{var s;let t=(e==null?void 0:e.getAttribute("aria-expanded"))==="true",i=(s=e==null?void 0:e.getAttribute("aria-controls"))==null?void 0:s.trimEnd().split(" ");i==null||i.map(r=>{let l=document.getElementById(`${r}`);t?(l==null||l.classList.add("visible"),l==null||l.classList.remove("hidden")):(l==null||l.classList.add("hidden"),l==null||l.classList.remove("visible"))})})}updateGlobalToggle(){if(!this.toggleAll)return;this.rows.some(t=>t.hasAttribute("aria-expanded"))&&(this.toggleAll.style.display="block"),this.toggles.some(t=>t.getAttribute("aria-expanded")==="false")?(this.toggleAll.innerText="Expand all",this.toggleAll.onclick=this.expandAllItems,this.toggleAll.setAttribute("aria-label","Expand all directories"),this.toggleAll.setAttribute("aria-live","polite")):(this.toggleAll.innerText="Collapse all",this.toggleAll.onclick=this.collapseAllItems,this.toggleAll.setAttribute("aria-label","Collapse all directories"),this.toggleAll.setAttribute("aria-live","polite"))}};L();var m=document.querySelector(".js-expandableTable");if(m){let n=new E(m,document.querySelector(".js-expandAllDirectories"));window.location.search.includes("expand-directories")&&n.expandAllItems();let e=document.querySelector(".js-showInternalDirectories");e&&(document.querySelector(".UnitDirectories-internal")&&(e.style.display="block",e.setAttribute("aria-label","Show Internal Directories"),e.setAttribute("aria-describedby","showInternal-description")),e.addEventListener("click",()=>{m.classList.contains("UnitDirectories-showInternal")?(m.classList.remove("UnitDirectories-showInternal"),e.innerText="Show internal",e.setAttribute("aria-label","Show Internal Directories"),e.setAttribute("aria-live","polite"),e.setAttribute("aria-describedby","showInternal-description")):(m.classList.add("UnitDirectories-showInternal"),e.innerText="Hide internal",e.setAttribute("aria-label","Hide Internal Directories"),e.setAttribute("aria-live","polite"),e.setAttribute("aria-describedby","hideInternal-description"))})),document.querySelector('html[data-local="true"]')&&(e==null||e.click())}var C=document.querySelector(".js-tree");if(C){let n=new f(C),e=I(n),t=document.querySelector(".js-mainNavMobile");t&&t.firstElementChild&&(t==null||t.replaceChild(e,t.firstElementChild)),e.firstElementChild&&new p(e.firstElementChild)}var o=document.querySelector(".js-readme"),x=document.querySelector(".js-readmeContent"),S=document.querySelector(".js-readmeOutline"),T=document.querySelectorAll(".js-readmeExpand"),w=document.querySelector(".js-readmeCollapse"),g=document.querySelector(".DocNavMobile-select");o&&x&&S&&T.length&&w&&(o.clientHeight>320&&(o==null||o.classList.remove("UnitReadme--expanded"),o==null||o.classList.add("UnitReadme--toggle")),window.location.hash.includes("readme")&&h(),g==null||g.addEventListener("change",n=>{n.target.value.startsWith("readme-")&&h()}),T.forEach(n=>n.addEventListener("click",e=>{e.preventDefault(),h(),o.scrollIntoView()})),w.addEventListener("click",n=>{n.preventDefault(),o.classList.remove("UnitReadme--expanded"),T[1]&&T[1].scrollIntoView({block:"center"})}),x.addEventListener("keyup",()=>{h()}),x.addEventListener("click",()=>{h()}),S.addEventListener("click",()=>{h()}),document.addEventListener("keydown",n=>{(n.ctrlKey||n.metaKey)&&n.key==="f"&&h()}));function h(){history.replaceState(null,"",`${location.pathname}${location.search}#section-readme`),o==null||o.classList.add("UnitReadme--expanded")}function k(){var t;if(!location.hash)return;let n=document.getElementById(location.hash.slice(1)),e=(t=n==null?void 0:n.parentElement)==null?void 0:t.parentElement;(e==null?void 0:e.nodeName)==="DETAILS"&&(e.open=!0)}k();window.addEventListener("hashchange",()=>k());document.querySelectorAll(".js-buildContextSelect").forEach(n=>{n.addEventListener("change",e=>{window.location.search=`?GOOS=${e.target.value}`})}); +`)),e}handleShareButtonClick(){let e="https://play.golang.org/p/";this.setOutputText("Waiting for remote server\u2026"),fetch(p("/play/share"),{method:"POST",body:this.getCodeWithModFile()}).then(t=>t.text()).then(t=>{let i=e+t;this.setOutputHTML(`${i}`),window.open(i)}).catch(t=>{this.setErrorText(t)})}handleFormatButtonClick(){var t,i;this.setOutputText("Waiting for remote server\u2026");let e=new FormData;e.append("body",(i=(t=this.inputEl)==null?void 0:t.value)!=null?i:""),fetch(p("/play/fmt"),{method:"POST",body:e}).then(s=>s.json()).then(({Body:s,Error:r})=>{this.setOutputText(r||"Done."),s&&(this.setInputText(s),this.resize())}).catch(s=>{this.setErrorText(s)})}handleRunButtonClick(){this.setOutputText("Waiting for remote server\u2026"),fetch(p("/play/compile"),{method:"POST",body:JSON.stringify({body:this.getCodeWithModFile(),version:2})}).then(e=>e.json()).then(async({Events:e,Errors:t})=>{this.setOutputText(t||"");for(let i of e||[])this.appendToOutputText(i.Message),await new Promise(s=>setTimeout(s,i.Delay/1e6))}).catch(e=>{this.setErrorText(e)})}};function I(){let l=location.hash.match(/^#(example-.*)$/);if(l){let i=document.getElementById(l[1]);i&&(i.open=!0)}let e=[...document.querySelectorAll(d.PLAY_HREF)],t=i=>e.find(s=>s.hash===i.getAnchorHash());for(let i of document.querySelectorAll(d.PLAY_CONTAINER)){let s=new v(i),r=t(s);r?r.addEventListener("click",()=>{s.expand()}):console.warn("example href not found")}}var f=class{constructor(e){this.el=e;this.el.addEventListener("change",t=>{let i=t.target,s=i.value;i.value.startsWith("/")||(s="/"+s),window.location.href=s})}};function C(l){let e=document.createElement("label");e.classList.add("go-Label"),e.setAttribute("aria-label","Menu");let t=document.createElement("select");t.classList.add("go-Select","js-selectNav"),e.appendChild(t);let i=document.createElement("optgroup");i.label="Outline",t.appendChild(i);let s={},r;for(let n of l.treeitems){if(Number(n.depth)>4)continue;n.groupTreeitem?(r=s[n.groupTreeitem.label],r||(r=s[n.groupTreeitem.label]=document.createElement("optgroup"),r.label=n.groupTreeitem.label,t.appendChild(r))):r=i;let a=document.createElement("option");a.label=n.label,a.textContent=n.label,a.value=n.el.href.replace(window.location.origin,"").replace("/",""),r.appendChild(a)}return l.addObserver(n=>{var c;let a=n.el.hash,u=(c=t.querySelector(`[value$="${a}"]`))==null?void 0:c.value;u&&(t.value=u)},50),e}var E=class{constructor(e){this.el=e;this.handleResize=()=>{this.el.style.setProperty("--js-tree-height","100vh"),this.el.style.setProperty("--js-tree-height",this.el.clientHeight+"px")};this.treeitems=[],this.firstChars=[],this.firstTreeitem=null,this.lastTreeitem=null,this.observerCallbacks=[],this.init()}init(){this.handleResize(),window.addEventListener("resize",this.handleResize),this.findTreeItems(),this.updateVisibleTreeitems(),this.observeTargets(),this.firstTreeitem&&(this.firstTreeitem.el.tabIndex=0)}observeTargets(){this.addObserver(i=>{this.expandTreeitem(i),this.setSelected(i)});let e=new Map,t=new IntersectionObserver(i=>{for(let s of i)e.set(s.target.id,s.isIntersecting||s.intersectionRatio===1);for(let[s,r]of e)if(r){let n=this.treeitems.find(a=>{var u;return(u=a.el)==null?void 0:u.href.endsWith(`#${s}`)});if(n)for(let a of this.observerCallbacks)a(n);break}},{threshold:1,rootMargin:"-60px 0px 0px 0px"});for(let i of this.treeitems.map(s=>s.el.getAttribute("href")))if(i){let s=i.replace(window.location.origin,"").replace("/","").replace("#",""),r=document.getElementById(s);r&&!s.startsWith("example-")&&t.observe(r)}}addObserver(e,t=200){this.observerCallbacks.push(O(e,t))}setFocusToNextItem(e){let t=null;for(let i=e.index+1;i-1;i--){let s=this.treeitems[i];if(s.isVisible){t=s;break}}t&&this.setFocusToItem(t)}setFocusToParentItem(e){e.groupTreeitem&&this.setFocusToItem(e.groupTreeitem)}setFocusToFirstItem(){this.firstTreeitem&&this.setFocusToItem(this.firstTreeitem)}setFocusToLastItem(){this.lastTreeitem&&this.setFocusToItem(this.lastTreeitem)}setSelected(e){var t;for(let i of this.el.querySelectorAll('[aria-expanded="true"]'))i!==e.el&&((t=i.nextElementSibling)!=null&&t.contains(e.el)||i.setAttribute("aria-expanded","false"));for(let i of this.el.querySelectorAll("[aria-selected]"))i!==e.el&&i.setAttribute("aria-selected","false");e.el.setAttribute("aria-selected","true"),this.updateVisibleTreeitems(),this.setFocusToItem(e,!1)}expandTreeitem(e){let t=e;for(;t;)t.isExpandable&&t.el.setAttribute("aria-expanded","true"),t=t.groupTreeitem;this.updateVisibleTreeitems()}expandAllSiblingItems(e){for(let t of this.treeitems)t.groupTreeitem===e.groupTreeitem&&t.isExpandable&&this.expandTreeitem(t)}collapseTreeitem(e){let t=null;e.isExpanded()?t=e:t=e.groupTreeitem,t&&(t.el.setAttribute("aria-expanded","false"),this.updateVisibleTreeitems(),this.setFocusToItem(t))}setFocusByFirstCharacter(e,t){let i,s;t=t.toLowerCase(),i=e.index+1,i===this.treeitems.length&&(i=0),s=this.getIndexFirstChars(i,t),s===-1&&(s=this.getIndexFirstChars(0,t)),s>-1&&this.setFocusToItem(this.treeitems[s])}findTreeItems(){let e=(t,i)=>{let s=i,r=t.firstElementChild;for(;r;)(r.tagName==="A"||r.tagName==="SPAN")&&(s=new x(r,this,i),this.treeitems.push(s),this.firstChars.push(s.label.substring(0,1).toLowerCase())),r.firstElementChild&&e(r,s),r=r.nextElementSibling};e(this.el,null),this.treeitems.map((t,i)=>t.index=i)}updateVisibleTreeitems(){this.firstTreeitem=this.treeitems[0];for(let e of this.treeitems){let t=e.groupTreeitem;for(e.isVisible=!0;t&&t.el!==this.el;)t.isExpanded()||(e.isVisible=!1),t=t.groupTreeitem;e.isVisible&&(this.lastTreeitem=e)}}setFocusToItem(e,t=!0){e.el.tabIndex=0,t&&e.el.focus();for(let i of this.treeitems)i!==e&&(i.el.tabIndex=-1)}getIndexFirstChars(e,t){for(let i=e;i{let s=()=>{t=null,l(...i)};t&&clearTimeout(t),t=setTimeout(s,e)}}var T=class{constructor(e,t){this.table=e;this.toggleAll=t;this.expandAllItems=()=>{this.toggles.map(e=>e.setAttribute("aria-expanded","true")),this.update()};this.collapseAllItems=()=>{this.toggles.map(e=>e.setAttribute("aria-expanded","false")),this.update()};this.update=()=>{this.updateVisibleItems(),setTimeout(()=>this.updateGlobalToggle())};this.rows=Array.from(e.querySelectorAll("[data-aria-controls]")),this.toggles=Array.from(this.table.querySelectorAll("[aria-expanded]")),this.setAttributes(),this.attachEventListeners(),this.update()}setAttributes(){for(let e of["data-aria-controls","data-aria-labelledby","data-id"])this.table.querySelectorAll(`[${e}]`).forEach(t=>{var i;t.setAttribute(e.replace("data-",""),(i=t.getAttribute(e))!=null?i:""),t.removeAttribute(e)})}attachEventListeners(){var e;this.rows.forEach(t=>{t.addEventListener("click",i=>{this.handleToggleClick(i)})}),(e=this.toggleAll)==null||e.addEventListener("click",()=>{this.expandAllItems()}),document.addEventListener("keydown",t=>{(t.ctrlKey||t.metaKey)&&t.key==="f"&&this.expandAllItems()})}handleToggleClick(e){let t=e.currentTarget;t!=null&&t.hasAttribute("aria-expanded")||(t=this.table.querySelector(`button[aria-controls="${t==null?void 0:t.getAttribute("aria-controls")}"]`));let i=(t==null?void 0:t.getAttribute("aria-expanded"))==="true";t==null||t.setAttribute("aria-expanded",i?"false":"true"),e.stopPropagation(),this.update()}updateVisibleItems(){this.rows.map(e=>{var s;let t=(e==null?void 0:e.getAttribute("aria-expanded"))==="true",i=(s=e==null?void 0:e.getAttribute("aria-controls"))==null?void 0:s.trimEnd().split(" ");i==null||i.map(r=>{let n=document.getElementById(`${r}`);t?(n==null||n.classList.add("visible"),n==null||n.classList.remove("hidden")):(n==null||n.classList.add("hidden"),n==null||n.classList.remove("visible"))})})}updateGlobalToggle(){if(!this.toggleAll)return;this.rows.some(t=>t.hasAttribute("aria-expanded"))&&(this.toggleAll.style.display="block"),this.toggles.some(t=>t.getAttribute("aria-expanded")==="false")?(this.toggleAll.innerText="Expand all",this.toggleAll.onclick=this.expandAllItems,this.toggleAll.setAttribute("aria-label","Expand all directories"),this.toggleAll.setAttribute("aria-live","polite")):(this.toggleAll.innerText="Collapse all",this.toggleAll.onclick=this.collapseAllItems,this.toggleAll.setAttribute("aria-label","Collapse all directories"),this.toggleAll.setAttribute("aria-live","polite"))}};I();var m=document.querySelector(".js-expandableTable");if(m){let l=new T(m,document.querySelector(".js-expandAllDirectories"));window.location.search.includes("expand-directories")&&l.expandAllItems();let e=document.querySelector(".js-showInternalDirectories");e&&(document.querySelector(".UnitDirectories-internal")&&(e.style.display="block",e.setAttribute("aria-label","Show Internal Directories"),e.setAttribute("aria-describedby","showInternal-description")),e.addEventListener("click",()=>{m.classList.contains("UnitDirectories-showInternal")?(m.classList.remove("UnitDirectories-showInternal"),e.innerText="Show internal",e.setAttribute("aria-label","Show Internal Directories"),e.setAttribute("aria-live","polite"),e.setAttribute("aria-describedby","showInternal-description")):(m.classList.add("UnitDirectories-showInternal"),e.innerText="Hide internal",e.setAttribute("aria-label","Hide Internal Directories"),e.setAttribute("aria-live","polite"),e.setAttribute("aria-describedby","hideInternal-description"))})),document.querySelector('html[data-local="true"]')&&(e==null||e.click())}var S=document.querySelector(".js-tree");if(S){let l=new E(S),e=C(l),t=document.querySelector(".js-mainNavMobile");t&&t.firstElementChild&&(t==null||t.replaceChild(e,t.firstElementChild)),e.firstElementChild&&new f(e.firstElementChild)}var o=document.querySelector(".js-readme"),g=document.querySelector(".js-readmeContent"),w=document.querySelector(".js-readmeOutline"),b=document.querySelectorAll(".js-readmeExpand"),k=document.querySelector(".js-readmeCollapse"),A=document.querySelector(".DocNavMobile-select");o&&g&&w&&b.length&&k&&(o.clientHeight>320&&(o==null||o.classList.remove("UnitReadme--expanded"),o==null||o.classList.add("UnitReadme--toggle")),window.location.hash.includes("readme")&&h(),A==null||A.addEventListener("change",l=>{l.target.value.startsWith("readme-")&&h()}),b.forEach(l=>l.addEventListener("click",e=>{e.preventDefault(),h(),o.scrollIntoView()})),k.addEventListener("click",l=>{l.preventDefault(),o.classList.remove("UnitReadme--expanded"),b[1]&&b[1].scrollIntoView({block:"center"})}),g.addEventListener("keyup",()=>{h()}),g.addEventListener("click",()=>{h()}),w.addEventListener("click",()=>{h()}),document.addEventListener("keydown",l=>{(l.ctrlKey||l.metaKey)&&l.key==="f"&&h()}));function h(){history.replaceState(null,"",`${location.pathname}${location.search}#section-readme`),o==null||o.classList.add("UnitReadme--expanded")}function M(){var t;if(!location.hash)return;let l=document.getElementById(location.hash.slice(1)),e=(t=l==null?void 0:l.parentElement)==null?void 0:t.parentElement;(e==null?void 0:e.nodeName)==="DETAILS"&&(e.open=!0)}M();window.addEventListener("hashchange",()=>M());document.querySelectorAll(".js-buildContextSelect").forEach(l=>{l.addEventListener("change",e=>{window.location.search=`?GOOS=${e.target.value}`})}); +/** + * @license + * Copyright 2024 The Go Authors. All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ /*! * @license * Copyright 2021 The Go Authors. All rights reserved. diff --git a/static/frontend/unit/main/main.js.map b/static/frontend/unit/main/main.js.map index d17695203..f599413c2 100644 --- a/static/frontend/unit/main/main.js.map +++ b/static/frontend/unit/main/main.js.map @@ -1,7 +1,7 @@ { "version": 3, - "sources": ["../../../shared/playground/playground.ts", "../../../shared/outline/select.ts", "../../../shared/outline/tree.ts", "../../../shared/table/table.ts", "main.ts"], - "sourcesContent": ["/*!\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n// This file implements the playground implementation of the documentation\n// page. The playground involves a \"play\" button that allows you to open up\n// a new link to play.golang.org using the example code.\n\n// The CSS is in static/frontend/unit/main/_doc.css\n\n/**\n * CSS classes used by PlaygroundExampleController\n */\nconst PlayExampleClassName = {\n PLAY_HREF: '.js-exampleHref',\n PLAY_CONTAINER: '.js-exampleContainer',\n EXAMPLE_INPUT: '.Documentation-exampleCode',\n EXAMPLE_OUTPUT: '.Documentation-exampleOutput',\n EXAMPLE_OUTPUT_CONTAINER: '.js-exampleOutputContainer',\n EXAMPLE_ERROR: '.Documentation-exampleError',\n PLAY_BUTTON: '.Documentation-examplePlayButton',\n SHARE_BUTTON: '.Documentation-exampleShareButton',\n FORMAT_BUTTON: '.Documentation-exampleFormatButton',\n RUN_BUTTON: '.Documentation-exampleRunButton',\n};\n\n/**\n * This controller enables playground examples to expand their dropdown or\n * generate shareable Go Playground URLs.\n */\nexport class PlaygroundExampleController {\n /**\n * The anchor tag used to identify the container with an example href.\n * There is only one in an example container div.\n */\n private readonly anchorEl: HTMLAnchorElement | null;\n\n /**\n * The error element\n */\n private readonly errorEl: Element | null;\n\n /**\n * Buttons that redirect to an example's playground, this element\n * only exists in executable examples.\n */\n private readonly playButtonEl: Element | null;\n private readonly shareButtonEl: Element | null;\n\n /**\n * Button that formats the code in an example's playground.\n */\n private readonly formatButtonEl: Element | null;\n\n /**\n * Button that runs the code in an example's playground, this element\n * only exists in executable examples.\n */\n private readonly runButtonEl: Element | null;\n\n /**\n * The executable code of an example.\n */\n private readonly inputEl: HTMLTextAreaElement | null;\n\n /**\n * The output of the given example code. This only exists if the\n * author of the package provides an output for this example.\n */\n private readonly outputEl: Element | null;\n\n /**\n * The container for the output.\n */\n private readonly outputContainerEl: HTMLElement | null;\n\n /**\n * @param exampleEl The div that contains playground content for the given example.\n */\n constructor(private readonly exampleEl: HTMLDetailsElement) {\n this.exampleEl = exampleEl;\n this.anchorEl = exampleEl.querySelector('a');\n this.errorEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_ERROR);\n this.playButtonEl = exampleEl.querySelector(PlayExampleClassName.PLAY_BUTTON);\n this.shareButtonEl = exampleEl.querySelector(PlayExampleClassName.SHARE_BUTTON);\n this.formatButtonEl = exampleEl.querySelector(PlayExampleClassName.FORMAT_BUTTON);\n this.runButtonEl = exampleEl.querySelector(PlayExampleClassName.RUN_BUTTON);\n this.inputEl = this.makeTextArea(exampleEl.querySelector(PlayExampleClassName.EXAMPLE_INPUT));\n this.outputEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_OUTPUT);\n this.outputContainerEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_OUTPUT_CONTAINER);\n\n // This is legacy listener to be replaced the listener for shareButtonEl.\n this.playButtonEl?.addEventListener('click', () => this.handleShareButtonClick());\n this.shareButtonEl?.addEventListener('click', () => this.handleShareButtonClick());\n this.formatButtonEl?.addEventListener('click', () => this.handleFormatButtonClick());\n this.runButtonEl?.addEventListener('click', () => this.handleRunButtonClick());\n\n if (!this.inputEl) return;\n\n this.resize();\n this.inputEl.addEventListener('keyup', () => this.resize());\n this.inputEl.addEventListener('keydown', e => this.onKeydown(e));\n }\n\n /**\n * Replace the pre element with a textarea. The examples are initially rendered\n * as pre elements so they're fully visible when JS is disabled.\n */\n makeTextArea(el: Element | null): HTMLTextAreaElement {\n const t = document.createElement('textarea');\n t.classList.add('Documentation-exampleCode', 'code');\n t.spellcheck = false;\n t.value = el?.textContent ?? '';\n el?.parentElement?.replaceChild(t, el);\n return t;\n }\n\n /**\n * Retrieve the hash value of the anchor element.\n */\n getAnchorHash(): string | undefined {\n return this.anchorEl?.hash;\n }\n\n /**\n * Expands the current playground example.\n */\n expand(): void {\n this.exampleEl.open = true;\n }\n\n /**\n * Resizes the input element to accommodate the amount of text present.\n */\n private resize(): void {\n if (this.inputEl?.value) {\n const numLineBreaks = (this.inputEl.value.match(/\\n/g) || []).length;\n // min-height + lines x line-height + padding + border\n this.inputEl.style.height = `${(20 + numLineBreaks * 20 + 12 + 2) / 16}rem`;\n }\n }\n\n /**\n * Handler to override keyboard behavior in the playground's\n * textarea element.\n *\n * Tab key inserts tabs into the example playground instead of\n * switching to the next interactive element.\n * @param e input element keyboard event.\n */\n private onKeydown(e: KeyboardEvent) {\n if (e.key === 'Tab') {\n document.execCommand('insertText', false, '\\t');\n e.preventDefault();\n }\n }\n\n /**\n * Changes the text of the example's input box.\n */\n private setInputText(output: string) {\n if (this.inputEl) {\n this.inputEl.value = output;\n }\n }\n\n /**\n * Changes the text of the example's output box.\n */\n private setOutputText(output: string) {\n if (this.outputEl) {\n this.outputEl.textContent = output;\n }\n if (this.outputContainerEl) {\n this.outputContainerEl.hidden = false;\n }\n }\n\n /**\n * Appends to the text of the example's output box.\n */\n private appendToOutputText(output: string) {\n if (this.outputEl) {\n this.outputEl.textContent += output;\n }\n if (this.outputContainerEl) {\n this.outputContainerEl.hidden = false;\n }\n }\n\n private setOutputHTML(output: string) {\n if (this.outputEl) {\n this.outputEl.innerHTML = output;\n }\n }\n\n /**\n * Sets the error message text and overwrites\n * output box to indicate a failed response.\n */\n private setErrorText(err: string) {\n if (this.errorEl) {\n this.errorEl.textContent = err;\n }\n this.setOutputText('An error has occurred\u2026');\n }\n\n private getCodeWithModFile(): string {\n let codeWithModFile = this.inputEl?.value ?? '';\n const moduleVars = document.querySelector('.js-playgroundVars')?.dataset ?? {};\n if (moduleVars.modulepath !== 'std') {\n codeWithModFile = codeWithModFile.concat(`\n-- go.mod --\nmodule play.ground\n\nrequire ${moduleVars.modulepath} ${moduleVars.version}\n`);\n }\n\n return codeWithModFile;\n }\n\n /**\n * Opens a new window to play.golang.org using the\n * example snippet's code in the playground.\n */\n private handleShareButtonClick() {\n const PLAYGROUND_BASE_URL = 'https://play.golang.org/p/';\n\n this.setOutputText('Waiting for remote server\u2026');\n\n fetch('/play/share', {\n method: 'POST',\n body: this.getCodeWithModFile(),\n })\n .then(res => res.text())\n .then(shareId => {\n const href = PLAYGROUND_BASE_URL + shareId;\n this.setOutputHTML(`${href}`);\n window.open(href);\n })\n .catch(err => {\n this.setErrorText(err);\n });\n }\n\n /**\n * Runs gofmt on the example snippet in the playground.\n */\n private handleFormatButtonClick() {\n this.setOutputText('Waiting for remote server\u2026');\n const body = new FormData();\n body.append('body', this.inputEl?.value ?? '');\n\n fetch('/play/fmt', {\n method: 'POST',\n body: body,\n })\n .then(res => res.json())\n .then(({ Body, Error }) => {\n this.setOutputText(Error || 'Done.');\n if (Body) {\n this.setInputText(Body);\n this.resize();\n }\n })\n .catch(err => {\n this.setErrorText(err);\n });\n }\n\n /**\n * Runs the code snippet in the example playground.\n */\n private handleRunButtonClick() {\n this.setOutputText('Waiting for remote server\u2026');\n\n fetch('/play/compile', {\n method: 'POST',\n body: JSON.stringify({ body: this.getCodeWithModFile(), version: 2 }),\n })\n .then(res => res.json())\n .then(async ({ Events, Errors }) => {\n this.setOutputText(Errors || '');\n for (const e of Events || []) {\n this.appendToOutputText(e.Message);\n await new Promise(resolve => setTimeout(resolve, e.Delay / 1000000));\n }\n })\n .catch(err => {\n this.setErrorText(err);\n });\n }\n}\n\nexport function initPlaygrounds(): void {\n const exampleHashRegex = location.hash.match(/^#(example-.*)$/);\n if (exampleHashRegex) {\n const exampleHashEl = document.getElementById(exampleHashRegex[1]) as HTMLDetailsElement;\n if (exampleHashEl) {\n exampleHashEl.open = true;\n }\n }\n\n // We use a spread operator to convert a nodelist into an array of elements.\n const exampleHrefs = [\n ...document.querySelectorAll(PlayExampleClassName.PLAY_HREF),\n ];\n\n /**\n * Sometimes exampleHrefs and playContainers are in different order, so we\n * find an exampleHref from a common hash.\n * @param playContainer - playground container\n */\n const findExampleHash = (playContainer: PlaygroundExampleController) =>\n exampleHrefs.find(ex => {\n return ex.hash === playContainer.getAnchorHash();\n });\n\n for (const el of document.querySelectorAll(PlayExampleClassName.PLAY_CONTAINER)) {\n // There should be the same amount of hrefs referencing examples as example containers.\n const playContainer = new PlaygroundExampleController(el as HTMLDetailsElement);\n const exampleHref = findExampleHash(playContainer);\n if (exampleHref) {\n exampleHref.addEventListener('click', () => {\n playContainer.expand();\n });\n } else {\n console.warn('example href not found');\n }\n }\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { TreeNavController } from './tree.js';\n\nexport class SelectNavController {\n constructor(private el: Element) {\n this.el.addEventListener('change', e => {\n const target = e.target as HTMLSelectElement;\n let href = target.value;\n if (!target.value.startsWith('/')) {\n href = '/' + href;\n }\n window.location.href = href;\n });\n }\n}\n\nexport function makeSelectNav(tree: TreeNavController): HTMLLabelElement {\n const label = document.createElement('label');\n label.classList.add('go-Label');\n label.setAttribute('aria-label', 'Menu');\n const select = document.createElement('select');\n select.classList.add('go-Select', 'js-selectNav');\n label.appendChild(select);\n const outline = document.createElement('optgroup');\n outline.label = 'Outline';\n select.appendChild(outline);\n const groupMap: Record = {};\n let group: HTMLOptGroupElement;\n for (const t of tree.treeitems) {\n if (Number(t.depth) > 4) continue;\n if (t.groupTreeitem) {\n group = groupMap[t.groupTreeitem.label];\n if (!group) {\n group = groupMap[t.groupTreeitem.label] = document.createElement('optgroup');\n group.label = t.groupTreeitem.label;\n select.appendChild(group);\n }\n } else {\n group = outline;\n }\n const o = document.createElement('option');\n o.label = t.label;\n o.textContent = t.label;\n o.value = (t.el as HTMLAnchorElement).href.replace(window.location.origin, '').replace('/', '');\n group.appendChild(o);\n }\n tree.addObserver(t => {\n const hash = (t.el as HTMLAnchorElement).hash;\n const value = select.querySelector(`[value$=\"${hash}\"]`)?.value;\n if (value) {\n select.value = value;\n }\n }, 50);\n return label;\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * TreeNavController is the navigation tree component of the documentation page.\n * It adds accessiblity attributes to a tree, observes the heading elements\n * focus the topmost link for headings visible on the page, and implements the\n * WAI-ARIA Treeview Design Pattern with full\n * [keyboard support](https://www.w3.org/TR/wai-aria-practices/examples/treeview/treeview-2/treeview-2a.html#kbd_label).\n */\nexport class TreeNavController {\n treeitems: TreeItem[];\n\n /**\n * firstChars is the first character of each treeitem in the same order\n * as this.treeitems. We use this array to set focus by character when\n * navigating the tree with a keyboard.\n */\n private firstChars: string[];\n private firstTreeitem: TreeItem | null;\n private lastTreeitem: TreeItem | null;\n private observerCallbacks: ((t: TreeItem) => void)[];\n\n constructor(private el: HTMLElement) {\n this.treeitems = [];\n this.firstChars = [];\n this.firstTreeitem = null;\n this.lastTreeitem = null;\n this.observerCallbacks = [];\n this.init();\n }\n\n private init(): void {\n this.handleResize();\n window.addEventListener('resize', this.handleResize);\n this.findTreeItems();\n this.updateVisibleTreeitems();\n this.observeTargets();\n if (this.firstTreeitem) {\n this.firstTreeitem.el.tabIndex = 0;\n }\n }\n\n private handleResize = (): void => {\n this.el.style.setProperty('--js-tree-height', '100vh');\n this.el.style.setProperty('--js-tree-height', this.el.clientHeight + 'px');\n };\n\n private observeTargets() {\n this.addObserver(treeitem => {\n this.expandTreeitem(treeitem);\n this.setSelected(treeitem);\n // TODO: Fix scroll issue in https://golang.org/issue/47450.\n // treeitem.el.scrollIntoView({ block: 'nearest' });\n });\n\n const targets = new Map();\n const observer = new IntersectionObserver(\n entries => {\n for (const entry of entries) {\n targets.set(entry.target.id, entry.isIntersecting || entry.intersectionRatio === 1);\n }\n for (const [id, isIntersecting] of targets) {\n if (isIntersecting) {\n const active = this.treeitems.find(t =>\n (t.el as HTMLAnchorElement)?.href.endsWith(`#${id}`)\n );\n if (active) {\n for (const fn of this.observerCallbacks) {\n fn(active);\n }\n }\n break;\n }\n }\n },\n {\n threshold: 1.0,\n rootMargin: '-60px 0px 0px 0px',\n }\n );\n\n for (const href of this.treeitems.map(t => t.el.getAttribute('href'))) {\n if (href) {\n const id = href.replace(window.location.origin, '').replace('/', '').replace('#', '');\n const target = document.getElementById(id);\n if (target && !id.startsWith('example-')) {\n observer.observe(target);\n }\n }\n }\n }\n\n addObserver(fn: (t: TreeItem) => void, delay = 200): void {\n this.observerCallbacks.push(debounce(fn, delay));\n }\n\n setFocusToNextItem(currentItem: TreeItem): void {\n let nextItem = null;\n for (let i = currentItem.index + 1; i < this.treeitems.length; i++) {\n const ti = this.treeitems[i];\n if (ti.isVisible) {\n nextItem = ti;\n break;\n }\n }\n if (nextItem) {\n this.setFocusToItem(nextItem);\n }\n }\n\n setFocusToPreviousItem(currentItem: TreeItem): void {\n let prevItem = null;\n for (let i = currentItem.index - 1; i > -1; i--) {\n const ti = this.treeitems[i];\n if (ti.isVisible) {\n prevItem = ti;\n break;\n }\n }\n if (prevItem) {\n this.setFocusToItem(prevItem);\n }\n }\n\n setFocusToParentItem(currentItem: TreeItem): void {\n if (currentItem.groupTreeitem) {\n this.setFocusToItem(currentItem.groupTreeitem);\n }\n }\n\n setFocusToFirstItem(): void {\n this.firstTreeitem && this.setFocusToItem(this.firstTreeitem);\n }\n\n setFocusToLastItem(): void {\n this.lastTreeitem && this.setFocusToItem(this.lastTreeitem);\n }\n\n setSelected(currentItem: TreeItem): void {\n for (const l1 of this.el.querySelectorAll('[aria-expanded=\"true\"]')) {\n if (l1 === currentItem.el) continue;\n if (!l1.nextElementSibling?.contains(currentItem.el)) {\n l1.setAttribute('aria-expanded', 'false');\n }\n }\n for (const l1 of this.el.querySelectorAll('[aria-selected]')) {\n if (l1 !== currentItem.el) {\n l1.setAttribute('aria-selected', 'false');\n }\n }\n currentItem.el.setAttribute('aria-selected', 'true');\n this.updateVisibleTreeitems();\n this.setFocusToItem(currentItem, false);\n }\n\n expandTreeitem(treeitem: TreeItem): void {\n let currentItem: TreeItem | null = treeitem;\n while (currentItem) {\n if (currentItem.isExpandable) {\n currentItem.el.setAttribute('aria-expanded', 'true');\n }\n currentItem = currentItem.groupTreeitem;\n }\n this.updateVisibleTreeitems();\n }\n\n expandAllSiblingItems(currentItem: TreeItem): void {\n for (const ti of this.treeitems) {\n if (ti.groupTreeitem === currentItem.groupTreeitem && ti.isExpandable) {\n this.expandTreeitem(ti);\n }\n }\n }\n\n collapseTreeitem(currentItem: TreeItem): void {\n let groupTreeitem = null;\n\n if (currentItem.isExpanded()) {\n groupTreeitem = currentItem;\n } else {\n groupTreeitem = currentItem.groupTreeitem;\n }\n\n if (groupTreeitem) {\n groupTreeitem.el.setAttribute('aria-expanded', 'false');\n this.updateVisibleTreeitems();\n this.setFocusToItem(groupTreeitem);\n }\n }\n\n setFocusByFirstCharacter(currentItem: TreeItem, char: string): void {\n let start: number, index: number;\n char = char.toLowerCase();\n\n // Get start index for search based on position of currentItem\n start = currentItem.index + 1;\n if (start === this.treeitems.length) {\n start = 0;\n }\n\n // Check remaining slots in the menu\n index = this.getIndexFirstChars(start, char);\n\n // If not found in remaining slots, check from beginning\n if (index === -1) {\n index = this.getIndexFirstChars(0, char);\n }\n\n // If match was found...\n if (index > -1) {\n this.setFocusToItem(this.treeitems[index]);\n }\n }\n\n private findTreeItems() {\n const findItems = (el: HTMLElement, group: TreeItem | null) => {\n let ti = group;\n let curr = el.firstElementChild as HTMLElement;\n while (curr) {\n if (curr.tagName === 'A' || curr.tagName === 'SPAN') {\n ti = new TreeItem(curr, this, group);\n this.treeitems.push(ti);\n this.firstChars.push(ti.label.substring(0, 1).toLowerCase());\n }\n if (curr.firstElementChild) {\n findItems(curr, ti);\n }\n curr = curr.nextElementSibling as HTMLElement;\n }\n };\n findItems(this.el as HTMLElement, null);\n this.treeitems.map((ti, idx) => (ti.index = idx));\n }\n\n private updateVisibleTreeitems(): void {\n this.firstTreeitem = this.treeitems[0];\n\n for (const ti of this.treeitems) {\n let parent = ti.groupTreeitem;\n ti.isVisible = true;\n while (parent && parent.el !== this.el) {\n if (!parent.isExpanded()) {\n ti.isVisible = false;\n }\n parent = parent.groupTreeitem;\n }\n if (ti.isVisible) {\n this.lastTreeitem = ti;\n }\n }\n }\n\n private setFocusToItem(treeitem: TreeItem, focusEl = true) {\n treeitem.el.tabIndex = 0;\n if (focusEl) {\n treeitem.el.focus();\n }\n for (const ti of this.treeitems) {\n if (ti !== treeitem) {\n ti.el.tabIndex = -1;\n }\n }\n }\n\n private getIndexFirstChars(startIndex: number, char: string): number {\n for (let i = startIndex; i < this.firstChars.length; i++) {\n if (this.treeitems[i].isVisible && char === this.firstChars[i]) {\n return i;\n }\n }\n return -1;\n }\n}\n\nclass TreeItem {\n el: HTMLElement;\n groupTreeitem: TreeItem | null;\n label: string;\n isExpandable: boolean;\n isVisible: boolean;\n depth: number;\n index: number;\n\n private tree: TreeNavController;\n private isInGroup: boolean;\n\n constructor(el: HTMLElement, treeObj: TreeNavController, group: TreeItem | null) {\n el.tabIndex = -1;\n this.el = el;\n this.groupTreeitem = group;\n this.label = el.textContent?.trim() ?? '';\n this.tree = treeObj;\n this.depth = (group?.depth || 0) + 1;\n this.index = 0;\n\n const parent = el.parentElement;\n if (parent?.tagName.toLowerCase() === 'li') {\n parent?.setAttribute('role', 'none');\n }\n el.setAttribute('aria-level', this.depth + '');\n if (el.getAttribute('aria-label')) {\n this.label = el?.getAttribute('aria-label')?.trim() ?? '';\n }\n\n this.isExpandable = false;\n this.isVisible = false;\n this.isInGroup = !!group;\n\n let curr = el.nextElementSibling;\n while (curr) {\n if (curr.tagName.toLowerCase() == 'ul') {\n const groupId = `${group?.label ?? ''} nav group ${this.label}`.replace(/[\\W_]+/g, '_');\n el.setAttribute('aria-owns', groupId);\n el.setAttribute('aria-expanded', 'false');\n curr.setAttribute('role', 'group');\n curr.setAttribute('id', groupId);\n this.isExpandable = true;\n break;\n }\n\n curr = curr.nextElementSibling;\n }\n this.init();\n }\n\n private init() {\n this.el.tabIndex = -1;\n if (!this.el.getAttribute('role')) {\n this.el.setAttribute('role', 'treeitem');\n }\n this.el.addEventListener('keydown', this.handleKeydown.bind(this));\n this.el.addEventListener('click', this.handleClick.bind(this));\n this.el.addEventListener('focus', this.handleFocus.bind(this));\n this.el.addEventListener('blur', this.handleBlur.bind(this));\n }\n\n isExpanded() {\n if (this.isExpandable) {\n return this.el.getAttribute('aria-expanded') === 'true';\n }\n\n return false;\n }\n\n isSelected() {\n return this.el.getAttribute('aria-selected') === 'true';\n }\n\n private handleClick(event: MouseEvent) {\n // only process click events that directly happened on this treeitem\n if (event.target !== this.el && event.target !== this.el.firstElementChild) {\n return;\n }\n if (this.isExpandable) {\n if (this.isExpanded() && this.isSelected()) {\n this.tree.collapseTreeitem(this);\n } else {\n this.tree.expandTreeitem(this);\n }\n event.stopPropagation();\n }\n this.tree.setSelected(this);\n }\n\n private handleFocus() {\n let el = this.el;\n if (this.isExpandable) {\n el = (el.firstElementChild as HTMLElement) ?? el;\n }\n el.classList.add('focus');\n }\n\n private handleBlur() {\n let el = this.el;\n if (this.isExpandable) {\n el = (el.firstElementChild as HTMLElement) ?? el;\n }\n el.classList.remove('focus');\n }\n\n private handleKeydown(event: KeyboardEvent) {\n if (event.altKey || event.ctrlKey || event.metaKey) {\n return;\n }\n\n let captured = false;\n switch (event.key) {\n case ' ':\n case 'Enter':\n if (this.isExpandable) {\n if (this.isExpanded() && this.isSelected()) {\n this.tree.collapseTreeitem(this);\n } else {\n this.tree.expandTreeitem(this);\n }\n captured = true;\n } else {\n event.stopPropagation();\n }\n this.tree.setSelected(this);\n break;\n\n case 'ArrowUp':\n this.tree.setFocusToPreviousItem(this);\n captured = true;\n break;\n\n case 'ArrowDown':\n this.tree.setFocusToNextItem(this);\n captured = true;\n break;\n\n case 'ArrowRight':\n if (this.isExpandable) {\n if (this.isExpanded()) {\n this.tree.setFocusToNextItem(this);\n } else {\n this.tree.expandTreeitem(this);\n }\n }\n captured = true;\n break;\n\n case 'ArrowLeft':\n if (this.isExpandable && this.isExpanded()) {\n this.tree.collapseTreeitem(this);\n captured = true;\n } else {\n if (this.isInGroup) {\n this.tree.setFocusToParentItem(this);\n captured = true;\n }\n }\n break;\n\n case 'Home':\n this.tree.setFocusToFirstItem();\n captured = true;\n break;\n\n case 'End':\n this.tree.setFocusToLastItem();\n captured = true;\n break;\n\n default:\n if (event.key.length === 1 && event.key.match(/\\S/)) {\n if (event.key == '*') {\n this.tree.expandAllSiblingItems(this);\n } else {\n this.tree.setFocusByFirstCharacter(this, event.key);\n }\n captured = true;\n }\n break;\n }\n\n if (captured) {\n event.stopPropagation();\n event.preventDefault();\n }\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction debounce any>(func: T, wait: number) {\n let timeout: ReturnType | null;\n return (...args: Parameters) => {\n const later = () => {\n timeout = null;\n func(...args);\n };\n if (timeout) {\n clearTimeout(timeout);\n }\n timeout = setTimeout(later, wait);\n };\n}\n", "/*!\n * @license\n * Copyright 2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * Controller for a table element with expandable rows. Adds event listeners to\n * a toggle within a table row that controls visiblity of additional related\n * rows in the table.\n *\n * @example\n * ```typescript\n * import {ExpandableRowsTableController} from '/static/js/table';\n *\n * const el = document .querySelector('.js-myTableElement')\n * new ExpandableRowsTableController(el));\n * ```\n */\nexport class ExpandableRowsTableController {\n private rows: HTMLTableRowElement[];\n private toggles: HTMLButtonElement[];\n\n /**\n * Create a table controller.\n * @param table - The table element to which the controller binds.\n */\n constructor(private table: HTMLTableElement, private toggleAll?: HTMLButtonElement | null) {\n this.rows = Array.from(table.querySelectorAll('[data-aria-controls]'));\n this.toggles = Array.from(this.table.querySelectorAll('[aria-expanded]'));\n this.setAttributes();\n this.attachEventListeners();\n this.update();\n }\n\n /**\n * setAttributes sets data-aria-* and data-id attributes to regular\n * html attributes as a workaround for limitations from safehtml.\n */\n private setAttributes() {\n for (const a of ['data-aria-controls', 'data-aria-labelledby', 'data-id']) {\n this.table.querySelectorAll(`[${a}]`).forEach(t => {\n t.setAttribute(a.replace('data-', ''), t.getAttribute(a) ?? '');\n t.removeAttribute(a);\n });\n }\n }\n\n private attachEventListeners() {\n this.rows.forEach(t => {\n t.addEventListener('click', e => {\n this.handleToggleClick(e);\n });\n });\n this.toggleAll?.addEventListener('click', () => {\n this.expandAllItems();\n });\n\n document.addEventListener('keydown', e => {\n if ((e.ctrlKey || e.metaKey) && e.key === 'f') {\n this.expandAllItems();\n }\n });\n }\n\n private handleToggleClick(e: MouseEvent) {\n let target = e.currentTarget as HTMLTableRowElement | null;\n if (!target?.hasAttribute('aria-expanded')) {\n target = this.table.querySelector(\n `button[aria-controls=\"${target?.getAttribute('aria-controls')}\"]`\n );\n }\n const isExpanded = target?.getAttribute('aria-expanded') === 'true';\n target?.setAttribute('aria-expanded', isExpanded ? 'false' : 'true');\n e.stopPropagation();\n this.update();\n }\n\n expandAllItems = (): void => {\n this.toggles.map(t => t.setAttribute('aria-expanded', 'true'));\n this.update();\n };\n\n private collapseAllItems = () => {\n this.toggles.map(t => t.setAttribute('aria-expanded', 'false'));\n this.update();\n };\n\n private update = () => {\n this.updateVisibleItems();\n setTimeout(() => this.updateGlobalToggle());\n };\n\n private updateVisibleItems() {\n this.rows.map(t => {\n const isExpanded = t?.getAttribute('aria-expanded') === 'true';\n const rowIds = t?.getAttribute('aria-controls')?.trimEnd().split(' ');\n rowIds?.map(id => {\n const target = document.getElementById(`${id}`);\n if (isExpanded) {\n target?.classList.add('visible');\n target?.classList.remove('hidden');\n } else {\n target?.classList.add('hidden');\n target?.classList.remove('visible');\n }\n });\n });\n }\n\n private updateGlobalToggle() {\n if (!this.toggleAll) return;\n if (this.rows.some(t => t.hasAttribute('aria-expanded'))) {\n this.toggleAll.style.display = 'block';\n }\n const someCollapsed = this.toggles.some(el => el.getAttribute('aria-expanded') === 'false');\n if (someCollapsed) {\n this.toggleAll.innerText = 'Expand all';\n this.toggleAll.onclick = this.expandAllItems;\n this.toggleAll.setAttribute('aria-label', 'Expand all directories');\n this.toggleAll.setAttribute('aria-live', 'polite');\n } else {\n this.toggleAll.innerText = 'Collapse all';\n this.toggleAll.onclick = this.collapseAllItems;\n this.toggleAll.setAttribute('aria-label', 'Collapse all directories');\n this.toggleAll.setAttribute('aria-live', 'polite');\n }\n }\n}\n", "import { initPlaygrounds } from 'static/shared/playground/playground';\nimport { SelectNavController, makeSelectNav } from 'static/shared/outline/select';\nimport { TreeNavController } from 'static/shared/outline/tree';\nimport { ExpandableRowsTableController } from 'static/shared/table/table';\n\ninitPlaygrounds();\n\nconst directories = document.querySelector('.js-expandableTable');\nif (directories) {\n const table = new ExpandableRowsTableController(\n directories,\n document.querySelector('.js-expandAllDirectories')\n );\n // Expand directories on page load with expand-directories query param.\n if (window.location.search.includes('expand-directories')) {\n table.expandAllItems();\n }\n\n const internalToggle = document.querySelector('.js-showInternalDirectories');\n if (internalToggle) {\n if (document.querySelector('.UnitDirectories-internal')) {\n internalToggle.style.display = 'block';\n internalToggle.setAttribute('aria-label', 'Show Internal Directories');\n internalToggle.setAttribute('aria-describedby', 'showInternal-description');\n }\n internalToggle.addEventListener('click', () => {\n if (directories.classList.contains('UnitDirectories-showInternal')) {\n directories.classList.remove('UnitDirectories-showInternal');\n internalToggle.innerText = 'Show internal';\n internalToggle.setAttribute('aria-label', 'Show Internal Directories');\n internalToggle.setAttribute('aria-live', 'polite');\n internalToggle.setAttribute('aria-describedby', 'showInternal-description');\n } else {\n directories.classList.add('UnitDirectories-showInternal');\n internalToggle.innerText = 'Hide internal';\n internalToggle.setAttribute('aria-label', 'Hide Internal Directories');\n internalToggle.setAttribute('aria-live', 'polite');\n internalToggle.setAttribute('aria-describedby', 'hideInternal-description');\n }\n });\n }\n if (document.querySelector('html[data-local=\"true\"]')) {\n internalToggle?.click();\n }\n}\n\nconst treeEl = document.querySelector('.js-tree');\nif (treeEl) {\n const treeCtrl = new TreeNavController(treeEl);\n const select = makeSelectNav(treeCtrl);\n const mobileNav = document.querySelector('.js-mainNavMobile');\n if (mobileNav && mobileNav.firstElementChild) {\n mobileNav?.replaceChild(select, mobileNav.firstElementChild);\n }\n if (select.firstElementChild) {\n new SelectNavController(select.firstElementChild);\n }\n}\n\n/**\n * Event handlers for expanding and collapsing the readme section.\n */\nconst readme = document.querySelector('.js-readme');\nconst readmeContent = document.querySelector('.js-readmeContent');\nconst readmeOutline = document.querySelector('.js-readmeOutline');\nconst readmeExpand = document.querySelectorAll('.js-readmeExpand');\nconst readmeCollapse = document.querySelector('.js-readmeCollapse');\nconst mobileNavSelect = document.querySelector('.DocNavMobile-select');\nif (readme && readmeContent && readmeOutline && readmeExpand.length && readmeCollapse) {\n if (readme.clientHeight > 320) {\n readme?.classList.remove('UnitReadme--expanded');\n readme?.classList.add('UnitReadme--toggle');\n }\n if (window.location.hash.includes('readme')) {\n expandReadme();\n }\n mobileNavSelect?.addEventListener('change', e => {\n if ((e.target as HTMLSelectElement).value.startsWith('readme-')) {\n expandReadme();\n }\n });\n readmeExpand.forEach(el =>\n el.addEventListener('click', e => {\n e.preventDefault();\n expandReadme();\n readme.scrollIntoView();\n })\n );\n readmeCollapse.addEventListener('click', e => {\n e.preventDefault();\n readme.classList.remove('UnitReadme--expanded');\n if (readmeExpand[1]) {\n readmeExpand[1].scrollIntoView({ block: 'center' });\n }\n });\n readmeContent.addEventListener('keyup', () => {\n expandReadme();\n });\n readmeContent.addEventListener('click', () => {\n expandReadme();\n });\n readmeOutline.addEventListener('click', () => {\n expandReadme();\n });\n document.addEventListener('keydown', e => {\n if ((e.ctrlKey || e.metaKey) && e.key === 'f') {\n expandReadme();\n }\n });\n}\n\n/**\n * expandReadme expands the readme and adds the section-readme hash to the\n * URL so it stays expanded when navigating back from an external link.\n */\nfunction expandReadme() {\n history.replaceState(null, '', `${location.pathname}${location.search}#section-readme`);\n readme?.classList.add('UnitReadme--expanded');\n}\n\n/**\n * Expand details items that are focused. This will expand\n * deprecated symbols when they are navigated to from the index\n * or a direct link.\n */\nfunction openDeprecatedSymbol() {\n if (!location.hash) return;\n const heading = document.getElementById(location.hash.slice(1));\n const grandParent = heading?.parentElement?.parentElement as HTMLDetailsElement | null;\n if (grandParent?.nodeName === 'DETAILS') {\n grandParent.open = true;\n }\n}\nopenDeprecatedSymbol();\nwindow.addEventListener('hashchange', () => openDeprecatedSymbol());\n\n/**\n * Listen for changes in the build context dropdown.\n */\ndocument.querySelectorAll('.js-buildContextSelect').forEach(el => {\n el.addEventListener('change', e => {\n window.location.search = `?GOOS=${(e.target as HTMLSelectElement).value}`;\n });\n});\n"], - "mappings": "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", - "names": ["PlayExampleClassName", "PlaygroundExampleController", "exampleEl", "_a", "_b", "_c", "_d", "e", "el", "numLineBreaks", "output", "err", "codeWithModFile", "moduleVars", "PLAYGROUND_BASE_URL", "res", "shareId", "href", "body", "Body", "Error", "Events", "Errors", "resolve", "initPlaygrounds", "exampleHashRegex", "exampleHashEl", "exampleHrefs", "findExampleHash", "playContainer", "ex", "exampleHref", "SelectNavController", "el", "e", "target", "href", "makeSelectNav", "tree", "label", "select", "outline", "groupMap", "group", "t", "o", "_a", "hash", "value", "TreeNavController", "el", "treeitem", "targets", "observer", "entries", "entry", "id", "isIntersecting", "active", "t", "_a", "fn", "href", "target", "delay", "debounce", "currentItem", "nextItem", "ti", "prevItem", "l1", "groupTreeitem", "char", "start", "index", "findItems", "group", "curr", "TreeItem", "idx", "parent", "focusEl", "startIndex", "treeObj", "_b", "_c", "_d", "_e", "groupId", "event", "captured", "func", "wait", "timeout", "args", "later", "ExpandableRowsTableController", "table", "toggleAll", "t", "a", "_a", "e", "target", "isExpanded", "rowIds", "id", "el", "initPlaygrounds", "directories", "table", "ExpandableRowsTableController", "internalToggle", "treeEl", "treeCtrl", "TreeNavController", "select", "makeSelectNav", "mobileNav", "SelectNavController", "readme", "readmeContent", "readmeOutline", "readmeExpand", "readmeCollapse", "mobileNavSelect", "expandReadme", "e", "el", "openDeprecatedSymbol", "_a", "heading", "grandParent"] + "sources": ["../../../shared/base-path/base-path.ts", "../../../shared/playground/playground.ts", "../../../shared/outline/select.ts", "../../../shared/outline/tree.ts", "../../../shared/table/table.ts", "main.ts"], + "sourcesContent": ["/**\n * @license\n * Copyright 2024 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * \u7AD9\u70B9 URL \u5B50\u8DEF\u5F84\u524D\u7F00\uFF0C\u4ECE \u8BFB\u51FA\u3002\n *\n * fork \u7528 -base-path=/gogodocs \u542F\u52A8\u65F6\uFF0Cserver \u7AEF\u6A21\u677F\u628A attribute \u5199\u6210\n * \"/gogodocs\"\uFF1B\u4E0A\u6E38\u516C\u7F51 pkg.go.dev \u9ED8\u8BA4\u6302\u6839\uFF0Cattribute \u4E3A\u7A7A\u5B57\u7B26\u4E32\u3002\n *\n * \u5728 \u4E4B\u5916\u7684\u811A\u672C\u521D\u59CB\u5316\u9636\u6BB5\u8C03\u7528\u90FD\u5B89\u5168\u2014\u2014documentElement \u4E00\u5B9A\u5B58\u5728\u3002\n */\nexport function getBasePath(): string {\n return document.documentElement.dataset.basePath ?? '';\n}\n\n/**\n * \u7ED9\u4EE5 / \u5F00\u5934\u7684\u7EDD\u5BF9\u8DEF\u5F84\u52A0 BasePath \u524D\u7F00\u3002\n *\n * \u7528\u4E8E fetch URL / innerHTML src / location \u6BD4\u8F83\u7B49\u4EFB\u4F55\u9700\u8981\u62FC\u7AD9\u70B9\u7EDD\u5BF9 path\n * \u7684\u5730\u65B9\u3002\u975E\u7EDD\u5BF9\u8DEF\u5F84\uFF08\u4E0D\u4EE5 / \u5F00\u5934\uFF09\u539F\u6837\u8FD4\u56DE\u2014\u2014caller \u81EA\u5DF1\u4FDD\u8BC1\u8BED\u4E49\u3002\n *\n * \u4F8B\uFF1A\n * abs('/play/share') // \u2192 '/gogodocs/play/share' \u6216 '/play/share'\n * abs('/static/foo.svg') // \u2192 '/gogodocs/static/foo.svg' \u6216 '/static/foo.svg'\n * abs('relative/x') // \u2192 'relative/x'\uFF08\u4E0D\u52A8\uFF09\n */\nexport function abs(p: string): string {\n if (!p.startsWith('/')) return p;\n return getBasePath() + p;\n}\n", "/*!\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n// This file implements the playground implementation of the documentation\n// page. The playground involves a \"play\" button that allows you to open up\n// a new link to play.golang.org using the example code.\n\n// The CSS is in static/frontend/unit/main/_doc.css\n\nimport { abs } from '../base-path/base-path';\n\n/**\n * CSS classes used by PlaygroundExampleController\n */\nconst PlayExampleClassName = {\n PLAY_HREF: '.js-exampleHref',\n PLAY_CONTAINER: '.js-exampleContainer',\n EXAMPLE_INPUT: '.Documentation-exampleCode',\n EXAMPLE_OUTPUT: '.Documentation-exampleOutput',\n EXAMPLE_OUTPUT_CONTAINER: '.js-exampleOutputContainer',\n EXAMPLE_ERROR: '.Documentation-exampleError',\n PLAY_BUTTON: '.Documentation-examplePlayButton',\n SHARE_BUTTON: '.Documentation-exampleShareButton',\n FORMAT_BUTTON: '.Documentation-exampleFormatButton',\n RUN_BUTTON: '.Documentation-exampleRunButton',\n};\n\n/**\n * This controller enables playground examples to expand their dropdown or\n * generate shareable Go Playground URLs.\n */\nexport class PlaygroundExampleController {\n /**\n * The anchor tag used to identify the container with an example href.\n * There is only one in an example container div.\n */\n private readonly anchorEl: HTMLAnchorElement | null;\n\n /**\n * The error element\n */\n private readonly errorEl: Element | null;\n\n /**\n * Buttons that redirect to an example's playground, this element\n * only exists in executable examples.\n */\n private readonly playButtonEl: Element | null;\n private readonly shareButtonEl: Element | null;\n\n /**\n * Button that formats the code in an example's playground.\n */\n private readonly formatButtonEl: Element | null;\n\n /**\n * Button that runs the code in an example's playground, this element\n * only exists in executable examples.\n */\n private readonly runButtonEl: Element | null;\n\n /**\n * The executable code of an example.\n */\n private readonly inputEl: HTMLTextAreaElement | null;\n\n /**\n * The output of the given example code. This only exists if the\n * author of the package provides an output for this example.\n */\n private readonly outputEl: Element | null;\n\n /**\n * The container for the output.\n */\n private readonly outputContainerEl: HTMLElement | null;\n\n /**\n * @param exampleEl The div that contains playground content for the given example.\n */\n constructor(private readonly exampleEl: HTMLDetailsElement) {\n this.exampleEl = exampleEl;\n this.anchorEl = exampleEl.querySelector('a');\n this.errorEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_ERROR);\n this.playButtonEl = exampleEl.querySelector(PlayExampleClassName.PLAY_BUTTON);\n this.shareButtonEl = exampleEl.querySelector(PlayExampleClassName.SHARE_BUTTON);\n this.formatButtonEl = exampleEl.querySelector(PlayExampleClassName.FORMAT_BUTTON);\n this.runButtonEl = exampleEl.querySelector(PlayExampleClassName.RUN_BUTTON);\n this.inputEl = this.makeTextArea(exampleEl.querySelector(PlayExampleClassName.EXAMPLE_INPUT));\n this.outputEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_OUTPUT);\n this.outputContainerEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_OUTPUT_CONTAINER);\n\n // This is legacy listener to be replaced the listener for shareButtonEl.\n this.playButtonEl?.addEventListener('click', () => this.handleShareButtonClick());\n this.shareButtonEl?.addEventListener('click', () => this.handleShareButtonClick());\n this.formatButtonEl?.addEventListener('click', () => this.handleFormatButtonClick());\n this.runButtonEl?.addEventListener('click', () => this.handleRunButtonClick());\n\n if (!this.inputEl) return;\n\n this.resize();\n this.inputEl.addEventListener('keyup', () => this.resize());\n this.inputEl.addEventListener('keydown', e => this.onKeydown(e));\n }\n\n /**\n * Replace the pre element with a textarea. The examples are initially rendered\n * as pre elements so they're fully visible when JS is disabled.\n */\n makeTextArea(el: Element | null): HTMLTextAreaElement {\n const t = document.createElement('textarea');\n t.classList.add('Documentation-exampleCode', 'code');\n t.spellcheck = false;\n t.value = el?.textContent ?? '';\n el?.parentElement?.replaceChild(t, el);\n return t;\n }\n\n /**\n * Retrieve the hash value of the anchor element.\n */\n getAnchorHash(): string | undefined {\n return this.anchorEl?.hash;\n }\n\n /**\n * Expands the current playground example.\n */\n expand(): void {\n this.exampleEl.open = true;\n }\n\n /**\n * Resizes the input element to accommodate the amount of text present.\n */\n private resize(): void {\n if (this.inputEl?.value) {\n const numLineBreaks = (this.inputEl.value.match(/\\n/g) || []).length;\n // min-height + lines x line-height + padding + border\n this.inputEl.style.height = `${(20 + numLineBreaks * 20 + 12 + 2) / 16}rem`;\n }\n }\n\n /**\n * Handler to override keyboard behavior in the playground's\n * textarea element.\n *\n * Tab key inserts tabs into the example playground instead of\n * switching to the next interactive element.\n * @param e input element keyboard event.\n */\n private onKeydown(e: KeyboardEvent) {\n if (e.key === 'Tab') {\n document.execCommand('insertText', false, '\\t');\n e.preventDefault();\n }\n }\n\n /**\n * Changes the text of the example's input box.\n */\n private setInputText(output: string) {\n if (this.inputEl) {\n this.inputEl.value = output;\n }\n }\n\n /**\n * Changes the text of the example's output box.\n */\n private setOutputText(output: string) {\n if (this.outputEl) {\n this.outputEl.textContent = output;\n }\n if (this.outputContainerEl) {\n this.outputContainerEl.hidden = false;\n }\n }\n\n /**\n * Appends to the text of the example's output box.\n */\n private appendToOutputText(output: string) {\n if (this.outputEl) {\n this.outputEl.textContent += output;\n }\n if (this.outputContainerEl) {\n this.outputContainerEl.hidden = false;\n }\n }\n\n private setOutputHTML(output: string) {\n if (this.outputEl) {\n this.outputEl.innerHTML = output;\n }\n }\n\n /**\n * Sets the error message text and overwrites\n * output box to indicate a failed response.\n */\n private setErrorText(err: string) {\n if (this.errorEl) {\n this.errorEl.textContent = err;\n }\n this.setOutputText('An error has occurred\u2026');\n }\n\n private getCodeWithModFile(): string {\n let codeWithModFile = this.inputEl?.value ?? '';\n const moduleVars = document.querySelector('.js-playgroundVars')?.dataset ?? {};\n if (moduleVars.modulepath !== 'std') {\n codeWithModFile = codeWithModFile.concat(`\n-- go.mod --\nmodule play.ground\n\nrequire ${moduleVars.modulepath} ${moduleVars.version}\n`);\n }\n\n return codeWithModFile;\n }\n\n /**\n * Opens a new window to play.golang.org using the\n * example snippet's code in the playground.\n */\n private handleShareButtonClick() {\n const PLAYGROUND_BASE_URL = 'https://play.golang.org/p/';\n\n this.setOutputText('Waiting for remote server\u2026');\n\n fetch(abs('/play/share'), {\n method: 'POST',\n body: this.getCodeWithModFile(),\n })\n .then(res => res.text())\n .then(shareId => {\n const href = PLAYGROUND_BASE_URL + shareId;\n this.setOutputHTML(`${href}`);\n window.open(href);\n })\n .catch(err => {\n this.setErrorText(err);\n });\n }\n\n /**\n * Runs gofmt on the example snippet in the playground.\n */\n private handleFormatButtonClick() {\n this.setOutputText('Waiting for remote server\u2026');\n const body = new FormData();\n body.append('body', this.inputEl?.value ?? '');\n\n fetch(abs('/play/fmt'), {\n method: 'POST',\n body: body,\n })\n .then(res => res.json())\n .then(({ Body, Error }) => {\n this.setOutputText(Error || 'Done.');\n if (Body) {\n this.setInputText(Body);\n this.resize();\n }\n })\n .catch(err => {\n this.setErrorText(err);\n });\n }\n\n /**\n * Runs the code snippet in the example playground.\n */\n private handleRunButtonClick() {\n this.setOutputText('Waiting for remote server\u2026');\n\n fetch(abs('/play/compile'), {\n method: 'POST',\n body: JSON.stringify({ body: this.getCodeWithModFile(), version: 2 }),\n })\n .then(res => res.json())\n .then(async ({ Events, Errors }) => {\n this.setOutputText(Errors || '');\n for (const e of Events || []) {\n this.appendToOutputText(e.Message);\n await new Promise(resolve => setTimeout(resolve, e.Delay / 1000000));\n }\n })\n .catch(err => {\n this.setErrorText(err);\n });\n }\n}\n\nexport function initPlaygrounds(): void {\n const exampleHashRegex = location.hash.match(/^#(example-.*)$/);\n if (exampleHashRegex) {\n const exampleHashEl = document.getElementById(exampleHashRegex[1]) as HTMLDetailsElement;\n if (exampleHashEl) {\n exampleHashEl.open = true;\n }\n }\n\n // We use a spread operator to convert a nodelist into an array of elements.\n const exampleHrefs = [\n ...document.querySelectorAll(PlayExampleClassName.PLAY_HREF),\n ];\n\n /**\n * Sometimes exampleHrefs and playContainers are in different order, so we\n * find an exampleHref from a common hash.\n * @param playContainer - playground container\n */\n const findExampleHash = (playContainer: PlaygroundExampleController) =>\n exampleHrefs.find(ex => {\n return ex.hash === playContainer.getAnchorHash();\n });\n\n for (const el of document.querySelectorAll(PlayExampleClassName.PLAY_CONTAINER)) {\n // There should be the same amount of hrefs referencing examples as example containers.\n const playContainer = new PlaygroundExampleController(el as HTMLDetailsElement);\n const exampleHref = findExampleHash(playContainer);\n if (exampleHref) {\n exampleHref.addEventListener('click', () => {\n playContainer.expand();\n });\n } else {\n console.warn('example href not found');\n }\n }\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { TreeNavController } from './tree.js';\n\nexport class SelectNavController {\n constructor(private el: Element) {\n this.el.addEventListener('change', e => {\n const target = e.target as HTMLSelectElement;\n let href = target.value;\n if (!target.value.startsWith('/')) {\n href = '/' + href;\n }\n window.location.href = href;\n });\n }\n}\n\nexport function makeSelectNav(tree: TreeNavController): HTMLLabelElement {\n const label = document.createElement('label');\n label.classList.add('go-Label');\n label.setAttribute('aria-label', 'Menu');\n const select = document.createElement('select');\n select.classList.add('go-Select', 'js-selectNav');\n label.appendChild(select);\n const outline = document.createElement('optgroup');\n outline.label = 'Outline';\n select.appendChild(outline);\n const groupMap: Record = {};\n let group: HTMLOptGroupElement;\n for (const t of tree.treeitems) {\n if (Number(t.depth) > 4) continue;\n if (t.groupTreeitem) {\n group = groupMap[t.groupTreeitem.label];\n if (!group) {\n group = groupMap[t.groupTreeitem.label] = document.createElement('optgroup');\n group.label = t.groupTreeitem.label;\n select.appendChild(group);\n }\n } else {\n group = outline;\n }\n const o = document.createElement('option');\n o.label = t.label;\n o.textContent = t.label;\n o.value = (t.el as HTMLAnchorElement).href.replace(window.location.origin, '').replace('/', '');\n group.appendChild(o);\n }\n tree.addObserver(t => {\n const hash = (t.el as HTMLAnchorElement).hash;\n const value = select.querySelector(`[value$=\"${hash}\"]`)?.value;\n if (value) {\n select.value = value;\n }\n }, 50);\n return label;\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * TreeNavController is the navigation tree component of the documentation page.\n * It adds accessiblity attributes to a tree, observes the heading elements\n * focus the topmost link for headings visible on the page, and implements the\n * WAI-ARIA Treeview Design Pattern with full\n * [keyboard support](https://www.w3.org/TR/wai-aria-practices/examples/treeview/treeview-2/treeview-2a.html#kbd_label).\n */\nexport class TreeNavController {\n treeitems: TreeItem[];\n\n /**\n * firstChars is the first character of each treeitem in the same order\n * as this.treeitems. We use this array to set focus by character when\n * navigating the tree with a keyboard.\n */\n private firstChars: string[];\n private firstTreeitem: TreeItem | null;\n private lastTreeitem: TreeItem | null;\n private observerCallbacks: ((t: TreeItem) => void)[];\n\n constructor(private el: HTMLElement) {\n this.treeitems = [];\n this.firstChars = [];\n this.firstTreeitem = null;\n this.lastTreeitem = null;\n this.observerCallbacks = [];\n this.init();\n }\n\n private init(): void {\n this.handleResize();\n window.addEventListener('resize', this.handleResize);\n this.findTreeItems();\n this.updateVisibleTreeitems();\n this.observeTargets();\n if (this.firstTreeitem) {\n this.firstTreeitem.el.tabIndex = 0;\n }\n }\n\n private handleResize = (): void => {\n this.el.style.setProperty('--js-tree-height', '100vh');\n this.el.style.setProperty('--js-tree-height', this.el.clientHeight + 'px');\n };\n\n private observeTargets() {\n this.addObserver(treeitem => {\n this.expandTreeitem(treeitem);\n this.setSelected(treeitem);\n // TODO: Fix scroll issue in https://golang.org/issue/47450.\n // treeitem.el.scrollIntoView({ block: 'nearest' });\n });\n\n const targets = new Map();\n const observer = new IntersectionObserver(\n entries => {\n for (const entry of entries) {\n targets.set(entry.target.id, entry.isIntersecting || entry.intersectionRatio === 1);\n }\n for (const [id, isIntersecting] of targets) {\n if (isIntersecting) {\n const active = this.treeitems.find(t =>\n (t.el as HTMLAnchorElement)?.href.endsWith(`#${id}`)\n );\n if (active) {\n for (const fn of this.observerCallbacks) {\n fn(active);\n }\n }\n break;\n }\n }\n },\n {\n threshold: 1.0,\n rootMargin: '-60px 0px 0px 0px',\n }\n );\n\n for (const href of this.treeitems.map(t => t.el.getAttribute('href'))) {\n if (href) {\n const id = href.replace(window.location.origin, '').replace('/', '').replace('#', '');\n const target = document.getElementById(id);\n if (target && !id.startsWith('example-')) {\n observer.observe(target);\n }\n }\n }\n }\n\n addObserver(fn: (t: TreeItem) => void, delay = 200): void {\n this.observerCallbacks.push(debounce(fn, delay));\n }\n\n setFocusToNextItem(currentItem: TreeItem): void {\n let nextItem = null;\n for (let i = currentItem.index + 1; i < this.treeitems.length; i++) {\n const ti = this.treeitems[i];\n if (ti.isVisible) {\n nextItem = ti;\n break;\n }\n }\n if (nextItem) {\n this.setFocusToItem(nextItem);\n }\n }\n\n setFocusToPreviousItem(currentItem: TreeItem): void {\n let prevItem = null;\n for (let i = currentItem.index - 1; i > -1; i--) {\n const ti = this.treeitems[i];\n if (ti.isVisible) {\n prevItem = ti;\n break;\n }\n }\n if (prevItem) {\n this.setFocusToItem(prevItem);\n }\n }\n\n setFocusToParentItem(currentItem: TreeItem): void {\n if (currentItem.groupTreeitem) {\n this.setFocusToItem(currentItem.groupTreeitem);\n }\n }\n\n setFocusToFirstItem(): void {\n this.firstTreeitem && this.setFocusToItem(this.firstTreeitem);\n }\n\n setFocusToLastItem(): void {\n this.lastTreeitem && this.setFocusToItem(this.lastTreeitem);\n }\n\n setSelected(currentItem: TreeItem): void {\n for (const l1 of this.el.querySelectorAll('[aria-expanded=\"true\"]')) {\n if (l1 === currentItem.el) continue;\n if (!l1.nextElementSibling?.contains(currentItem.el)) {\n l1.setAttribute('aria-expanded', 'false');\n }\n }\n for (const l1 of this.el.querySelectorAll('[aria-selected]')) {\n if (l1 !== currentItem.el) {\n l1.setAttribute('aria-selected', 'false');\n }\n }\n currentItem.el.setAttribute('aria-selected', 'true');\n this.updateVisibleTreeitems();\n this.setFocusToItem(currentItem, false);\n }\n\n expandTreeitem(treeitem: TreeItem): void {\n let currentItem: TreeItem | null = treeitem;\n while (currentItem) {\n if (currentItem.isExpandable) {\n currentItem.el.setAttribute('aria-expanded', 'true');\n }\n currentItem = currentItem.groupTreeitem;\n }\n this.updateVisibleTreeitems();\n }\n\n expandAllSiblingItems(currentItem: TreeItem): void {\n for (const ti of this.treeitems) {\n if (ti.groupTreeitem === currentItem.groupTreeitem && ti.isExpandable) {\n this.expandTreeitem(ti);\n }\n }\n }\n\n collapseTreeitem(currentItem: TreeItem): void {\n let groupTreeitem = null;\n\n if (currentItem.isExpanded()) {\n groupTreeitem = currentItem;\n } else {\n groupTreeitem = currentItem.groupTreeitem;\n }\n\n if (groupTreeitem) {\n groupTreeitem.el.setAttribute('aria-expanded', 'false');\n this.updateVisibleTreeitems();\n this.setFocusToItem(groupTreeitem);\n }\n }\n\n setFocusByFirstCharacter(currentItem: TreeItem, char: string): void {\n let start: number, index: number;\n char = char.toLowerCase();\n\n // Get start index for search based on position of currentItem\n start = currentItem.index + 1;\n if (start === this.treeitems.length) {\n start = 0;\n }\n\n // Check remaining slots in the menu\n index = this.getIndexFirstChars(start, char);\n\n // If not found in remaining slots, check from beginning\n if (index === -1) {\n index = this.getIndexFirstChars(0, char);\n }\n\n // If match was found...\n if (index > -1) {\n this.setFocusToItem(this.treeitems[index]);\n }\n }\n\n private findTreeItems() {\n const findItems = (el: HTMLElement, group: TreeItem | null) => {\n let ti = group;\n let curr = el.firstElementChild as HTMLElement;\n while (curr) {\n if (curr.tagName === 'A' || curr.tagName === 'SPAN') {\n ti = new TreeItem(curr, this, group);\n this.treeitems.push(ti);\n this.firstChars.push(ti.label.substring(0, 1).toLowerCase());\n }\n if (curr.firstElementChild) {\n findItems(curr, ti);\n }\n curr = curr.nextElementSibling as HTMLElement;\n }\n };\n findItems(this.el as HTMLElement, null);\n this.treeitems.map((ti, idx) => (ti.index = idx));\n }\n\n private updateVisibleTreeitems(): void {\n this.firstTreeitem = this.treeitems[0];\n\n for (const ti of this.treeitems) {\n let parent = ti.groupTreeitem;\n ti.isVisible = true;\n while (parent && parent.el !== this.el) {\n if (!parent.isExpanded()) {\n ti.isVisible = false;\n }\n parent = parent.groupTreeitem;\n }\n if (ti.isVisible) {\n this.lastTreeitem = ti;\n }\n }\n }\n\n private setFocusToItem(treeitem: TreeItem, focusEl = true) {\n treeitem.el.tabIndex = 0;\n if (focusEl) {\n treeitem.el.focus();\n }\n for (const ti of this.treeitems) {\n if (ti !== treeitem) {\n ti.el.tabIndex = -1;\n }\n }\n }\n\n private getIndexFirstChars(startIndex: number, char: string): number {\n for (let i = startIndex; i < this.firstChars.length; i++) {\n if (this.treeitems[i].isVisible && char === this.firstChars[i]) {\n return i;\n }\n }\n return -1;\n }\n}\n\nclass TreeItem {\n el: HTMLElement;\n groupTreeitem: TreeItem | null;\n label: string;\n isExpandable: boolean;\n isVisible: boolean;\n depth: number;\n index: number;\n\n private tree: TreeNavController;\n private isInGroup: boolean;\n\n constructor(el: HTMLElement, treeObj: TreeNavController, group: TreeItem | null) {\n el.tabIndex = -1;\n this.el = el;\n this.groupTreeitem = group;\n this.label = el.textContent?.trim() ?? '';\n this.tree = treeObj;\n this.depth = (group?.depth || 0) + 1;\n this.index = 0;\n\n const parent = el.parentElement;\n if (parent?.tagName.toLowerCase() === 'li') {\n parent?.setAttribute('role', 'none');\n }\n el.setAttribute('aria-level', this.depth + '');\n if (el.getAttribute('aria-label')) {\n this.label = el?.getAttribute('aria-label')?.trim() ?? '';\n }\n\n this.isExpandable = false;\n this.isVisible = false;\n this.isInGroup = !!group;\n\n let curr = el.nextElementSibling;\n while (curr) {\n if (curr.tagName.toLowerCase() == 'ul') {\n const groupId = `${group?.label ?? ''} nav group ${this.label}`.replace(/[\\W_]+/g, '_');\n el.setAttribute('aria-owns', groupId);\n el.setAttribute('aria-expanded', 'false');\n curr.setAttribute('role', 'group');\n curr.setAttribute('id', groupId);\n this.isExpandable = true;\n break;\n }\n\n curr = curr.nextElementSibling;\n }\n this.init();\n }\n\n private init() {\n this.el.tabIndex = -1;\n if (!this.el.getAttribute('role')) {\n this.el.setAttribute('role', 'treeitem');\n }\n this.el.addEventListener('keydown', this.handleKeydown.bind(this));\n this.el.addEventListener('click', this.handleClick.bind(this));\n this.el.addEventListener('focus', this.handleFocus.bind(this));\n this.el.addEventListener('blur', this.handleBlur.bind(this));\n }\n\n isExpanded() {\n if (this.isExpandable) {\n return this.el.getAttribute('aria-expanded') === 'true';\n }\n\n return false;\n }\n\n isSelected() {\n return this.el.getAttribute('aria-selected') === 'true';\n }\n\n private handleClick(event: MouseEvent) {\n // only process click events that directly happened on this treeitem\n if (event.target !== this.el && event.target !== this.el.firstElementChild) {\n return;\n }\n if (this.isExpandable) {\n if (this.isExpanded() && this.isSelected()) {\n this.tree.collapseTreeitem(this);\n } else {\n this.tree.expandTreeitem(this);\n }\n event.stopPropagation();\n }\n this.tree.setSelected(this);\n }\n\n private handleFocus() {\n let el = this.el;\n if (this.isExpandable) {\n el = (el.firstElementChild as HTMLElement) ?? el;\n }\n el.classList.add('focus');\n }\n\n private handleBlur() {\n let el = this.el;\n if (this.isExpandable) {\n el = (el.firstElementChild as HTMLElement) ?? el;\n }\n el.classList.remove('focus');\n }\n\n private handleKeydown(event: KeyboardEvent) {\n if (event.altKey || event.ctrlKey || event.metaKey) {\n return;\n }\n\n let captured = false;\n switch (event.key) {\n case ' ':\n case 'Enter':\n if (this.isExpandable) {\n if (this.isExpanded() && this.isSelected()) {\n this.tree.collapseTreeitem(this);\n } else {\n this.tree.expandTreeitem(this);\n }\n captured = true;\n } else {\n event.stopPropagation();\n }\n this.tree.setSelected(this);\n break;\n\n case 'ArrowUp':\n this.tree.setFocusToPreviousItem(this);\n captured = true;\n break;\n\n case 'ArrowDown':\n this.tree.setFocusToNextItem(this);\n captured = true;\n break;\n\n case 'ArrowRight':\n if (this.isExpandable) {\n if (this.isExpanded()) {\n this.tree.setFocusToNextItem(this);\n } else {\n this.tree.expandTreeitem(this);\n }\n }\n captured = true;\n break;\n\n case 'ArrowLeft':\n if (this.isExpandable && this.isExpanded()) {\n this.tree.collapseTreeitem(this);\n captured = true;\n } else {\n if (this.isInGroup) {\n this.tree.setFocusToParentItem(this);\n captured = true;\n }\n }\n break;\n\n case 'Home':\n this.tree.setFocusToFirstItem();\n captured = true;\n break;\n\n case 'End':\n this.tree.setFocusToLastItem();\n captured = true;\n break;\n\n default:\n if (event.key.length === 1 && event.key.match(/\\S/)) {\n if (event.key == '*') {\n this.tree.expandAllSiblingItems(this);\n } else {\n this.tree.setFocusByFirstCharacter(this, event.key);\n }\n captured = true;\n }\n break;\n }\n\n if (captured) {\n event.stopPropagation();\n event.preventDefault();\n }\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction debounce any>(func: T, wait: number) {\n let timeout: ReturnType | null;\n return (...args: Parameters) => {\n const later = () => {\n timeout = null;\n func(...args);\n };\n if (timeout) {\n clearTimeout(timeout);\n }\n timeout = setTimeout(later, wait);\n };\n}\n", "/*!\n * @license\n * Copyright 2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * Controller for a table element with expandable rows. Adds event listeners to\n * a toggle within a table row that controls visiblity of additional related\n * rows in the table.\n *\n * @example\n * ```typescript\n * import {ExpandableRowsTableController} from '/static/js/table';\n *\n * const el = document .querySelector('.js-myTableElement')\n * new ExpandableRowsTableController(el));\n * ```\n */\nexport class ExpandableRowsTableController {\n private rows: HTMLTableRowElement[];\n private toggles: HTMLButtonElement[];\n\n /**\n * Create a table controller.\n * @param table - The table element to which the controller binds.\n */\n constructor(private table: HTMLTableElement, private toggleAll?: HTMLButtonElement | null) {\n this.rows = Array.from(table.querySelectorAll('[data-aria-controls]'));\n this.toggles = Array.from(this.table.querySelectorAll('[aria-expanded]'));\n this.setAttributes();\n this.attachEventListeners();\n this.update();\n }\n\n /**\n * setAttributes sets data-aria-* and data-id attributes to regular\n * html attributes as a workaround for limitations from safehtml.\n */\n private setAttributes() {\n for (const a of ['data-aria-controls', 'data-aria-labelledby', 'data-id']) {\n this.table.querySelectorAll(`[${a}]`).forEach(t => {\n t.setAttribute(a.replace('data-', ''), t.getAttribute(a) ?? '');\n t.removeAttribute(a);\n });\n }\n }\n\n private attachEventListeners() {\n this.rows.forEach(t => {\n t.addEventListener('click', e => {\n this.handleToggleClick(e);\n });\n });\n this.toggleAll?.addEventListener('click', () => {\n this.expandAllItems();\n });\n\n document.addEventListener('keydown', e => {\n if ((e.ctrlKey || e.metaKey) && e.key === 'f') {\n this.expandAllItems();\n }\n });\n }\n\n private handleToggleClick(e: MouseEvent) {\n let target = e.currentTarget as HTMLTableRowElement | null;\n if (!target?.hasAttribute('aria-expanded')) {\n target = this.table.querySelector(\n `button[aria-controls=\"${target?.getAttribute('aria-controls')}\"]`\n );\n }\n const isExpanded = target?.getAttribute('aria-expanded') === 'true';\n target?.setAttribute('aria-expanded', isExpanded ? 'false' : 'true');\n e.stopPropagation();\n this.update();\n }\n\n expandAllItems = (): void => {\n this.toggles.map(t => t.setAttribute('aria-expanded', 'true'));\n this.update();\n };\n\n private collapseAllItems = () => {\n this.toggles.map(t => t.setAttribute('aria-expanded', 'false'));\n this.update();\n };\n\n private update = () => {\n this.updateVisibleItems();\n setTimeout(() => this.updateGlobalToggle());\n };\n\n private updateVisibleItems() {\n this.rows.map(t => {\n const isExpanded = t?.getAttribute('aria-expanded') === 'true';\n const rowIds = t?.getAttribute('aria-controls')?.trimEnd().split(' ');\n rowIds?.map(id => {\n const target = document.getElementById(`${id}`);\n if (isExpanded) {\n target?.classList.add('visible');\n target?.classList.remove('hidden');\n } else {\n target?.classList.add('hidden');\n target?.classList.remove('visible');\n }\n });\n });\n }\n\n private updateGlobalToggle() {\n if (!this.toggleAll) return;\n if (this.rows.some(t => t.hasAttribute('aria-expanded'))) {\n this.toggleAll.style.display = 'block';\n }\n const someCollapsed = this.toggles.some(el => el.getAttribute('aria-expanded') === 'false');\n if (someCollapsed) {\n this.toggleAll.innerText = 'Expand all';\n this.toggleAll.onclick = this.expandAllItems;\n this.toggleAll.setAttribute('aria-label', 'Expand all directories');\n this.toggleAll.setAttribute('aria-live', 'polite');\n } else {\n this.toggleAll.innerText = 'Collapse all';\n this.toggleAll.onclick = this.collapseAllItems;\n this.toggleAll.setAttribute('aria-label', 'Collapse all directories');\n this.toggleAll.setAttribute('aria-live', 'polite');\n }\n }\n}\n", "import { initPlaygrounds } from 'static/shared/playground/playground';\nimport { SelectNavController, makeSelectNav } from 'static/shared/outline/select';\nimport { TreeNavController } from 'static/shared/outline/tree';\nimport { ExpandableRowsTableController } from 'static/shared/table/table';\n\ninitPlaygrounds();\n\nconst directories = document.querySelector('.js-expandableTable');\nif (directories) {\n const table = new ExpandableRowsTableController(\n directories,\n document.querySelector('.js-expandAllDirectories')\n );\n // Expand directories on page load with expand-directories query param.\n if (window.location.search.includes('expand-directories')) {\n table.expandAllItems();\n }\n\n const internalToggle = document.querySelector('.js-showInternalDirectories');\n if (internalToggle) {\n if (document.querySelector('.UnitDirectories-internal')) {\n internalToggle.style.display = 'block';\n internalToggle.setAttribute('aria-label', 'Show Internal Directories');\n internalToggle.setAttribute('aria-describedby', 'showInternal-description');\n }\n internalToggle.addEventListener('click', () => {\n if (directories.classList.contains('UnitDirectories-showInternal')) {\n directories.classList.remove('UnitDirectories-showInternal');\n internalToggle.innerText = 'Show internal';\n internalToggle.setAttribute('aria-label', 'Show Internal Directories');\n internalToggle.setAttribute('aria-live', 'polite');\n internalToggle.setAttribute('aria-describedby', 'showInternal-description');\n } else {\n directories.classList.add('UnitDirectories-showInternal');\n internalToggle.innerText = 'Hide internal';\n internalToggle.setAttribute('aria-label', 'Hide Internal Directories');\n internalToggle.setAttribute('aria-live', 'polite');\n internalToggle.setAttribute('aria-describedby', 'hideInternal-description');\n }\n });\n }\n if (document.querySelector('html[data-local=\"true\"]')) {\n internalToggle?.click();\n }\n}\n\nconst treeEl = document.querySelector('.js-tree');\nif (treeEl) {\n const treeCtrl = new TreeNavController(treeEl);\n const select = makeSelectNav(treeCtrl);\n const mobileNav = document.querySelector('.js-mainNavMobile');\n if (mobileNav && mobileNav.firstElementChild) {\n mobileNav?.replaceChild(select, mobileNav.firstElementChild);\n }\n if (select.firstElementChild) {\n new SelectNavController(select.firstElementChild);\n }\n}\n\n/**\n * Event handlers for expanding and collapsing the readme section.\n */\nconst readme = document.querySelector('.js-readme');\nconst readmeContent = document.querySelector('.js-readmeContent');\nconst readmeOutline = document.querySelector('.js-readmeOutline');\nconst readmeExpand = document.querySelectorAll('.js-readmeExpand');\nconst readmeCollapse = document.querySelector('.js-readmeCollapse');\nconst mobileNavSelect = document.querySelector('.DocNavMobile-select');\nif (readme && readmeContent && readmeOutline && readmeExpand.length && readmeCollapse) {\n if (readme.clientHeight > 320) {\n readme?.classList.remove('UnitReadme--expanded');\n readme?.classList.add('UnitReadme--toggle');\n }\n if (window.location.hash.includes('readme')) {\n expandReadme();\n }\n mobileNavSelect?.addEventListener('change', e => {\n if ((e.target as HTMLSelectElement).value.startsWith('readme-')) {\n expandReadme();\n }\n });\n readmeExpand.forEach(el =>\n el.addEventListener('click', e => {\n e.preventDefault();\n expandReadme();\n readme.scrollIntoView();\n })\n );\n readmeCollapse.addEventListener('click', e => {\n e.preventDefault();\n readme.classList.remove('UnitReadme--expanded');\n if (readmeExpand[1]) {\n readmeExpand[1].scrollIntoView({ block: 'center' });\n }\n });\n readmeContent.addEventListener('keyup', () => {\n expandReadme();\n });\n readmeContent.addEventListener('click', () => {\n expandReadme();\n });\n readmeOutline.addEventListener('click', () => {\n expandReadme();\n });\n document.addEventListener('keydown', e => {\n if ((e.ctrlKey || e.metaKey) && e.key === 'f') {\n expandReadme();\n }\n });\n}\n\n/**\n * expandReadme expands the readme and adds the section-readme hash to the\n * URL so it stays expanded when navigating back from an external link.\n */\nfunction expandReadme() {\n history.replaceState(null, '', `${location.pathname}${location.search}#section-readme`);\n readme?.classList.add('UnitReadme--expanded');\n}\n\n/**\n * Expand details items that are focused. This will expand\n * deprecated symbols when they are navigated to from the index\n * or a direct link.\n */\nfunction openDeprecatedSymbol() {\n if (!location.hash) return;\n const heading = document.getElementById(location.hash.slice(1));\n const grandParent = heading?.parentElement?.parentElement as HTMLDetailsElement | null;\n if (grandParent?.nodeName === 'DETAILS') {\n grandParent.open = true;\n }\n}\nopenDeprecatedSymbol();\nwindow.addEventListener('hashchange', () => openDeprecatedSymbol());\n\n/**\n * Listen for changes in the build context dropdown.\n */\ndocument.querySelectorAll('.js-buildContextSelect').forEach(el => {\n el.addEventListener('change', e => {\n window.location.search = `?GOOS=${(e.target as HTMLSelectElement).value}`;\n });\n});\n"], + "mappings": "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", + "names": ["getBasePath", "_a", "abs", "p", "PlayExampleClassName", "PlaygroundExampleController", "exampleEl", "_a", "_b", "_c", "_d", "e", "el", "numLineBreaks", "output", "err", "codeWithModFile", "moduleVars", "PLAYGROUND_BASE_URL", "abs", "res", "shareId", "href", "body", "Body", "Error", "Events", "Errors", "resolve", "initPlaygrounds", "exampleHashRegex", "exampleHashEl", "exampleHrefs", "findExampleHash", "playContainer", "ex", "exampleHref", "SelectNavController", "el", "e", "target", "href", "makeSelectNav", "tree", "label", "select", "outline", "groupMap", "group", "t", "o", "_a", "hash", "value", "TreeNavController", "el", "treeitem", "targets", "observer", "entries", "entry", "id", "isIntersecting", "active", "t", "_a", "fn", "href", "target", "delay", "debounce", "currentItem", "nextItem", "ti", "prevItem", "l1", "groupTreeitem", "char", "start", "index", "findItems", "group", "curr", "TreeItem", "idx", "parent", "focusEl", "startIndex", "treeObj", "_b", "_c", "_d", "_e", "groupId", "event", "captured", "func", "wait", "timeout", "args", "later", "ExpandableRowsTableController", "table", "toggleAll", "t", "a", "_a", "e", "target", "isExpanded", "rowIds", "id", "el", "initPlaygrounds", "directories", "table", "ExpandableRowsTableController", "internalToggle", "treeEl", "treeCtrl", "TreeNavController", "select", "makeSelectNav", "mobileNav", "SelectNavController", "readme", "readmeContent", "readmeOutline", "readmeExpand", "readmeCollapse", "mobileNavSelect", "expandReadme", "e", "el", "openDeprecatedSymbol", "_a", "heading", "grandParent"] } diff --git a/static/frontend/unit/main/main.tmpl b/static/frontend/unit/main/main.tmpl index 52bf5870e..0f1ee7881 100644 --- a/static/frontend/unit/main/main.tmpl +++ b/static/frontend/unit/main/main.tmpl @@ -13,7 +13,7 @@ {{end}} {{define "main-styles"}} - + {{end}} {{define "main-banner"}} diff --git a/static/frontend/unit/unit.tmpl b/static/frontend/unit/unit.tmpl index c3cd86b27..6a250a3d1 100644 --- a/static/frontend/unit/unit.tmpl +++ b/static/frontend/unit/unit.tmpl @@ -11,7 +11,7 @@ {{define "description"}}{{.MetaDescription}}{{end}} {{define "pre-content"}} - + {{block "main-styles".}}{{end}} {{end}} diff --git a/static/frontend/unit/versions/versions.tmpl b/static/frontend/unit/versions/versions.tmpl index 1a9c7c39e..d97451473 100644 --- a/static/frontend/unit/versions/versions.tmpl +++ b/static/frontend/unit/versions/versions.tmpl @@ -9,7 +9,7 @@ {{end}} {{define "main-styles"}} - + {{end}} {{define "main-header"}} @@ -22,7 +22,7 @@ {{define "main-scripts"}} {{end}} diff --git a/static/frontend/vuln/entry/entry.tmpl b/static/frontend/vuln/entry/entry.tmpl index e97127db1..69b65a0b6 100644 --- a/static/frontend/vuln/entry/entry.tmpl +++ b/static/frontend/vuln/entry/entry.tmpl @@ -5,7 +5,7 @@ --> {{define "main-styles"}} - + {{end}} {{define "main-content"}} diff --git a/static/frontend/vuln/list/list.tmpl b/static/frontend/vuln/list/list.tmpl index 584ec336e..20b93fb9f 100644 --- a/static/frontend/vuln/list/list.tmpl +++ b/static/frontend/vuln/list/list.tmpl @@ -5,7 +5,7 @@ --> {{define "main-styles"}} - + {{end}} {{define "main-content"}} diff --git a/static/frontend/vuln/main/main.tmpl b/static/frontend/vuln/main/main.tmpl index 08de5369e..f60ab5d43 100644 --- a/static/frontend/vuln/main/main.tmpl +++ b/static/frontend/vuln/main/main.tmpl @@ -5,7 +5,7 @@ --> {{define "main-styles"}} - + {{end}} {{define "main-content"}} diff --git a/static/frontend/vuln/vuln.tmpl b/static/frontend/vuln/vuln.tmpl index e4c72dd58..f02868677 100644 --- a/static/frontend/vuln/vuln.tmpl +++ b/static/frontend/vuln/vuln.tmpl @@ -5,7 +5,7 @@ --> {{define "pre-content"}} - + {{block "main-styles" .}}{{end}} {{end}} diff --git a/static/shared/base-path/base-path.ts b/static/shared/base-path/base-path.ts new file mode 100644 index 000000000..8c2bec412 --- /dev/null +++ b/static/shared/base-path/base-path.ts @@ -0,0 +1,34 @@ +/** + * @license + * Copyright 2024 The Go Authors. All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + +/** + * 站点 URL 子路径前缀,从 读出。 + * + * fork 用 -base-path=/gogodocs 启动时,server 端模板把 attribute 写成 + * "/gogodocs";上游公网 pkg.go.dev 默认挂根,attribute 为空字符串。 + * + * 在 之外的脚本初始化阶段调用都安全——documentElement 一定存在。 + */ +export function getBasePath(): string { + return document.documentElement.dataset.basePath ?? ''; +} + +/** + * 给以 / 开头的绝对路径加 BasePath 前缀。 + * + * 用于 fetch URL / innerHTML src / location 比较等任何需要拼站点绝对 path + * 的地方。非绝对路径(不以 / 开头)原样返回——caller 自己保证语义。 + * + * 例: + * abs('/play/share') // → '/gogodocs/play/share' 或 '/play/share' + * abs('/static/foo.svg') // → '/gogodocs/static/foo.svg' 或 '/static/foo.svg' + * abs('relative/x') // → 'relative/x'(不动) + */ +export function abs(p: string): string { + if (!p.startsWith('/')) return p; + return getBasePath() + p; +} diff --git a/static/shared/carousel/carousel.ts b/static/shared/carousel/carousel.ts index d311e4efa..29567f25b 100644 --- a/static/shared/carousel/carousel.ts +++ b/static/shared/carousel/carousel.ts @@ -5,6 +5,8 @@ * license that can be found in the LICENSE file. */ +import { getBasePath } from '../base-path/base-path'; + /** * Carousel Controller adds event listeners, accessibility enhancements, and * control elements to a carousel component. @@ -52,15 +54,17 @@ export class CarouselController { private initArrows() { const arrows = document.createElement('ul'); arrows.classList.add('go-Carousel-arrows'); + // base path 通过 [getBasePath]() 注入;挂根时返空字符串,对上游零差异。 + const bp = getBasePath(); arrows.innerHTML = `
  • `; diff --git a/static/shared/playground/playground.ts b/static/shared/playground/playground.ts index c0dd0cf4b..dfc662fd2 100644 --- a/static/shared/playground/playground.ts +++ b/static/shared/playground/playground.ts @@ -11,6 +11,8 @@ // The CSS is in static/frontend/unit/main/_doc.css +import { abs } from '../base-path/base-path'; + /** * CSS classes used by PlaygroundExampleController */ @@ -232,7 +234,7 @@ require ${moduleVars.modulepath} ${moduleVars.version} this.setOutputText('Waiting for remote server…'); - fetch('/play/share', { + fetch(abs('/play/share'), { method: 'POST', body: this.getCodeWithModFile(), }) @@ -255,7 +257,7 @@ require ${moduleVars.modulepath} ${moduleVars.version} const body = new FormData(); body.append('body', this.inputEl?.value ?? ''); - fetch('/play/fmt', { + fetch(abs('/play/fmt'), { method: 'POST', body: body, }) @@ -278,7 +280,7 @@ require ${moduleVars.modulepath} ${moduleVars.version} private handleRunButtonClick() { this.setOutputText('Waiting for remote server…'); - fetch('/play/compile', { + fetch(abs('/play/compile'), { method: 'POST', body: JSON.stringify({ body: this.getCodeWithModFile(), version: 2 }), }) diff --git a/static/worker/index.tmpl b/static/worker/index.tmpl index 033156871..06b1b6d6c 100644 --- a/static/worker/index.tmpl +++ b/static/worker/index.tmpl @@ -224,5 +224,5 @@ s.src = src; document.head.appendChild(s); } - loadScript("{{abs `/static/worker/worker.js`}}"); + loadScript((document.documentElement.dataset.basePath || "") + "/static/worker/worker.js"); From 79951d467ea9c1fc59af1501274e5acbb5131498 Mon Sep 17 00:00:00 2001 From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com> Date: Thu, 7 May 2026 15:27:55 +0800 Subject: [PATCH 04/26] feat(p2): mermaid lazy render + -show-unexported flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P2 patch 集——给 fork 加三个内网部署常用能力,跟 P1 base path 解耦: 1. Mermaid 渲染(rsc.io/markdown 已经把 fenced code 输出
    ,缺最后一公里):
       - internal/sanitizer/sanitizer.go:放行 /
     的 class 属性,
         用新加的 langClass 正则限定只能是 "language-X" 形态,防任意
         class 注入
       - static/frontend/frontend.tmpl:head 末尾加 
    +    
    +    
         {{template "header" .}}
         {{template "main" .}}
         {{template "footer" .}}
    
    From 6edad4679f0da149927b654928694097e8e33db0 Mon Sep 17 00:00:00 2001
    From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com>
    Date: Thu, 7 May 2026 15:29:55 +0800
    Subject: [PATCH 05/26] feat(p0b): Dockerfile for self-hosted gogodocs
     container
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    
    cmd/pkgsite local mode 跑 docker container 用:
    - multi-stage build:golang:1.24 编 cmd/pkgsite 二进制,stage 2 复用
      golang:1.24 base(不能用 alpine——local mode 走 go/packages.Load,
      container 必须自带 Go toolchain 调 `go list` 解析 module 依赖)
    - ARG GOPROXY 默认 goproxy.cn,build / runtime 都生效——内网 / 国内
      网络环境的常态
    - /repos 约定 mount 目录,docker-compose 把每个 Go module 仓库挂进
      这个路径下;容器主进程是 pkgsite,命令行参数指定 base-path /
      show-unexported / -http 端口 / mount 进来的 module path
    - ENTRYPOINT pkgsite + CMD -h:默认行为是打 usage(防止误启动空容器
      hang);docker-compose 通过 command: 覆盖
    
    下一步:Chat 仓库 docker-compose.dev.yml 加 gogodocs service,
    mount 仓库,base-path=/gogodocs。
    ---
     Dockerfile | 43 +++++++++++++++++++++++++++++++++++++++++++
     1 file changed, 43 insertions(+)
     create mode 100644 Dockerfile
    
    diff --git a/Dockerfile b/Dockerfile
    new file mode 100644
    index 000000000..8a9282a67
    --- /dev/null
    +++ b/Dockerfile
    @@ -0,0 +1,43 @@
    +# GoGoDocs(fork from golang/pkgsite)容器镜像。
    +#
    +# 目标场景:内网部署 cmd/pkgsite local mode 服务多个 mount 进来的 Go module
    +# 仓库(不走上游 cmd/frontend + worker + Postgres 的全套,省一个数量级运维)。
    +#
    +# 不能用 alpine 极简 base——cmd/pkgsite 在 local mode 下通过 go/packages.Load
    +# 调 `go list` 解析模块依赖,container 必须自带 Go toolchain。同 base image
    +# 用 golang:1.24 既 build 又 run,stage 1 复制二进制到 stage 2 也只是少几 MB
    +# 收益不显著。
    +#
    +# GOPROXY 默认走 goproxy.cn 应对国内网络环境;改 build arg / env 可换走
    +# 公司内网 Athens 之类。
    +
    +FROM golang:1.24 AS builder
    +WORKDIR /src
    +
    +# go.mod / go.sum 单独 COPY 让 dep 下载层可缓存(跟随 source 改动)
    +COPY go.mod go.sum ./
    +ARG GOPROXY=https://goproxy.cn,https://proxy.golang.org,direct
    +ENV GOPROXY=${GOPROXY}
    +RUN go mod download
    +
    +COPY . .
    +RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /pkgsite ./cmd/pkgsite
    +
    +FROM golang:1.24
    +LABEL org.opencontainers.image.source="https://github.com/NickWilde18/gogodocs"
    +LABEL org.opencontainers.image.description="Self-hosted godoc browser (pkgsite fork) with base-path support"
    +
    +COPY --from=builder /pkgsite /usr/local/bin/pkgsite
    +
    +# runtime 也需要 GOPROXY 给 go/packages 拉依赖(即便 mount 进来的仓库是 vendor 模式)
    +ARG GOPROXY=https://goproxy.cn,https://proxy.golang.org,direct
    +ENV GOPROXY=${GOPROXY}
    +
    +# /repos 是约定的 mount 目录,docker-compose 把每个 Go module 仓库挂进来
    +WORKDIR /repos
    +
    +EXPOSE 8080
    +
    +# 默认参数让 -h 能直接看 usage;docker-compose 通过 command: 覆盖
    +ENTRYPOINT ["pkgsite"]
    +CMD ["-h"]
    
    From 6b246791eb15e880e384dbadbd55c11017cbe3ff Mon Sep 17 00:00:00 2001
    From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com>
    Date: Thu, 7 May 2026 16:04:12 +0800
    Subject: [PATCH 06/26] =?UTF-8?q?fix(base-path):=20patch=20ConstructUnitUR?=
     =?UTF-8?q?L=20+=20=E5=8D=95=E5=BC=95=E5=8F=B7=20loadScript=20=E6=BC=8F?=
     =?UTF-8?q?=E7=BD=91?=
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    
    P1 smoke test 没覆盖到 module overview / unit detail 页的子目录链接,
    用户在浏览器实测时发现 chat.cuhksz module 页只显示 api / manifest 两个
    目录(internal 被 pkg.go.dev 默认 "Show internal" toggle 隐藏),
    点子目录链接还会跳出 base path 外 404——两个独立 bug:
    
    1. internal/frontend/versions/versions.go: ConstructUnitURL 是 pkgsite
       内部所有 unit/package/module 详情页链接的核心 URL builder(subdir
       列表 / search / breadcrumb 都走它),它生成的绝对路径必须带 base
       path 前缀。加包级 var BasePath,cmd/pkgsite/main.go 跟 godoc.IncludeUnexported
       同模式设置——pkgsite 单进程一种行为,不必加新参数让上百 caller 都改
    
    2. unit.tmpl / unit/main/main.tmpl / search/search.tmpl 三处
       `loadScript('/static/...')` 用**单引号**,P1 sed 只匹配双引号
       loadScript("...") 漏过去。改成 `(document.documentElement.dataset.basePath
       || "") + "/static/..."` runtime 拼前缀(同 frontend.tmpl 内 inline
       script 模式)。修不修这个的影响:unit.js / main.js bundle 加载 404 →
       main.ts 里 "data-local=true 时 auto-click Show internal toggle 让
       internal 目录默认展开" 这段没跑 → 用户感知 internal 目录"不存在"
    ---
     cmd/pkgsite/main.go                    |  8 ++++++++
     internal/frontend/versions/versions.go | 18 +++++++++++++++---
     static/frontend/search/search.tmpl     |  2 +-
     static/frontend/unit/main/main.tmpl    |  2 +-
     static/frontend/unit/unit.tmpl         |  2 +-
     5 files changed, 26 insertions(+), 6 deletions(-)
    
    diff --git a/cmd/pkgsite/main.go b/cmd/pkgsite/main.go
    index f5139d827..32668a654 100644
    --- a/cmd/pkgsite/main.go
    +++ b/cmd/pkgsite/main.go
    @@ -62,6 +62,7 @@ import (
     
     	"golang.org/x/pkgsite/cmd/internal/pkgsite"
     	"golang.org/x/pkgsite/internal/browser"
    +	"golang.org/x/pkgsite/internal/frontend/versions"
     	"golang.org/x/pkgsite/internal/godoc"
     	"golang.org/x/pkgsite/internal/log"
     	"golang.org/x/pkgsite/internal/middleware/timeout"
    @@ -113,6 +114,13 @@ func main() {
     	// 单进程 pkgsite 一种行为,没必要加到 ServerConfig 里再透传一层。
     	godoc.IncludeUnexported = *showUnexported
     
    +	// versions 包级 BasePath——pkgsite 内部 ConstructUnitURL 是所有 unit /
    +	// package / module 详情页链接的核心 URL builder(subdir 列表、search
    +	// 结果、breadcrumb 都走它),它生成的绝对 URL 必须带 base path 前缀,
    +	// 否则点子目录会跳出 base path 外 404。同 godoc.IncludeUnexported 模式
    +	// 用包级 var 而非加 ServerConfig 字段层层透传。
    +	versions.BasePath = *basePath
    +
     	serverCfg.UseLocalStdlib = true
     	serverCfg.GoRepoPath = *goRepoPath
     	serverCfg.Paths = collectPaths(flag.Args())
    diff --git a/internal/frontend/versions/versions.go b/internal/frontend/versions/versions.go
    index 2cc83214c..43056e859 100644
    --- a/internal/frontend/versions/versions.go
    +++ b/internal/frontend/versions/versions.go
    @@ -430,16 +430,28 @@ func absoluteTime(date time.Time) string {
     	return date.In(time.UTC).Format("Jan _2, 2006")
     }
     
    +// BasePath 是 fork 加的包级 URL 前缀(如 "/gogodocs"),由 cmd/pkgsite/main.go
    +// 在 -base-path flag 处设置。空 = 默认挂根,跟上游零差异。
    +//
    +// [ConstructUnitURL] 是 pkgsite 内部所有 unit / package / module 详情页链接
    +// 的核心 URL builder——unit overview 上的子目录列表 / search 结果 / breadcrumb
    +// 各处都通过它生成绝对路径。fork 把整站挂在子路径下时这些路径都得带前缀,
    +// 否则点子目录链接会跳出 base path 外 404。
    +//
    +// 用包级 var 跟 [godoc.IncludeUnexported] 同模式——pkgsite 单进程一种行为,
    +// 不必 ConstructUnitURL 加新参数让上百个 caller 都改一遍。
    +var BasePath string
    +
     // ConstructUnitURL returns a URL path that refers to the given unit at the requested
     // version. If requestedVersion is "latest", then the resulting path has no
     // version; otherwise, it has requestedVersion.
     func ConstructUnitURL(fullPath, modulePath, requestedVersion string) string {
     	if requestedVersion == version.Latest {
    -		return "/" + fullPath
    +		return BasePath + "/" + fullPath
     	}
     	v := LinkVersion(modulePath, requestedVersion, requestedVersion)
     	if fullPath == modulePath || modulePath == stdlib.ModulePath {
    -		return fmt.Sprintf("/%s@%s", fullPath, v)
    +		return fmt.Sprintf("%s/%s@%s", BasePath, fullPath, v)
     	}
    -	return fmt.Sprintf("/%s@%s/%s", modulePath, v, strings.TrimPrefix(fullPath, modulePath+"/"))
    +	return fmt.Sprintf("%s/%s@%s/%s", BasePath, modulePath, v, strings.TrimPrefix(fullPath, modulePath+"/"))
     }
    diff --git a/static/frontend/search/search.tmpl b/static/frontend/search/search.tmpl
    index 7dc1692b6..8eff9a2c2 100644
    --- a/static/frontend/search/search.tmpl
    +++ b/static/frontend/search/search.tmpl
    @@ -18,7 +18,7 @@
     
     {{define "post-content"}}
       
     {{end}}
     
    diff --git a/static/frontend/unit/main/main.tmpl b/static/frontend/unit/main/main.tmpl
    index 0f1ee7881..d0e2ddeff 100644
    --- a/static/frontend/unit/main/main.tmpl
    +++ b/static/frontend/unit/main/main.tmpl
    @@ -82,6 +82,6 @@
       
       
       
     {{end}}
    diff --git a/static/frontend/unit/unit.tmpl b/static/frontend/unit/unit.tmpl
    index 6a250a3d1..8366485a6 100644
    --- a/static/frontend/unit/unit.tmpl
    +++ b/static/frontend/unit/unit.tmpl
    @@ -43,6 +43,6 @@
     {{define "post-content"}}
       {{block "main-scripts" .}}{{end}}
       
     {{end}}
    \ No newline at end of file
    
    From bd298b58a062e6ce248cc4f34e0773a7dbda3af2 Mon Sep 17 00:00:00 2001
    From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com>
    Date: Thu, 7 May 2026 16:16:55 +0800
    Subject: [PATCH 07/26] feat(dochtml): minimal markdown ext for doc comments
     (inline code / bold / mermaid)
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    
    go/doc/comment parser 不识别 markdown 反引号 / **bold** / fenced code,
    但用户写 doc.go 时这些 markdown 风格已经形成习惯(毕竟 .md 写得多)。
    渲染产物里 raw \` 和 ** 字面输出体验差,mermaid 图块也没办法变 fenced
    code class 让客户端 mermaid.js 渲。
    
    新增 internal/godoc/dochtml/internal/render/markdown_ext.go:
    post-process 渲染好的 HTML 加三条 inline 转换:
    
    - \`text\` → text
    - **text** → text
    - 
    \`\`\`mermaid\\n...\`\`\`
    ...
    让 frontend.tmpl 里的 mermaid lazy-load CDN 脚本能识别并渲 实现:walkOutsidePre 把 HTML 切成
     内 / 外两类 segment,
     内
    原样保留——godoc 把 markdown 4 空格缩进 code block 渲染成 
    ,
    里面字符按字面(如 Go struct tag literal `json:"..."` 保持不动)。
    
    设计选型:post-process HTML 比 fork go/doc/comment parser 干净——
    保留 godoc 原生 [Symbol] cross-reference / heading-id / 链接提取等
    功能不变,只在表层加几条 inline 转换。代价是 regex on HTML 不优雅,
    但 godoc 渲染的 HTML 结构有限,
     边界清晰,实测没遇到 corner case。
    
    linkify.go formatDocHTML 在最后调一次 applyDocMarkdownExt 包装。
    
    Smoke test (/gogodocs/chat.cuhksz/internal/service/im/weixin):
    - : 40 个,含 wx:monitor:lease:{upn} 等用户报告的
      反引号字面量
    - : 38 个,含 不同 binding 等 bold
    - language-mermaid: 2 个——doc.go 里两张 mermaid 时序图都被识别,
      浏览器侧 mermaid.js 渲成 SVG
    ---
     .../godoc/dochtml/internal/render/linkify.go  |  4 +-
     .../dochtml/internal/render/markdown_ext.go   | 87 +++++++++++++++++++
     2 files changed, 90 insertions(+), 1 deletion(-)
     create mode 100644 internal/godoc/dochtml/internal/render/markdown_ext.go
    
    diff --git a/internal/godoc/dochtml/internal/render/linkify.go b/internal/godoc/dochtml/internal/render/linkify.go
    index 2cbcf41a8..86496ee2d 100644
    --- a/internal/godoc/dochtml/internal/render/linkify.go
    +++ b/internal/godoc/dochtml/internal/render/linkify.go
    @@ -129,7 +129,9 @@ func (r *Renderer) formatDocHTML(text string, decl ast.Decl, extractLinks bool)
     	if len(hscope.headings) > 0 {
     		h = safe.HTMLConcat(ExecuteToHTML(tocTemplate, hscope.headings), h)
     	}
    -	return h
    +	// fork:在 godoc 渲染产物上加最小 markdown 风格 inline 修饰
    +	// (`code` / **bold** / mermaid fence)。详见 [applyDocMarkdownExt]。
    +	return applyDocMarkdownExt(h)
     }
     
     // removeLinks removes the "Links" section from doc.
    diff --git a/internal/godoc/dochtml/internal/render/markdown_ext.go b/internal/godoc/dochtml/internal/render/markdown_ext.go
    new file mode 100644
    index 000000000..d45b73bc9
    --- /dev/null
    +++ b/internal/godoc/dochtml/internal/render/markdown_ext.go
    @@ -0,0 +1,87 @@
    +// Copyright 2026 Yechi Yang. All rights reserved.
    +// Use of this source code is governed by a BSD-style
    +// license that can be found in the LICENSE file of the upstream pkgsite.
    +
    +package render
    +
    +import (
    +	"regexp"
    +	"strings"
    +
    +	"github.com/google/safehtml"
    +	"github.com/google/safehtml/uncheckedconversions"
    +)
    +
    +// applyDocMarkdownExt 给 godoc 渲染产物加最小的 markdown 风格 inline 修饰。
    +//
    +// 背景:godoc 注释走 go/doc/comment parser,不识别 markdown 的反引号
    +// inline code / **bold** / fenced code blocks——这些会被原样输出成字面文本。
    +// 但用户写 doc.go 时这些 markdown 风格已经形成习惯(毕竟 .md 写得多),
    +// 渲染产物里看到 `` `xxx` `` 和 `**bold**` raw text 体验差。
    +//
    +// 这层 post-process 把渲染好的 HTML 二次扫描加修饰:
    +//   - `` `text` `` → text
    +//   - **text** → text
    +//   - 
    ```mermaid\n...```
    ...
    +// 让客户端 mermaid.js 能识别并渲染时序图(frontend.tmpl 里有 lazy-load +// mermaid CDN 的逻辑,scan 选择器是 code.language-mermaid) +// +// 顺序:先处理 mermaid(在
     内),再 walkOutsidePre 在 
     块外面
    +// 做 inline 修饰——避免改到 preformatted code 内的字面字符(用户在缩进
    +// 4 空格 code block 里写的反引号是真的反引号字面,不是 inline code)。
    +//
    +// 设计选型:post-process HTML 比 fork go/doc/comment parser 干净——保留
    +// godoc 原生的 [Symbol] cross-reference / heading-id / 链接提取等功能不变,
    +// 只在表层加几条 inline 转换。代价是 regex on HTML 不优雅,但 godoc 渲染
    +// 的 HTML 结构有限制,
     边界清晰,实测没遇到 corner case。
    +func applyDocMarkdownExt(h safehtml.HTML) safehtml.HTML {
    +	s := h.String()
    +	s = mermaidPreRe.ReplaceAllString(s, `
    $1
    `) + s = walkOutsidePre(s, func(seg string) string { + // 顺序:bold 先于 backtick——`**code**` 先匹配粗体再匹配反引号; + // 否则反引号匹配会把 ** 包进 code 里破坏。 + seg = boldRe.ReplaceAllString(seg, "$1") + seg = backtickRe.ReplaceAllString(seg, "$1") + return seg + }) + return uncheckedconversions.HTMLFromStringKnownToSatisfyTypeContract(s) +} + +var ( + // 反引号 inline code——不跨行(避免 multi-paragraph 误捕、避免与 ``` 围栏 + // 冲突),允许内容含其他字符但不能再含反引号。 + backtickRe = regexp.MustCompile("`([^`\n]+)`") + + // **bold**——不跨行;内容不能含 *,避免吃掉相邻 ** 标记。 + boldRe = regexp.MustCompile(`\*\*([^*\n]+)\*\*`) + + //
     内首行 ```mermaid 起头的围栏块——godoc 把 fenced 当 raw text 渲染
    +	// 进 
    ,我们补上 。`(?s)` 让 . 匹配换行。
    +	// 容许尾部 ``` 缺失或带空白(godoc preformatted 块包内时尾部 ``` 可能被
    +	// 当下一个段落渲染分裂——简化匹配到第一个 
    闭合)。 + mermaidPreRe = regexp.MustCompile("(?s)
    (?:\\s*\n)*```mermaid\\s*\n(.+?)(?:\\s*```\\s*)?
    ") +) + +// walkOutsidePre 把 html 切成
    ...
    内 / 外两类 segment,对外面 segment +// 调用 fn 转换,
     内原样保留——godoc 把 markdown 4 空格缩进 code block
    +// 渲染成 
    ,这里面字符按字面,不该被 inline 替换误改。
    +func walkOutsidePre(html string, fn func(string) string) string {
    +	var out strings.Builder
    +	for {
    +		idx := strings.Index(html, "
    ")
    +		if idx < 0 {
    +			out.WriteString(fn(html))
    +			return out.String()
    +		}
    +		out.WriteString(fn(html[:idx]))
    +		end := strings.Index(html[idx:], "
    ") + if end < 0 { + // 残缺 HTML——直接保留剩余部分不再处理 + out.WriteString(html[idx:]) + return out.String() + } + end += len("
    ") + out.WriteString(html[idx : idx+end]) + html = html[idx+end:] + } +} From e7cf7de9908e97aefc9c231854af64b8aee27f00 Mon Sep 17 00:00:00 2001 From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com> Date: Thu, 7 May 2026 16:44:48 +0800 Subject: [PATCH 08/26] =?UTF-8?q?fix:=20-show-unexported=20=E7=9C=9F?= =?UTF-8?q?=E7=94=9F=E6=95=88=20+=20view=20source=20=E5=B8=A6=20base-path?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 两个 bug 一起修: 1. -show-unexported 实际没效果——godoc.IncludeUnexported=true 只让 doc.NewFromFiles 用 doc.AllDecls,但 fetchdatasource 在喂给它之前先 调 godoc.Package.AddFile(removeNodes=true) 在 AST 阶段就剥光未导出 FuncDecl,doc.NewFromFiles 看到的 AST 已经被剔光 → AllDecls 救不回。 修:internal/fetch/load.go 在 godoc.IncludeUnexported=true 时也把 removeNodes 设 false,跟 stdlib builtin 特例同模式保留全部 nodes。 smoke:weixin 包页面 dispatchMessage / authedClient / buildHelpText 等私有函数现作为 data-kind="function" declaration 渲染,不再只是 raw text 引用。 2. view source 链接没带 /gogodocs/ 前缀——cmd/pkgsite local mode 下 source.Info 模板生成 "/files/{path}" 让 file mux serve 源码(不 拷代码到 pod,直接从 mount 进来的目录读),但这个 URL 没经过 ConstructUnitURL 这条已 patched 的路径。修:godoc 加 BasePath 包级 var(同 IncludeUnexported 模式),renderOptions 里加 localPrefix 闭包识别 "/files/" 起头的 local URL 前置 BasePath。GitHub 远程 URL (http:// 起头)不受影响,跟上游一致。 smoke:view source URL 现是 "/gogodocs/files/.../poll_loop.go" 且 HTTP 200 能正常 serve 源码内容。 --- cmd/pkgsite/main.go | 3 +++ internal/fetch/load.go | 7 +++++++ internal/godoc/render.go | 22 ++++++++++++++++++++-- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/cmd/pkgsite/main.go b/cmd/pkgsite/main.go index 32668a654..d24544312 100644 --- a/cmd/pkgsite/main.go +++ b/cmd/pkgsite/main.go @@ -113,6 +113,9 @@ func main() { // 全局开关——godoc.DocPackage 读包级 var 决定是否传 doc.AllDecls。 // 单进程 pkgsite 一种行为,没必要加到 ServerConfig 里再透传一层。 godoc.IncludeUnexported = *showUnexported + // view source / file link 在 local mode 走 "/files/..." 路径——base path + // 挂子路径时 godoc.renderOptions 的 localPrefix 闭包读这个 var 加前缀。 + godoc.BasePath = *basePath // versions 包级 BasePath——pkgsite 内部 ConstructUnitURL 是所有 unit / // package / module 详情页链接的核心 URL builder(subdir 列表、search diff --git a/internal/fetch/load.go b/internal/fetch/load.go index e8f03316c..5fede8539 100644 --- a/internal/fetch/load.go +++ b/internal/fetch/load.go @@ -303,6 +303,13 @@ func loadPackageForBuildContext(ctx context.Context, files map[string][]byte, in if modulePath == stdlib.ModulePath && innerPath == "builtin" { removeNodes = false } + // fork:-show-unexported flag 设全局 godoc.IncludeUnexported=true + // 时,AST 阶段也保留未导出 FuncDecl(默认 [removeUnusedASTNodes] + // 整个剥掉),后续 [DocPackage] 才有 nodes 可读 + doc.AllDecls 才生效。 + // 否则 doc.AllDecls 在 AST 已经被剔光的输入上无效。 + if godoc.IncludeUnexported { + removeNodes = false + } docPkg.AddFile(pf, removeNodes) } diff --git a/internal/godoc/render.go b/internal/godoc/render.go index 7355e1251..c509ab3aa 100644 --- a/internal/godoc/render.go +++ b/internal/godoc/render.go @@ -54,6 +54,15 @@ var MaxDocumentationHTML = 40 * megabyte // 透传到 [DocPackage] 调用方(caller 链很深)。 var IncludeUnexported bool +// BasePath 跟 [IncludeUnexported] 同模式的包级开关——cmd/pkgsite/main.go 设。 +// +// 用途:cmd/pkgsite local mode 下,[Renderer] 生成 view source / file link 时 +// source.Info 走的是 `/files/{path}` 模板,不带 fork 站点子路径。挂 +// -base-path=/gogodocs 时这些链接得变成 `/gogodocs/files/{path}` 才能正确路由 +// 到 file mux。在 [renderOptions] 的 fileLinkFunc / sourceLinkFunc 里识别 local +// 模式(URL 以 "/files/" 起头)并 prefix。 +var BasePath string + // DocInfo returns information extracted from the package's documentation. // This destroys p's AST; do not call any methods of p after it returns. func (p *Package) DocInfo(ctx context.Context, innerPath string, sourceInfo *source.Info, modInfo *ModuleInfo) ( @@ -166,6 +175,15 @@ func (p *Package) DocPackage(innerPath string, modInfo *ModuleInfo) (_ *doc.Pack // renderOptions returns a RenderOptions for p. func (p *Package) renderOptions(innerPath string, sourceInfo *source.Info, modInfo *ModuleInfo, nameToVersion map[string]string, bc internal.BuildContext) dochtml.RenderOptions { + // localPrefix wrap:cmd/pkgsite local mode 下 source.Info 模板生成 "/files/..." + // 路径,挂 -base-path 时要 prefix 才能路由到 file mux;远程 GitHub URL + // (以 https:// 起头)不动。空 BasePath 时也 no-op 跟上游一致。 + localPrefix := func(u string) string { + if BasePath != "" && strings.HasPrefix(u, "/files/") { + return BasePath + u + } + return u + } sourceLinkFunc := func(n ast.Node) string { if sourceInfo == nil { return "" @@ -174,13 +192,13 @@ func (p *Package) renderOptions(innerPath string, sourceInfo *source.Info, modIn if p.Line == 0 { // invalid Position return "" } - return sourceInfo.LineURL(path.Join(innerPath, p.Filename), p.Line) + return localPrefix(sourceInfo.LineURL(path.Join(innerPath, p.Filename), p.Line)) } fileLinkFunc := func(filename string) string { if sourceInfo == nil { return "" } - return sourceInfo.FileURL(path.Join(innerPath, filename)) + return localPrefix(sourceInfo.FileURL(path.Join(innerPath, filename))) } return dochtml.RenderOptions{ From b1200cf709a08673568eb95067946c7cde87e108 Mon Sep 17 00:00:00 2001 From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com> Date: Thu, 7 May 2026 17:07:24 +0800 Subject: [PATCH 09/26] =?UTF-8?q?fix:=20bold=20=E8=B7=A8=E8=A1=8C=20+=20?= =?UTF-8?q?=E5=8A=A0=20unexported=20toggle=20button?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 两个 follow-up bug fix + UX 增强: 1. bold 跨行漏识别:boldRe 旧 pattern \`\`\\*\\*([^*\\n]+)\\*\\*\`\` 排除 \\n 导致跨行 bold 不匹配——godoc 段落渲染保留段内 \\n,bold 写多行 是常见用法(如 weixin/poll_loop.go 里 \`\`**Redis cursor(wx:get_updates_buf:{upn}) 只在本批所有 handler 跑完后才推进**\`\` 这种)。改成 \`\`\\*\\*([^*<]{1,500})\\*\\*\`\` 容许 \\n + 用 [^<] 防跨 HTML 段落标签 + 500 字符上限双重防御 贪心跨段。smoke:\`\`Redis cursor(...)只在本批所有 handler 跑完后才推进\`\` 现正确包标签 2. unexported 符号 toggle(用户报告"私有太多反而拖慢阅读,但有时 要看"):static/frontend/unit/main/main.ts 末尾加 IIFE,按 id 首字母大小写自动给私有 declaration / index 链接的 wrapper 加 .Documentation-unexported class,默认 CSS 隐藏;Index header 旁注入 Show / Hide unexported toggle button,状态写 localStorage 跨页保留。method id "Type.method" 取最后段判私有。 const / var 不动(数量少且 godoc 整组渲染共享 declaration block, 单独 hide 个别名字会破坏视觉)。 --- .../dochtml/internal/render/markdown_ext.go | 7 +- static/frontend/unit/main/main.js | 4 +- static/frontend/unit/main/main.js.map | 6 +- static/frontend/unit/main/main.ts | 76 +++++++++++++++++++ 4 files changed, 86 insertions(+), 7 deletions(-) diff --git a/internal/godoc/dochtml/internal/render/markdown_ext.go b/internal/godoc/dochtml/internal/render/markdown_ext.go index d45b73bc9..a6ba25a5a 100644 --- a/internal/godoc/dochtml/internal/render/markdown_ext.go +++ b/internal/godoc/dochtml/internal/render/markdown_ext.go @@ -52,8 +52,11 @@ var ( // 冲突),允许内容含其他字符但不能再含反引号。 backtickRe = regexp.MustCompile("`([^`\n]+)`") - // **bold**——不跨行;内容不能含 *,避免吃掉相邻 ** 标记。 - boldRe = regexp.MustCompile(`\*\*([^*\n]+)\*\*`) + // **bold**——容许段内跨真实换行(godoc 段落渲染保留 \n,bold 写多行 + // 是常见用法),但用 `[^<]` 排除 HTML 标签边界 + 500 字符上限防止贪心 + // 跨段落(godoc 段间用

    ,含 < 的字符 class 已经能阻止跨段; + // 上限是双重防御,避免 corner case 一路匹到下个 ** 把整页吞了)。 + boldRe = regexp.MustCompile(`\*\*([^*<]{1,500})\*\*`) //

     内首行 ```mermaid 起头的围栏块——godoc 把 fenced 当 raw text 渲染
     	// 进 
    ,我们补上 。`(?s)` 让 . 匹配换行。
    diff --git a/static/frontend/unit/main/main.js b/static/frontend/unit/main/main.js
    index 29f7e656a..67dc3cbe3 100644
    --- a/static/frontend/unit/main/main.js
    +++ b/static/frontend/unit/main/main.js
    @@ -1,9 +1,9 @@
    -function H(){var l;return(l=document.documentElement.dataset.basePath)!=null?l:""}function p(l){return l.startsWith("/")?H()+l:l}var d={PLAY_HREF:".js-exampleHref",PLAY_CONTAINER:".js-exampleContainer",EXAMPLE_INPUT:".Documentation-exampleCode",EXAMPLE_OUTPUT:".Documentation-exampleOutput",EXAMPLE_OUTPUT_CONTAINER:".js-exampleOutputContainer",EXAMPLE_ERROR:".Documentation-exampleError",PLAY_BUTTON:".Documentation-examplePlayButton",SHARE_BUTTON:".Documentation-exampleShareButton",FORMAT_BUTTON:".Documentation-exampleFormatButton",RUN_BUTTON:".Documentation-exampleRunButton"},v=class{constructor(e){this.exampleEl=e;var t,i,s,r;this.exampleEl=e,this.anchorEl=e.querySelector("a"),this.errorEl=e.querySelector(d.EXAMPLE_ERROR),this.playButtonEl=e.querySelector(d.PLAY_BUTTON),this.shareButtonEl=e.querySelector(d.SHARE_BUTTON),this.formatButtonEl=e.querySelector(d.FORMAT_BUTTON),this.runButtonEl=e.querySelector(d.RUN_BUTTON),this.inputEl=this.makeTextArea(e.querySelector(d.EXAMPLE_INPUT)),this.outputEl=e.querySelector(d.EXAMPLE_OUTPUT),this.outputContainerEl=e.querySelector(d.EXAMPLE_OUTPUT_CONTAINER),(t=this.playButtonEl)==null||t.addEventListener("click",()=>this.handleShareButtonClick()),(i=this.shareButtonEl)==null||i.addEventListener("click",()=>this.handleShareButtonClick()),(s=this.formatButtonEl)==null||s.addEventListener("click",()=>this.handleFormatButtonClick()),(r=this.runButtonEl)==null||r.addEventListener("click",()=>this.handleRunButtonClick()),this.inputEl&&(this.resize(),this.inputEl.addEventListener("keyup",()=>this.resize()),this.inputEl.addEventListener("keydown",n=>this.onKeydown(n)))}makeTextArea(e){var i,s;let t=document.createElement("textarea");return t.classList.add("Documentation-exampleCode","code"),t.spellcheck=!1,t.value=(i=e==null?void 0:e.textContent)!=null?i:"",(s=e==null?void 0:e.parentElement)==null||s.replaceChild(t,e),t}getAnchorHash(){var e;return(e=this.anchorEl)==null?void 0:e.hash}expand(){this.exampleEl.open=!0}resize(){var e;if((e=this.inputEl)!=null&&e.value){let t=(this.inputEl.value.match(/\n/g)||[]).length;this.inputEl.style.height=`${(20+t*20+12+2)/16}rem`}}onKeydown(e){e.key==="Tab"&&(document.execCommand("insertText",!1,"	"),e.preventDefault())}setInputText(e){this.inputEl&&(this.inputEl.value=e)}setOutputText(e){this.outputEl&&(this.outputEl.textContent=e),this.outputContainerEl&&(this.outputContainerEl.hidden=!1)}appendToOutputText(e){this.outputEl&&(this.outputEl.textContent+=e),this.outputContainerEl&&(this.outputContainerEl.hidden=!1)}setOutputHTML(e){this.outputEl&&(this.outputEl.innerHTML=e)}setErrorText(e){this.errorEl&&(this.errorEl.textContent=e),this.setOutputText("An error has occurred\u2026")}getCodeWithModFile(){var i,s,r,n;let e=(s=(i=this.inputEl)==null?void 0:i.value)!=null?s:"",t=(n=(r=document.querySelector(".js-playgroundVars"))==null?void 0:r.dataset)!=null?n:{};return t.modulepath!=="std"&&(e=e.concat(`
    +function H(){var l;return(l=document.documentElement.dataset.basePath)!=null?l:""}function p(l){return l.startsWith("/")?H()+l:l}var u={PLAY_HREF:".js-exampleHref",PLAY_CONTAINER:".js-exampleContainer",EXAMPLE_INPUT:".Documentation-exampleCode",EXAMPLE_OUTPUT:".Documentation-exampleOutput",EXAMPLE_OUTPUT_CONTAINER:".js-exampleOutputContainer",EXAMPLE_ERROR:".Documentation-exampleError",PLAY_BUTTON:".Documentation-examplePlayButton",SHARE_BUTTON:".Documentation-exampleShareButton",FORMAT_BUTTON:".Documentation-exampleFormatButton",RUN_BUTTON:".Documentation-exampleRunButton"},x=class{constructor(e){this.exampleEl=e;var t,i,s,n;this.exampleEl=e,this.anchorEl=e.querySelector("a"),this.errorEl=e.querySelector(u.EXAMPLE_ERROR),this.playButtonEl=e.querySelector(u.PLAY_BUTTON),this.shareButtonEl=e.querySelector(u.SHARE_BUTTON),this.formatButtonEl=e.querySelector(u.FORMAT_BUTTON),this.runButtonEl=e.querySelector(u.RUN_BUTTON),this.inputEl=this.makeTextArea(e.querySelector(u.EXAMPLE_INPUT)),this.outputEl=e.querySelector(u.EXAMPLE_OUTPUT),this.outputContainerEl=e.querySelector(u.EXAMPLE_OUTPUT_CONTAINER),(t=this.playButtonEl)==null||t.addEventListener("click",()=>this.handleShareButtonClick()),(i=this.shareButtonEl)==null||i.addEventListener("click",()=>this.handleShareButtonClick()),(s=this.formatButtonEl)==null||s.addEventListener("click",()=>this.handleFormatButtonClick()),(n=this.runButtonEl)==null||n.addEventListener("click",()=>this.handleRunButtonClick()),this.inputEl&&(this.resize(),this.inputEl.addEventListener("keyup",()=>this.resize()),this.inputEl.addEventListener("keydown",r=>this.onKeydown(r)))}makeTextArea(e){var i,s;let t=document.createElement("textarea");return t.classList.add("Documentation-exampleCode","code"),t.spellcheck=!1,t.value=(i=e==null?void 0:e.textContent)!=null?i:"",(s=e==null?void 0:e.parentElement)==null||s.replaceChild(t,e),t}getAnchorHash(){var e;return(e=this.anchorEl)==null?void 0:e.hash}expand(){this.exampleEl.open=!0}resize(){var e;if((e=this.inputEl)!=null&&e.value){let t=(this.inputEl.value.match(/\n/g)||[]).length;this.inputEl.style.height=`${(20+t*20+12+2)/16}rem`}}onKeydown(e){e.key==="Tab"&&(document.execCommand("insertText",!1,"	"),e.preventDefault())}setInputText(e){this.inputEl&&(this.inputEl.value=e)}setOutputText(e){this.outputEl&&(this.outputEl.textContent=e),this.outputContainerEl&&(this.outputContainerEl.hidden=!1)}appendToOutputText(e){this.outputEl&&(this.outputEl.textContent+=e),this.outputContainerEl&&(this.outputContainerEl.hidden=!1)}setOutputHTML(e){this.outputEl&&(this.outputEl.innerHTML=e)}setErrorText(e){this.errorEl&&(this.errorEl.textContent=e),this.setOutputText("An error has occurred\u2026")}getCodeWithModFile(){var i,s,n,r;let e=(s=(i=this.inputEl)==null?void 0:i.value)!=null?s:"",t=(r=(n=document.querySelector(".js-playgroundVars"))==null?void 0:n.dataset)!=null?r:{};return t.modulepath!=="std"&&(e=e.concat(`
     -- go.mod --
     module play.ground
     
     require ${t.modulepath} ${t.version}
    -`)),e}handleShareButtonClick(){let e="https://play.golang.org/p/";this.setOutputText("Waiting for remote server\u2026"),fetch(p("/play/share"),{method:"POST",body:this.getCodeWithModFile()}).then(t=>t.text()).then(t=>{let i=e+t;this.setOutputHTML(`${i}`),window.open(i)}).catch(t=>{this.setErrorText(t)})}handleFormatButtonClick(){var t,i;this.setOutputText("Waiting for remote server\u2026");let e=new FormData;e.append("body",(i=(t=this.inputEl)==null?void 0:t.value)!=null?i:""),fetch(p("/play/fmt"),{method:"POST",body:e}).then(s=>s.json()).then(({Body:s,Error:r})=>{this.setOutputText(r||"Done."),s&&(this.setInputText(s),this.resize())}).catch(s=>{this.setErrorText(s)})}handleRunButtonClick(){this.setOutputText("Waiting for remote server\u2026"),fetch(p("/play/compile"),{method:"POST",body:JSON.stringify({body:this.getCodeWithModFile(),version:2})}).then(e=>e.json()).then(async({Events:e,Errors:t})=>{this.setOutputText(t||"");for(let i of e||[])this.appendToOutputText(i.Message),await new Promise(s=>setTimeout(s,i.Delay/1e6))}).catch(e=>{this.setErrorText(e)})}};function I(){let l=location.hash.match(/^#(example-.*)$/);if(l){let i=document.getElementById(l[1]);i&&(i.open=!0)}let e=[...document.querySelectorAll(d.PLAY_HREF)],t=i=>e.find(s=>s.hash===i.getAnchorHash());for(let i of document.querySelectorAll(d.PLAY_CONTAINER)){let s=new v(i),r=t(s);r?r.addEventListener("click",()=>{s.expand()}):console.warn("example href not found")}}var f=class{constructor(e){this.el=e;this.el.addEventListener("change",t=>{let i=t.target,s=i.value;i.value.startsWith("/")||(s="/"+s),window.location.href=s})}};function C(l){let e=document.createElement("label");e.classList.add("go-Label"),e.setAttribute("aria-label","Menu");let t=document.createElement("select");t.classList.add("go-Select","js-selectNav"),e.appendChild(t);let i=document.createElement("optgroup");i.label="Outline",t.appendChild(i);let s={},r;for(let n of l.treeitems){if(Number(n.depth)>4)continue;n.groupTreeitem?(r=s[n.groupTreeitem.label],r||(r=s[n.groupTreeitem.label]=document.createElement("optgroup"),r.label=n.groupTreeitem.label,t.appendChild(r))):r=i;let a=document.createElement("option");a.label=n.label,a.textContent=n.label,a.value=n.el.href.replace(window.location.origin,"").replace("/",""),r.appendChild(a)}return l.addObserver(n=>{var c;let a=n.el.hash,u=(c=t.querySelector(`[value$="${a}"]`))==null?void 0:c.value;u&&(t.value=u)},50),e}var E=class{constructor(e){this.el=e;this.handleResize=()=>{this.el.style.setProperty("--js-tree-height","100vh"),this.el.style.setProperty("--js-tree-height",this.el.clientHeight+"px")};this.treeitems=[],this.firstChars=[],this.firstTreeitem=null,this.lastTreeitem=null,this.observerCallbacks=[],this.init()}init(){this.handleResize(),window.addEventListener("resize",this.handleResize),this.findTreeItems(),this.updateVisibleTreeitems(),this.observeTargets(),this.firstTreeitem&&(this.firstTreeitem.el.tabIndex=0)}observeTargets(){this.addObserver(i=>{this.expandTreeitem(i),this.setSelected(i)});let e=new Map,t=new IntersectionObserver(i=>{for(let s of i)e.set(s.target.id,s.isIntersecting||s.intersectionRatio===1);for(let[s,r]of e)if(r){let n=this.treeitems.find(a=>{var u;return(u=a.el)==null?void 0:u.href.endsWith(`#${s}`)});if(n)for(let a of this.observerCallbacks)a(n);break}},{threshold:1,rootMargin:"-60px 0px 0px 0px"});for(let i of this.treeitems.map(s=>s.el.getAttribute("href")))if(i){let s=i.replace(window.location.origin,"").replace("/","").replace("#",""),r=document.getElementById(s);r&&!s.startsWith("example-")&&t.observe(r)}}addObserver(e,t=200){this.observerCallbacks.push(O(e,t))}setFocusToNextItem(e){let t=null;for(let i=e.index+1;i-1;i--){let s=this.treeitems[i];if(s.isVisible){t=s;break}}t&&this.setFocusToItem(t)}setFocusToParentItem(e){e.groupTreeitem&&this.setFocusToItem(e.groupTreeitem)}setFocusToFirstItem(){this.firstTreeitem&&this.setFocusToItem(this.firstTreeitem)}setFocusToLastItem(){this.lastTreeitem&&this.setFocusToItem(this.lastTreeitem)}setSelected(e){var t;for(let i of this.el.querySelectorAll('[aria-expanded="true"]'))i!==e.el&&((t=i.nextElementSibling)!=null&&t.contains(e.el)||i.setAttribute("aria-expanded","false"));for(let i of this.el.querySelectorAll("[aria-selected]"))i!==e.el&&i.setAttribute("aria-selected","false");e.el.setAttribute("aria-selected","true"),this.updateVisibleTreeitems(),this.setFocusToItem(e,!1)}expandTreeitem(e){let t=e;for(;t;)t.isExpandable&&t.el.setAttribute("aria-expanded","true"),t=t.groupTreeitem;this.updateVisibleTreeitems()}expandAllSiblingItems(e){for(let t of this.treeitems)t.groupTreeitem===e.groupTreeitem&&t.isExpandable&&this.expandTreeitem(t)}collapseTreeitem(e){let t=null;e.isExpanded()?t=e:t=e.groupTreeitem,t&&(t.el.setAttribute("aria-expanded","false"),this.updateVisibleTreeitems(),this.setFocusToItem(t))}setFocusByFirstCharacter(e,t){let i,s;t=t.toLowerCase(),i=e.index+1,i===this.treeitems.length&&(i=0),s=this.getIndexFirstChars(i,t),s===-1&&(s=this.getIndexFirstChars(0,t)),s>-1&&this.setFocusToItem(this.treeitems[s])}findTreeItems(){let e=(t,i)=>{let s=i,r=t.firstElementChild;for(;r;)(r.tagName==="A"||r.tagName==="SPAN")&&(s=new x(r,this,i),this.treeitems.push(s),this.firstChars.push(s.label.substring(0,1).toLowerCase())),r.firstElementChild&&e(r,s),r=r.nextElementSibling};e(this.el,null),this.treeitems.map((t,i)=>t.index=i)}updateVisibleTreeitems(){this.firstTreeitem=this.treeitems[0];for(let e of this.treeitems){let t=e.groupTreeitem;for(e.isVisible=!0;t&&t.el!==this.el;)t.isExpanded()||(e.isVisible=!1),t=t.groupTreeitem;e.isVisible&&(this.lastTreeitem=e)}}setFocusToItem(e,t=!0){e.el.tabIndex=0,t&&e.el.focus();for(let i of this.treeitems)i!==e&&(i.el.tabIndex=-1)}getIndexFirstChars(e,t){for(let i=e;i{let s=()=>{t=null,l(...i)};t&&clearTimeout(t),t=setTimeout(s,e)}}var T=class{constructor(e,t){this.table=e;this.toggleAll=t;this.expandAllItems=()=>{this.toggles.map(e=>e.setAttribute("aria-expanded","true")),this.update()};this.collapseAllItems=()=>{this.toggles.map(e=>e.setAttribute("aria-expanded","false")),this.update()};this.update=()=>{this.updateVisibleItems(),setTimeout(()=>this.updateGlobalToggle())};this.rows=Array.from(e.querySelectorAll("[data-aria-controls]")),this.toggles=Array.from(this.table.querySelectorAll("[aria-expanded]")),this.setAttributes(),this.attachEventListeners(),this.update()}setAttributes(){for(let e of["data-aria-controls","data-aria-labelledby","data-id"])this.table.querySelectorAll(`[${e}]`).forEach(t=>{var i;t.setAttribute(e.replace("data-",""),(i=t.getAttribute(e))!=null?i:""),t.removeAttribute(e)})}attachEventListeners(){var e;this.rows.forEach(t=>{t.addEventListener("click",i=>{this.handleToggleClick(i)})}),(e=this.toggleAll)==null||e.addEventListener("click",()=>{this.expandAllItems()}),document.addEventListener("keydown",t=>{(t.ctrlKey||t.metaKey)&&t.key==="f"&&this.expandAllItems()})}handleToggleClick(e){let t=e.currentTarget;t!=null&&t.hasAttribute("aria-expanded")||(t=this.table.querySelector(`button[aria-controls="${t==null?void 0:t.getAttribute("aria-controls")}"]`));let i=(t==null?void 0:t.getAttribute("aria-expanded"))==="true";t==null||t.setAttribute("aria-expanded",i?"false":"true"),e.stopPropagation(),this.update()}updateVisibleItems(){this.rows.map(e=>{var s;let t=(e==null?void 0:e.getAttribute("aria-expanded"))==="true",i=(s=e==null?void 0:e.getAttribute("aria-controls"))==null?void 0:s.trimEnd().split(" ");i==null||i.map(r=>{let n=document.getElementById(`${r}`);t?(n==null||n.classList.add("visible"),n==null||n.classList.remove("hidden")):(n==null||n.classList.add("hidden"),n==null||n.classList.remove("visible"))})})}updateGlobalToggle(){if(!this.toggleAll)return;this.rows.some(t=>t.hasAttribute("aria-expanded"))&&(this.toggleAll.style.display="block"),this.toggles.some(t=>t.getAttribute("aria-expanded")==="false")?(this.toggleAll.innerText="Expand all",this.toggleAll.onclick=this.expandAllItems,this.toggleAll.setAttribute("aria-label","Expand all directories"),this.toggleAll.setAttribute("aria-live","polite")):(this.toggleAll.innerText="Collapse all",this.toggleAll.onclick=this.collapseAllItems,this.toggleAll.setAttribute("aria-label","Collapse all directories"),this.toggleAll.setAttribute("aria-live","polite"))}};I();var m=document.querySelector(".js-expandableTable");if(m){let l=new T(m,document.querySelector(".js-expandAllDirectories"));window.location.search.includes("expand-directories")&&l.expandAllItems();let e=document.querySelector(".js-showInternalDirectories");e&&(document.querySelector(".UnitDirectories-internal")&&(e.style.display="block",e.setAttribute("aria-label","Show Internal Directories"),e.setAttribute("aria-describedby","showInternal-description")),e.addEventListener("click",()=>{m.classList.contains("UnitDirectories-showInternal")?(m.classList.remove("UnitDirectories-showInternal"),e.innerText="Show internal",e.setAttribute("aria-label","Show Internal Directories"),e.setAttribute("aria-live","polite"),e.setAttribute("aria-describedby","showInternal-description")):(m.classList.add("UnitDirectories-showInternal"),e.innerText="Hide internal",e.setAttribute("aria-label","Hide Internal Directories"),e.setAttribute("aria-live","polite"),e.setAttribute("aria-describedby","hideInternal-description"))})),document.querySelector('html[data-local="true"]')&&(e==null||e.click())}var S=document.querySelector(".js-tree");if(S){let l=new E(S),e=C(l),t=document.querySelector(".js-mainNavMobile");t&&t.firstElementChild&&(t==null||t.replaceChild(e,t.firstElementChild)),e.firstElementChild&&new f(e.firstElementChild)}var o=document.querySelector(".js-readme"),g=document.querySelector(".js-readmeContent"),w=document.querySelector(".js-readmeOutline"),b=document.querySelectorAll(".js-readmeExpand"),k=document.querySelector(".js-readmeCollapse"),A=document.querySelector(".DocNavMobile-select");o&&g&&w&&b.length&&k&&(o.clientHeight>320&&(o==null||o.classList.remove("UnitReadme--expanded"),o==null||o.classList.add("UnitReadme--toggle")),window.location.hash.includes("readme")&&h(),A==null||A.addEventListener("change",l=>{l.target.value.startsWith("readme-")&&h()}),b.forEach(l=>l.addEventListener("click",e=>{e.preventDefault(),h(),o.scrollIntoView()})),k.addEventListener("click",l=>{l.preventDefault(),o.classList.remove("UnitReadme--expanded"),b[1]&&b[1].scrollIntoView({block:"center"})}),g.addEventListener("keyup",()=>{h()}),g.addEventListener("click",()=>{h()}),w.addEventListener("click",()=>{h()}),document.addEventListener("keydown",l=>{(l.ctrlKey||l.metaKey)&&l.key==="f"&&h()}));function h(){history.replaceState(null,"",`${location.pathname}${location.search}#section-readme`),o==null||o.classList.add("UnitReadme--expanded")}function M(){var t;if(!location.hash)return;let l=document.getElementById(location.hash.slice(1)),e=(t=l==null?void 0:l.parentElement)==null?void 0:t.parentElement;(e==null?void 0:e.nodeName)==="DETAILS"&&(e.open=!0)}M();window.addEventListener("hashchange",()=>M());document.querySelectorAll(".js-buildContextSelect").forEach(l=>{l.addEventListener("change",e=>{window.location.search=`?GOOS=${e.target.value}`})});
    +`)),e}handleShareButtonClick(){let e="https://play.golang.org/p/";this.setOutputText("Waiting for remote server\u2026"),fetch(p("/play/share"),{method:"POST",body:this.getCodeWithModFile()}).then(t=>t.text()).then(t=>{let i=e+t;this.setOutputHTML(`${i}`),window.open(i)}).catch(t=>{this.setErrorText(t)})}handleFormatButtonClick(){var t,i;this.setOutputText("Waiting for remote server\u2026");let e=new FormData;e.append("body",(i=(t=this.inputEl)==null?void 0:t.value)!=null?i:""),fetch(p("/play/fmt"),{method:"POST",body:e}).then(s=>s.json()).then(({Body:s,Error:n})=>{this.setOutputText(n||"Done."),s&&(this.setInputText(s),this.resize())}).catch(s=>{this.setErrorText(s)})}handleRunButtonClick(){this.setOutputText("Waiting for remote server\u2026"),fetch(p("/play/compile"),{method:"POST",body:JSON.stringify({body:this.getCodeWithModFile(),version:2})}).then(e=>e.json()).then(async({Events:e,Errors:t})=>{this.setOutputText(t||"");for(let i of e||[])this.appendToOutputText(i.Message),await new Promise(s=>setTimeout(s,i.Delay/1e6))}).catch(e=>{this.setErrorText(e)})}};function C(){let l=location.hash.match(/^#(example-.*)$/);if(l){let i=document.getElementById(l[1]);i&&(i.open=!0)}let e=[...document.querySelectorAll(u.PLAY_HREF)],t=i=>e.find(s=>s.hash===i.getAnchorHash());for(let i of document.querySelectorAll(u.PLAY_CONTAINER)){let s=new x(i),n=t(s);n?n.addEventListener("click",()=>{s.expand()}):console.warn("example href not found")}}var f=class{constructor(e){this.el=e;this.el.addEventListener("change",t=>{let i=t.target,s=i.value;i.value.startsWith("/")||(s="/"+s),window.location.href=s})}};function I(l){let e=document.createElement("label");e.classList.add("go-Label"),e.setAttribute("aria-label","Menu");let t=document.createElement("select");t.classList.add("go-Select","js-selectNav"),e.appendChild(t);let i=document.createElement("optgroup");i.label="Outline",t.appendChild(i);let s={},n;for(let r of l.treeitems){if(Number(r.depth)>4)continue;r.groupTreeitem?(n=s[r.groupTreeitem.label],n||(n=s[r.groupTreeitem.label]=document.createElement("optgroup"),n.label=r.groupTreeitem.label,t.appendChild(n))):n=i;let a=document.createElement("option");a.label=r.label,a.textContent=r.label,a.value=r.el.href.replace(window.location.origin,"").replace("/",""),n.appendChild(a)}return l.addObserver(r=>{var h;let a=r.el.hash,d=(h=t.querySelector(`[value$="${a}"]`))==null?void 0:h.value;d&&(t.value=d)},50),e}var E=class{constructor(e){this.el=e;this.handleResize=()=>{this.el.style.setProperty("--js-tree-height","100vh"),this.el.style.setProperty("--js-tree-height",this.el.clientHeight+"px")};this.treeitems=[],this.firstChars=[],this.firstTreeitem=null,this.lastTreeitem=null,this.observerCallbacks=[],this.init()}init(){this.handleResize(),window.addEventListener("resize",this.handleResize),this.findTreeItems(),this.updateVisibleTreeitems(),this.observeTargets(),this.firstTreeitem&&(this.firstTreeitem.el.tabIndex=0)}observeTargets(){this.addObserver(i=>{this.expandTreeitem(i),this.setSelected(i)});let e=new Map,t=new IntersectionObserver(i=>{for(let s of i)e.set(s.target.id,s.isIntersecting||s.intersectionRatio===1);for(let[s,n]of e)if(n){let r=this.treeitems.find(a=>{var d;return(d=a.el)==null?void 0:d.href.endsWith(`#${s}`)});if(r)for(let a of this.observerCallbacks)a(r);break}},{threshold:1,rootMargin:"-60px 0px 0px 0px"});for(let i of this.treeitems.map(s=>s.el.getAttribute("href")))if(i){let s=i.replace(window.location.origin,"").replace("/","").replace("#",""),n=document.getElementById(s);n&&!s.startsWith("example-")&&t.observe(n)}}addObserver(e,t=200){this.observerCallbacks.push(O(e,t))}setFocusToNextItem(e){let t=null;for(let i=e.index+1;i-1;i--){let s=this.treeitems[i];if(s.isVisible){t=s;break}}t&&this.setFocusToItem(t)}setFocusToParentItem(e){e.groupTreeitem&&this.setFocusToItem(e.groupTreeitem)}setFocusToFirstItem(){this.firstTreeitem&&this.setFocusToItem(this.firstTreeitem)}setFocusToLastItem(){this.lastTreeitem&&this.setFocusToItem(this.lastTreeitem)}setSelected(e){var t;for(let i of this.el.querySelectorAll('[aria-expanded="true"]'))i!==e.el&&((t=i.nextElementSibling)!=null&&t.contains(e.el)||i.setAttribute("aria-expanded","false"));for(let i of this.el.querySelectorAll("[aria-selected]"))i!==e.el&&i.setAttribute("aria-selected","false");e.el.setAttribute("aria-selected","true"),this.updateVisibleTreeitems(),this.setFocusToItem(e,!1)}expandTreeitem(e){let t=e;for(;t;)t.isExpandable&&t.el.setAttribute("aria-expanded","true"),t=t.groupTreeitem;this.updateVisibleTreeitems()}expandAllSiblingItems(e){for(let t of this.treeitems)t.groupTreeitem===e.groupTreeitem&&t.isExpandable&&this.expandTreeitem(t)}collapseTreeitem(e){let t=null;e.isExpanded()?t=e:t=e.groupTreeitem,t&&(t.el.setAttribute("aria-expanded","false"),this.updateVisibleTreeitems(),this.setFocusToItem(t))}setFocusByFirstCharacter(e,t){let i,s;t=t.toLowerCase(),i=e.index+1,i===this.treeitems.length&&(i=0),s=this.getIndexFirstChars(i,t),s===-1&&(s=this.getIndexFirstChars(0,t)),s>-1&&this.setFocusToItem(this.treeitems[s])}findTreeItems(){let e=(t,i)=>{let s=i,n=t.firstElementChild;for(;n;)(n.tagName==="A"||n.tagName==="SPAN")&&(s=new g(n,this,i),this.treeitems.push(s),this.firstChars.push(s.label.substring(0,1).toLowerCase())),n.firstElementChild&&e(n,s),n=n.nextElementSibling};e(this.el,null),this.treeitems.map((t,i)=>t.index=i)}updateVisibleTreeitems(){this.firstTreeitem=this.treeitems[0];for(let e of this.treeitems){let t=e.groupTreeitem;for(e.isVisible=!0;t&&t.el!==this.el;)t.isExpanded()||(e.isVisible=!1),t=t.groupTreeitem;e.isVisible&&(this.lastTreeitem=e)}}setFocusToItem(e,t=!0){e.el.tabIndex=0,t&&e.el.focus();for(let i of this.treeitems)i!==e&&(i.el.tabIndex=-1)}getIndexFirstChars(e,t){for(let i=e;i{let s=()=>{t=null,l(...i)};t&&clearTimeout(t),t=setTimeout(s,e)}}var T=class{constructor(e,t){this.table=e;this.toggleAll=t;this.expandAllItems=()=>{this.toggles.map(e=>e.setAttribute("aria-expanded","true")),this.update()};this.collapseAllItems=()=>{this.toggles.map(e=>e.setAttribute("aria-expanded","false")),this.update()};this.update=()=>{this.updateVisibleItems(),setTimeout(()=>this.updateGlobalToggle())};this.rows=Array.from(e.querySelectorAll("[data-aria-controls]")),this.toggles=Array.from(this.table.querySelectorAll("[aria-expanded]")),this.setAttributes(),this.attachEventListeners(),this.update()}setAttributes(){for(let e of["data-aria-controls","data-aria-labelledby","data-id"])this.table.querySelectorAll(`[${e}]`).forEach(t=>{var i;t.setAttribute(e.replace("data-",""),(i=t.getAttribute(e))!=null?i:""),t.removeAttribute(e)})}attachEventListeners(){var e;this.rows.forEach(t=>{t.addEventListener("click",i=>{this.handleToggleClick(i)})}),(e=this.toggleAll)==null||e.addEventListener("click",()=>{this.expandAllItems()}),document.addEventListener("keydown",t=>{(t.ctrlKey||t.metaKey)&&t.key==="f"&&this.expandAllItems()})}handleToggleClick(e){let t=e.currentTarget;t!=null&&t.hasAttribute("aria-expanded")||(t=this.table.querySelector(`button[aria-controls="${t==null?void 0:t.getAttribute("aria-controls")}"]`));let i=(t==null?void 0:t.getAttribute("aria-expanded"))==="true";t==null||t.setAttribute("aria-expanded",i?"false":"true"),e.stopPropagation(),this.update()}updateVisibleItems(){this.rows.map(e=>{var s;let t=(e==null?void 0:e.getAttribute("aria-expanded"))==="true",i=(s=e==null?void 0:e.getAttribute("aria-controls"))==null?void 0:s.trimEnd().split(" ");i==null||i.map(n=>{let r=document.getElementById(`${n}`);t?(r==null||r.classList.add("visible"),r==null||r.classList.remove("hidden")):(r==null||r.classList.add("hidden"),r==null||r.classList.remove("visible"))})})}updateGlobalToggle(){if(!this.toggleAll)return;this.rows.some(t=>t.hasAttribute("aria-expanded"))&&(this.toggleAll.style.display="block"),this.toggles.some(t=>t.getAttribute("aria-expanded")==="false")?(this.toggleAll.innerText="Expand all",this.toggleAll.onclick=this.expandAllItems,this.toggleAll.setAttribute("aria-label","Expand all directories"),this.toggleAll.setAttribute("aria-live","polite")):(this.toggleAll.innerText="Collapse all",this.toggleAll.onclick=this.collapseAllItems,this.toggleAll.setAttribute("aria-label","Collapse all directories"),this.toggleAll.setAttribute("aria-live","polite"))}};C();var m=document.querySelector(".js-expandableTable");if(m){let l=new T(m,document.querySelector(".js-expandAllDirectories"));window.location.search.includes("expand-directories")&&l.expandAllItems();let e=document.querySelector(".js-showInternalDirectories");e&&(document.querySelector(".UnitDirectories-internal")&&(e.style.display="block",e.setAttribute("aria-label","Show Internal Directories"),e.setAttribute("aria-describedby","showInternal-description")),e.addEventListener("click",()=>{m.classList.contains("UnitDirectories-showInternal")?(m.classList.remove("UnitDirectories-showInternal"),e.innerText="Show internal",e.setAttribute("aria-label","Show Internal Directories"),e.setAttribute("aria-live","polite"),e.setAttribute("aria-describedby","showInternal-description")):(m.classList.add("UnitDirectories-showInternal"),e.innerText="Hide internal",e.setAttribute("aria-label","Hide Internal Directories"),e.setAttribute("aria-live","polite"),e.setAttribute("aria-describedby","hideInternal-description"))})),document.querySelector('html[data-local="true"]')&&(e==null||e.click())}var S=document.querySelector(".js-tree");if(S){let l=new E(S),e=I(l),t=document.querySelector(".js-mainNavMobile");t&&t.firstElementChild&&(t==null||t.replaceChild(e,t.firstElementChild)),e.firstElementChild&&new f(e.firstElementChild)}var o=document.querySelector(".js-readme"),v=document.querySelector(".js-readmeContent"),w=document.querySelector(".js-readmeOutline"),b=document.querySelectorAll(".js-readmeExpand"),M=document.querySelector(".js-readmeCollapse"),y=document.querySelector(".DocNavMobile-select");o&&v&&w&&b.length&&M&&(o.clientHeight>320&&(o==null||o.classList.remove("UnitReadme--expanded"),o==null||o.classList.add("UnitReadme--toggle")),window.location.hash.includes("readme")&&c(),y==null||y.addEventListener("change",l=>{l.target.value.startsWith("readme-")&&c()}),b.forEach(l=>l.addEventListener("click",e=>{e.preventDefault(),c(),o.scrollIntoView()})),M.addEventListener("click",l=>{l.preventDefault(),o.classList.remove("UnitReadme--expanded"),b[1]&&b[1].scrollIntoView({block:"center"})}),v.addEventListener("keyup",()=>{c()}),v.addEventListener("click",()=>{c()}),w.addEventListener("click",()=>{c()}),document.addEventListener("keydown",l=>{(l.ctrlKey||l.metaKey)&&l.key==="f"&&c()}));function c(){history.replaceState(null,"",`${location.pathname}${location.search}#section-readme`),o==null||o.classList.add("UnitReadme--expanded")}function k(){var t;if(!location.hash)return;let l=document.getElementById(location.hash.slice(1)),e=(t=l==null?void 0:l.parentElement)==null?void 0:t.parentElement;(e==null?void 0:e.nodeName)==="DETAILS"&&(e.open=!0)}k();window.addEventListener("hashchange",()=>k());document.querySelectorAll(".js-buildContextSelect").forEach(l=>{l.addEventListener("change",e=>{window.location.search=`?GOOS=${e.target.value}`})});(()=>{if(!document.querySelector("h4[data-kind]"))return;let l=r=>{var d;let a=(d=r.split(".").pop())!=null?d:r;return/^[a-z]/.test(a)};document.querySelectorAll("h4[data-kind][id]").forEach(r=>{if(!l(r.id))return;let a=r.closest(".Documentation-function, .Documentation-type, .Documentation-typeFunc, .Documentation-typeMethod");a==null||a.classList.add("Documentation-unexported")}),document.querySelectorAll('.Documentation-indexFunction a[href^="#"], .Documentation-indexType a[href^="#"], .Documentation-indexTypeFunctions a[href^="#"], .Documentation-indexTypeMethods a[href^="#"]').forEach(r=>{var a;l(r.getAttribute("href").slice(1))&&((a=r.closest("li"))==null||a.classList.add("Documentation-unexported"))});let e=document.createElement("style");e.textContent="body:not(.show-unexported) .Documentation-unexported{display:none}",document.head.appendChild(e);let t=document.querySelector("#pkg-index");if(!t)return;let i=document.createElement("button");i.type="button",i.className="go-Button go-Button--inline",i.style.marginLeft="0.75rem",i.style.fontSize="0.875rem",i.style.verticalAlign="middle";let s="gogodocs:showUnexported",n=r=>{document.body.classList.toggle("show-unexported",r),i.textContent=r?"Hide unexported":"Show unexported";try{localStorage.setItem(s,r?"1":"0")}catch{}};n(localStorage.getItem(s)==="1"),i.addEventListener("click",()=>n(!document.body.classList.contains("show-unexported"))),t.appendChild(i)})();
     /**
      * @license
      * Copyright 2024 The Go Authors. All rights reserved.
    diff --git a/static/frontend/unit/main/main.js.map b/static/frontend/unit/main/main.js.map
    index f599413c2..154bedf34 100644
    --- a/static/frontend/unit/main/main.js.map
    +++ b/static/frontend/unit/main/main.js.map
    @@ -1,7 +1,7 @@
     {
       "version": 3,
       "sources": ["../../../shared/base-path/base-path.ts", "../../../shared/playground/playground.ts", "../../../shared/outline/select.ts", "../../../shared/outline/tree.ts", "../../../shared/table/table.ts", "main.ts"],
    -  "sourcesContent": ["/**\n * @license\n * Copyright 2024 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * \u7AD9\u70B9 URL \u5B50\u8DEF\u5F84\u524D\u7F00\uFF0C\u4ECE  \u8BFB\u51FA\u3002\n *\n * fork \u7528 -base-path=/gogodocs \u542F\u52A8\u65F6\uFF0Cserver \u7AEF\u6A21\u677F\u628A attribute \u5199\u6210\n * \"/gogodocs\"\uFF1B\u4E0A\u6E38\u516C\u7F51 pkg.go.dev \u9ED8\u8BA4\u6302\u6839\uFF0Cattribute \u4E3A\u7A7A\u5B57\u7B26\u4E32\u3002\n *\n * \u5728  \u4E4B\u5916\u7684\u811A\u672C\u521D\u59CB\u5316\u9636\u6BB5\u8C03\u7528\u90FD\u5B89\u5168\u2014\u2014documentElement \u4E00\u5B9A\u5B58\u5728\u3002\n */\nexport function getBasePath(): string {\n  return document.documentElement.dataset.basePath ?? '';\n}\n\n/**\n * \u7ED9\u4EE5 / \u5F00\u5934\u7684\u7EDD\u5BF9\u8DEF\u5F84\u52A0 BasePath \u524D\u7F00\u3002\n *\n * \u7528\u4E8E fetch URL / innerHTML src / location \u6BD4\u8F83\u7B49\u4EFB\u4F55\u9700\u8981\u62FC\u7AD9\u70B9\u7EDD\u5BF9 path\n * \u7684\u5730\u65B9\u3002\u975E\u7EDD\u5BF9\u8DEF\u5F84\uFF08\u4E0D\u4EE5 / \u5F00\u5934\uFF09\u539F\u6837\u8FD4\u56DE\u2014\u2014caller \u81EA\u5DF1\u4FDD\u8BC1\u8BED\u4E49\u3002\n *\n * \u4F8B\uFF1A\n *   abs('/play/share')       // \u2192 '/gogodocs/play/share' \u6216 '/play/share'\n *   abs('/static/foo.svg')   // \u2192 '/gogodocs/static/foo.svg' \u6216 '/static/foo.svg'\n *   abs('relative/x')        // \u2192 'relative/x'\uFF08\u4E0D\u52A8\uFF09\n */\nexport function abs(p: string): string {\n  if (!p.startsWith('/')) return p;\n  return getBasePath() + p;\n}\n", "/*!\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n// This file implements the playground implementation of the documentation\n// page. The playground involves a \"play\" button that allows you to open up\n// a new link to play.golang.org using the example code.\n\n// The CSS is in static/frontend/unit/main/_doc.css\n\nimport { abs } from '../base-path/base-path';\n\n/**\n * CSS classes used by PlaygroundExampleController\n */\nconst PlayExampleClassName = {\n  PLAY_HREF: '.js-exampleHref',\n  PLAY_CONTAINER: '.js-exampleContainer',\n  EXAMPLE_INPUT: '.Documentation-exampleCode',\n  EXAMPLE_OUTPUT: '.Documentation-exampleOutput',\n  EXAMPLE_OUTPUT_CONTAINER: '.js-exampleOutputContainer',\n  EXAMPLE_ERROR: '.Documentation-exampleError',\n  PLAY_BUTTON: '.Documentation-examplePlayButton',\n  SHARE_BUTTON: '.Documentation-exampleShareButton',\n  FORMAT_BUTTON: '.Documentation-exampleFormatButton',\n  RUN_BUTTON: '.Documentation-exampleRunButton',\n};\n\n/**\n * This controller enables playground examples to expand their dropdown or\n * generate shareable Go Playground URLs.\n */\nexport class PlaygroundExampleController {\n  /**\n   * The anchor tag used to identify the container with an example href.\n   * There is only one in an example container div.\n   */\n  private readonly anchorEl: HTMLAnchorElement | null;\n\n  /**\n   * The error element\n   */\n  private readonly errorEl: Element | null;\n\n  /**\n   * Buttons that redirect to an example's playground, this element\n   * only exists in executable examples.\n   */\n  private readonly playButtonEl: Element | null;\n  private readonly shareButtonEl: Element | null;\n\n  /**\n   * Button that formats the code in an example's playground.\n   */\n  private readonly formatButtonEl: Element | null;\n\n  /**\n   * Button that runs the code in an example's playground, this element\n   * only exists in executable examples.\n   */\n  private readonly runButtonEl: Element | null;\n\n  /**\n   * The executable code of an example.\n   */\n  private readonly inputEl: HTMLTextAreaElement | null;\n\n  /**\n   * The output of the given example code. This only exists if the\n   * author of the package provides an output for this example.\n   */\n  private readonly outputEl: Element | null;\n\n  /**\n   * The container for the output.\n   */\n  private readonly outputContainerEl: HTMLElement | null;\n\n  /**\n   * @param exampleEl The div that contains playground content for the given example.\n   */\n  constructor(private readonly exampleEl: HTMLDetailsElement) {\n    this.exampleEl = exampleEl;\n    this.anchorEl = exampleEl.querySelector('a');\n    this.errorEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_ERROR);\n    this.playButtonEl = exampleEl.querySelector(PlayExampleClassName.PLAY_BUTTON);\n    this.shareButtonEl = exampleEl.querySelector(PlayExampleClassName.SHARE_BUTTON);\n    this.formatButtonEl = exampleEl.querySelector(PlayExampleClassName.FORMAT_BUTTON);\n    this.runButtonEl = exampleEl.querySelector(PlayExampleClassName.RUN_BUTTON);\n    this.inputEl = this.makeTextArea(exampleEl.querySelector(PlayExampleClassName.EXAMPLE_INPUT));\n    this.outputEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_OUTPUT);\n    this.outputContainerEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_OUTPUT_CONTAINER);\n\n    // This is legacy listener to be replaced the listener for shareButtonEl.\n    this.playButtonEl?.addEventListener('click', () => this.handleShareButtonClick());\n    this.shareButtonEl?.addEventListener('click', () => this.handleShareButtonClick());\n    this.formatButtonEl?.addEventListener('click', () => this.handleFormatButtonClick());\n    this.runButtonEl?.addEventListener('click', () => this.handleRunButtonClick());\n\n    if (!this.inputEl) return;\n\n    this.resize();\n    this.inputEl.addEventListener('keyup', () => this.resize());\n    this.inputEl.addEventListener('keydown', e => this.onKeydown(e));\n  }\n\n  /**\n   * Replace the pre element with a textarea. The examples are initially rendered\n   * as pre elements so they're fully visible when JS is disabled.\n   */\n  makeTextArea(el: Element | null): HTMLTextAreaElement {\n    const t = document.createElement('textarea');\n    t.classList.add('Documentation-exampleCode', 'code');\n    t.spellcheck = false;\n    t.value = el?.textContent ?? '';\n    el?.parentElement?.replaceChild(t, el);\n    return t;\n  }\n\n  /**\n   * Retrieve the hash value of the anchor element.\n   */\n  getAnchorHash(): string | undefined {\n    return this.anchorEl?.hash;\n  }\n\n  /**\n   * Expands the current playground example.\n   */\n  expand(): void {\n    this.exampleEl.open = true;\n  }\n\n  /**\n   * Resizes the input element to accommodate the amount of text present.\n   */\n  private resize(): void {\n    if (this.inputEl?.value) {\n      const numLineBreaks = (this.inputEl.value.match(/\\n/g) || []).length;\n      // min-height + lines x line-height + padding + border\n      this.inputEl.style.height = `${(20 + numLineBreaks * 20 + 12 + 2) / 16}rem`;\n    }\n  }\n\n  /**\n   * Handler to override keyboard behavior in the playground's\n   * textarea element.\n   *\n   * Tab key inserts tabs into the example playground instead of\n   * switching to the next interactive element.\n   * @param e input element keyboard event.\n   */\n  private onKeydown(e: KeyboardEvent) {\n    if (e.key === 'Tab') {\n      document.execCommand('insertText', false, '\\t');\n      e.preventDefault();\n    }\n  }\n\n  /**\n   * Changes the text of the example's input box.\n   */\n  private setInputText(output: string) {\n    if (this.inputEl) {\n      this.inputEl.value = output;\n    }\n  }\n\n  /**\n   * Changes the text of the example's output box.\n   */\n  private setOutputText(output: string) {\n    if (this.outputEl) {\n      this.outputEl.textContent = output;\n    }\n    if (this.outputContainerEl) {\n      this.outputContainerEl.hidden = false;\n    }\n  }\n\n  /**\n   * Appends to the text of the example's output box.\n   */\n  private appendToOutputText(output: string) {\n    if (this.outputEl) {\n      this.outputEl.textContent += output;\n    }\n    if (this.outputContainerEl) {\n      this.outputContainerEl.hidden = false;\n    }\n  }\n\n  private setOutputHTML(output: string) {\n    if (this.outputEl) {\n      this.outputEl.innerHTML = output;\n    }\n  }\n\n  /**\n   * Sets the error message text and overwrites\n   * output box to indicate a failed response.\n   */\n  private setErrorText(err: string) {\n    if (this.errorEl) {\n      this.errorEl.textContent = err;\n    }\n    this.setOutputText('An error has occurred\u2026');\n  }\n\n  private getCodeWithModFile(): string {\n    let codeWithModFile = this.inputEl?.value ?? '';\n    const moduleVars = document.querySelector('.js-playgroundVars')?.dataset ?? {};\n    if (moduleVars.modulepath !== 'std') {\n      codeWithModFile = codeWithModFile.concat(`\n-- go.mod --\nmodule play.ground\n\nrequire ${moduleVars.modulepath} ${moduleVars.version}\n`);\n    }\n\n    return codeWithModFile;\n  }\n\n  /**\n   * Opens a new window to play.golang.org using the\n   * example snippet's code in the playground.\n   */\n  private handleShareButtonClick() {\n    const PLAYGROUND_BASE_URL = 'https://play.golang.org/p/';\n\n    this.setOutputText('Waiting for remote server\u2026');\n\n    fetch(abs('/play/share'), {\n      method: 'POST',\n      body: this.getCodeWithModFile(),\n    })\n      .then(res => res.text())\n      .then(shareId => {\n        const href = PLAYGROUND_BASE_URL + shareId;\n        this.setOutputHTML(`${href}`);\n        window.open(href);\n      })\n      .catch(err => {\n        this.setErrorText(err);\n      });\n  }\n\n  /**\n   * Runs gofmt on the example snippet in the playground.\n   */\n  private handleFormatButtonClick() {\n    this.setOutputText('Waiting for remote server\u2026');\n    const body = new FormData();\n    body.append('body', this.inputEl?.value ?? '');\n\n    fetch(abs('/play/fmt'), {\n      method: 'POST',\n      body: body,\n    })\n      .then(res => res.json())\n      .then(({ Body, Error }) => {\n        this.setOutputText(Error || 'Done.');\n        if (Body) {\n          this.setInputText(Body);\n          this.resize();\n        }\n      })\n      .catch(err => {\n        this.setErrorText(err);\n      });\n  }\n\n  /**\n   * Runs the code snippet in the example playground.\n   */\n  private handleRunButtonClick() {\n    this.setOutputText('Waiting for remote server\u2026');\n\n    fetch(abs('/play/compile'), {\n      method: 'POST',\n      body: JSON.stringify({ body: this.getCodeWithModFile(), version: 2 }),\n    })\n      .then(res => res.json())\n      .then(async ({ Events, Errors }) => {\n        this.setOutputText(Errors || '');\n        for (const e of Events || []) {\n          this.appendToOutputText(e.Message);\n          await new Promise(resolve => setTimeout(resolve, e.Delay / 1000000));\n        }\n      })\n      .catch(err => {\n        this.setErrorText(err);\n      });\n  }\n}\n\nexport function initPlaygrounds(): void {\n  const exampleHashRegex = location.hash.match(/^#(example-.*)$/);\n  if (exampleHashRegex) {\n    const exampleHashEl = document.getElementById(exampleHashRegex[1]) as HTMLDetailsElement;\n    if (exampleHashEl) {\n      exampleHashEl.open = true;\n    }\n  }\n\n  // We use a spread operator to convert a nodelist into an array of elements.\n  const exampleHrefs = [\n    ...document.querySelectorAll(PlayExampleClassName.PLAY_HREF),\n  ];\n\n  /**\n   * Sometimes exampleHrefs and playContainers are in different order, so we\n   * find an exampleHref from a common hash.\n   * @param playContainer - playground container\n   */\n  const findExampleHash = (playContainer: PlaygroundExampleController) =>\n    exampleHrefs.find(ex => {\n      return ex.hash === playContainer.getAnchorHash();\n    });\n\n  for (const el of document.querySelectorAll(PlayExampleClassName.PLAY_CONTAINER)) {\n    // There should be the same amount of hrefs referencing examples as example containers.\n    const playContainer = new PlaygroundExampleController(el as HTMLDetailsElement);\n    const exampleHref = findExampleHash(playContainer);\n    if (exampleHref) {\n      exampleHref.addEventListener('click', () => {\n        playContainer.expand();\n      });\n    } else {\n      console.warn('example href not found');\n    }\n  }\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { TreeNavController } from './tree.js';\n\nexport class SelectNavController {\n  constructor(private el: Element) {\n    this.el.addEventListener('change', e => {\n      const target = e.target as HTMLSelectElement;\n      let href = target.value;\n      if (!target.value.startsWith('/')) {\n        href = '/' + href;\n      }\n      window.location.href = href;\n    });\n  }\n}\n\nexport function makeSelectNav(tree: TreeNavController): HTMLLabelElement {\n  const label = document.createElement('label');\n  label.classList.add('go-Label');\n  label.setAttribute('aria-label', 'Menu');\n  const select = document.createElement('select');\n  select.classList.add('go-Select', 'js-selectNav');\n  label.appendChild(select);\n  const outline = document.createElement('optgroup');\n  outline.label = 'Outline';\n  select.appendChild(outline);\n  const groupMap: Record = {};\n  let group: HTMLOptGroupElement;\n  for (const t of tree.treeitems) {\n    if (Number(t.depth) > 4) continue;\n    if (t.groupTreeitem) {\n      group = groupMap[t.groupTreeitem.label];\n      if (!group) {\n        group = groupMap[t.groupTreeitem.label] = document.createElement('optgroup');\n        group.label = t.groupTreeitem.label;\n        select.appendChild(group);\n      }\n    } else {\n      group = outline;\n    }\n    const o = document.createElement('option');\n    o.label = t.label;\n    o.textContent = t.label;\n    o.value = (t.el as HTMLAnchorElement).href.replace(window.location.origin, '').replace('/', '');\n    group.appendChild(o);\n  }\n  tree.addObserver(t => {\n    const hash = (t.el as HTMLAnchorElement).hash;\n    const value = select.querySelector(`[value$=\"${hash}\"]`)?.value;\n    if (value) {\n      select.value = value;\n    }\n  }, 50);\n  return label;\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * TreeNavController is the navigation tree component of the documentation page.\n * It adds accessiblity attributes to a tree, observes the heading elements\n * focus the topmost link for headings visible on the page, and implements the\n * WAI-ARIA Treeview Design Pattern with full\n * [keyboard support](https://www.w3.org/TR/wai-aria-practices/examples/treeview/treeview-2/treeview-2a.html#kbd_label).\n */\nexport class TreeNavController {\n  treeitems: TreeItem[];\n\n  /**\n   * firstChars is the first character of each treeitem in the same order\n   * as this.treeitems. We use this array to set focus by character when\n   * navigating the tree with a keyboard.\n   */\n  private firstChars: string[];\n  private firstTreeitem: TreeItem | null;\n  private lastTreeitem: TreeItem | null;\n  private observerCallbacks: ((t: TreeItem) => void)[];\n\n  constructor(private el: HTMLElement) {\n    this.treeitems = [];\n    this.firstChars = [];\n    this.firstTreeitem = null;\n    this.lastTreeitem = null;\n    this.observerCallbacks = [];\n    this.init();\n  }\n\n  private init(): void {\n    this.handleResize();\n    window.addEventListener('resize', this.handleResize);\n    this.findTreeItems();\n    this.updateVisibleTreeitems();\n    this.observeTargets();\n    if (this.firstTreeitem) {\n      this.firstTreeitem.el.tabIndex = 0;\n    }\n  }\n\n  private handleResize = (): void => {\n    this.el.style.setProperty('--js-tree-height', '100vh');\n    this.el.style.setProperty('--js-tree-height', this.el.clientHeight + 'px');\n  };\n\n  private observeTargets() {\n    this.addObserver(treeitem => {\n      this.expandTreeitem(treeitem);\n      this.setSelected(treeitem);\n      // TODO: Fix scroll issue in https://golang.org/issue/47450.\n      // treeitem.el.scrollIntoView({ block: 'nearest' });\n    });\n\n    const targets = new Map();\n    const observer = new IntersectionObserver(\n      entries => {\n        for (const entry of entries) {\n          targets.set(entry.target.id, entry.isIntersecting || entry.intersectionRatio === 1);\n        }\n        for (const [id, isIntersecting] of targets) {\n          if (isIntersecting) {\n            const active = this.treeitems.find(t =>\n              (t.el as HTMLAnchorElement)?.href.endsWith(`#${id}`)\n            );\n            if (active) {\n              for (const fn of this.observerCallbacks) {\n                fn(active);\n              }\n            }\n            break;\n          }\n        }\n      },\n      {\n        threshold: 1.0,\n        rootMargin: '-60px 0px 0px 0px',\n      }\n    );\n\n    for (const href of this.treeitems.map(t => t.el.getAttribute('href'))) {\n      if (href) {\n        const id = href.replace(window.location.origin, '').replace('/', '').replace('#', '');\n        const target = document.getElementById(id);\n        if (target && !id.startsWith('example-')) {\n          observer.observe(target);\n        }\n      }\n    }\n  }\n\n  addObserver(fn: (t: TreeItem) => void, delay = 200): void {\n    this.observerCallbacks.push(debounce(fn, delay));\n  }\n\n  setFocusToNextItem(currentItem: TreeItem): void {\n    let nextItem = null;\n    for (let i = currentItem.index + 1; i < this.treeitems.length; i++) {\n      const ti = this.treeitems[i];\n      if (ti.isVisible) {\n        nextItem = ti;\n        break;\n      }\n    }\n    if (nextItem) {\n      this.setFocusToItem(nextItem);\n    }\n  }\n\n  setFocusToPreviousItem(currentItem: TreeItem): void {\n    let prevItem = null;\n    for (let i = currentItem.index - 1; i > -1; i--) {\n      const ti = this.treeitems[i];\n      if (ti.isVisible) {\n        prevItem = ti;\n        break;\n      }\n    }\n    if (prevItem) {\n      this.setFocusToItem(prevItem);\n    }\n  }\n\n  setFocusToParentItem(currentItem: TreeItem): void {\n    if (currentItem.groupTreeitem) {\n      this.setFocusToItem(currentItem.groupTreeitem);\n    }\n  }\n\n  setFocusToFirstItem(): void {\n    this.firstTreeitem && this.setFocusToItem(this.firstTreeitem);\n  }\n\n  setFocusToLastItem(): void {\n    this.lastTreeitem && this.setFocusToItem(this.lastTreeitem);\n  }\n\n  setSelected(currentItem: TreeItem): void {\n    for (const l1 of this.el.querySelectorAll('[aria-expanded=\"true\"]')) {\n      if (l1 === currentItem.el) continue;\n      if (!l1.nextElementSibling?.contains(currentItem.el)) {\n        l1.setAttribute('aria-expanded', 'false');\n      }\n    }\n    for (const l1 of this.el.querySelectorAll('[aria-selected]')) {\n      if (l1 !== currentItem.el) {\n        l1.setAttribute('aria-selected', 'false');\n      }\n    }\n    currentItem.el.setAttribute('aria-selected', 'true');\n    this.updateVisibleTreeitems();\n    this.setFocusToItem(currentItem, false);\n  }\n\n  expandTreeitem(treeitem: TreeItem): void {\n    let currentItem: TreeItem | null = treeitem;\n    while (currentItem) {\n      if (currentItem.isExpandable) {\n        currentItem.el.setAttribute('aria-expanded', 'true');\n      }\n      currentItem = currentItem.groupTreeitem;\n    }\n    this.updateVisibleTreeitems();\n  }\n\n  expandAllSiblingItems(currentItem: TreeItem): void {\n    for (const ti of this.treeitems) {\n      if (ti.groupTreeitem === currentItem.groupTreeitem && ti.isExpandable) {\n        this.expandTreeitem(ti);\n      }\n    }\n  }\n\n  collapseTreeitem(currentItem: TreeItem): void {\n    let groupTreeitem = null;\n\n    if (currentItem.isExpanded()) {\n      groupTreeitem = currentItem;\n    } else {\n      groupTreeitem = currentItem.groupTreeitem;\n    }\n\n    if (groupTreeitem) {\n      groupTreeitem.el.setAttribute('aria-expanded', 'false');\n      this.updateVisibleTreeitems();\n      this.setFocusToItem(groupTreeitem);\n    }\n  }\n\n  setFocusByFirstCharacter(currentItem: TreeItem, char: string): void {\n    let start: number, index: number;\n    char = char.toLowerCase();\n\n    // Get start index for search based on position of currentItem\n    start = currentItem.index + 1;\n    if (start === this.treeitems.length) {\n      start = 0;\n    }\n\n    // Check remaining slots in the menu\n    index = this.getIndexFirstChars(start, char);\n\n    // If not found in remaining slots, check from beginning\n    if (index === -1) {\n      index = this.getIndexFirstChars(0, char);\n    }\n\n    // If match was found...\n    if (index > -1) {\n      this.setFocusToItem(this.treeitems[index]);\n    }\n  }\n\n  private findTreeItems() {\n    const findItems = (el: HTMLElement, group: TreeItem | null) => {\n      let ti = group;\n      let curr = el.firstElementChild as HTMLElement;\n      while (curr) {\n        if (curr.tagName === 'A' || curr.tagName === 'SPAN') {\n          ti = new TreeItem(curr, this, group);\n          this.treeitems.push(ti);\n          this.firstChars.push(ti.label.substring(0, 1).toLowerCase());\n        }\n        if (curr.firstElementChild) {\n          findItems(curr, ti);\n        }\n        curr = curr.nextElementSibling as HTMLElement;\n      }\n    };\n    findItems(this.el as HTMLElement, null);\n    this.treeitems.map((ti, idx) => (ti.index = idx));\n  }\n\n  private updateVisibleTreeitems(): void {\n    this.firstTreeitem = this.treeitems[0];\n\n    for (const ti of this.treeitems) {\n      let parent = ti.groupTreeitem;\n      ti.isVisible = true;\n      while (parent && parent.el !== this.el) {\n        if (!parent.isExpanded()) {\n          ti.isVisible = false;\n        }\n        parent = parent.groupTreeitem;\n      }\n      if (ti.isVisible) {\n        this.lastTreeitem = ti;\n      }\n    }\n  }\n\n  private setFocusToItem(treeitem: TreeItem, focusEl = true) {\n    treeitem.el.tabIndex = 0;\n    if (focusEl) {\n      treeitem.el.focus();\n    }\n    for (const ti of this.treeitems) {\n      if (ti !== treeitem) {\n        ti.el.tabIndex = -1;\n      }\n    }\n  }\n\n  private getIndexFirstChars(startIndex: number, char: string): number {\n    for (let i = startIndex; i < this.firstChars.length; i++) {\n      if (this.treeitems[i].isVisible && char === this.firstChars[i]) {\n        return i;\n      }\n    }\n    return -1;\n  }\n}\n\nclass TreeItem {\n  el: HTMLElement;\n  groupTreeitem: TreeItem | null;\n  label: string;\n  isExpandable: boolean;\n  isVisible: boolean;\n  depth: number;\n  index: number;\n\n  private tree: TreeNavController;\n  private isInGroup: boolean;\n\n  constructor(el: HTMLElement, treeObj: TreeNavController, group: TreeItem | null) {\n    el.tabIndex = -1;\n    this.el = el;\n    this.groupTreeitem = group;\n    this.label = el.textContent?.trim() ?? '';\n    this.tree = treeObj;\n    this.depth = (group?.depth || 0) + 1;\n    this.index = 0;\n\n    const parent = el.parentElement;\n    if (parent?.tagName.toLowerCase() === 'li') {\n      parent?.setAttribute('role', 'none');\n    }\n    el.setAttribute('aria-level', this.depth + '');\n    if (el.getAttribute('aria-label')) {\n      this.label = el?.getAttribute('aria-label')?.trim() ?? '';\n    }\n\n    this.isExpandable = false;\n    this.isVisible = false;\n    this.isInGroup = !!group;\n\n    let curr = el.nextElementSibling;\n    while (curr) {\n      if (curr.tagName.toLowerCase() == 'ul') {\n        const groupId = `${group?.label ?? ''} nav group ${this.label}`.replace(/[\\W_]+/g, '_');\n        el.setAttribute('aria-owns', groupId);\n        el.setAttribute('aria-expanded', 'false');\n        curr.setAttribute('role', 'group');\n        curr.setAttribute('id', groupId);\n        this.isExpandable = true;\n        break;\n      }\n\n      curr = curr.nextElementSibling;\n    }\n    this.init();\n  }\n\n  private init() {\n    this.el.tabIndex = -1;\n    if (!this.el.getAttribute('role')) {\n      this.el.setAttribute('role', 'treeitem');\n    }\n    this.el.addEventListener('keydown', this.handleKeydown.bind(this));\n    this.el.addEventListener('click', this.handleClick.bind(this));\n    this.el.addEventListener('focus', this.handleFocus.bind(this));\n    this.el.addEventListener('blur', this.handleBlur.bind(this));\n  }\n\n  isExpanded() {\n    if (this.isExpandable) {\n      return this.el.getAttribute('aria-expanded') === 'true';\n    }\n\n    return false;\n  }\n\n  isSelected() {\n    return this.el.getAttribute('aria-selected') === 'true';\n  }\n\n  private handleClick(event: MouseEvent) {\n    // only process click events that directly happened on this treeitem\n    if (event.target !== this.el && event.target !== this.el.firstElementChild) {\n      return;\n    }\n    if (this.isExpandable) {\n      if (this.isExpanded() && this.isSelected()) {\n        this.tree.collapseTreeitem(this);\n      } else {\n        this.tree.expandTreeitem(this);\n      }\n      event.stopPropagation();\n    }\n    this.tree.setSelected(this);\n  }\n\n  private handleFocus() {\n    let el = this.el;\n    if (this.isExpandable) {\n      el = (el.firstElementChild as HTMLElement) ?? el;\n    }\n    el.classList.add('focus');\n  }\n\n  private handleBlur() {\n    let el = this.el;\n    if (this.isExpandable) {\n      el = (el.firstElementChild as HTMLElement) ?? el;\n    }\n    el.classList.remove('focus');\n  }\n\n  private handleKeydown(event: KeyboardEvent) {\n    if (event.altKey || event.ctrlKey || event.metaKey) {\n      return;\n    }\n\n    let captured = false;\n    switch (event.key) {\n      case ' ':\n      case 'Enter':\n        if (this.isExpandable) {\n          if (this.isExpanded() && this.isSelected()) {\n            this.tree.collapseTreeitem(this);\n          } else {\n            this.tree.expandTreeitem(this);\n          }\n          captured = true;\n        } else {\n          event.stopPropagation();\n        }\n        this.tree.setSelected(this);\n        break;\n\n      case 'ArrowUp':\n        this.tree.setFocusToPreviousItem(this);\n        captured = true;\n        break;\n\n      case 'ArrowDown':\n        this.tree.setFocusToNextItem(this);\n        captured = true;\n        break;\n\n      case 'ArrowRight':\n        if (this.isExpandable) {\n          if (this.isExpanded()) {\n            this.tree.setFocusToNextItem(this);\n          } else {\n            this.tree.expandTreeitem(this);\n          }\n        }\n        captured = true;\n        break;\n\n      case 'ArrowLeft':\n        if (this.isExpandable && this.isExpanded()) {\n          this.tree.collapseTreeitem(this);\n          captured = true;\n        } else {\n          if (this.isInGroup) {\n            this.tree.setFocusToParentItem(this);\n            captured = true;\n          }\n        }\n        break;\n\n      case 'Home':\n        this.tree.setFocusToFirstItem();\n        captured = true;\n        break;\n\n      case 'End':\n        this.tree.setFocusToLastItem();\n        captured = true;\n        break;\n\n      default:\n        if (event.key.length === 1 && event.key.match(/\\S/)) {\n          if (event.key == '*') {\n            this.tree.expandAllSiblingItems(this);\n          } else {\n            this.tree.setFocusByFirstCharacter(this, event.key);\n          }\n          captured = true;\n        }\n        break;\n    }\n\n    if (captured) {\n      event.stopPropagation();\n      event.preventDefault();\n    }\n  }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction debounce any>(func: T, wait: number) {\n  let timeout: ReturnType | null;\n  return (...args: Parameters) => {\n    const later = () => {\n      timeout = null;\n      func(...args);\n    };\n    if (timeout) {\n      clearTimeout(timeout);\n    }\n    timeout = setTimeout(later, wait);\n  };\n}\n", "/*!\n * @license\n * Copyright 2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * Controller for a table element with expandable rows. Adds event listeners to\n * a toggle within a table row that controls visiblity of additional related\n * rows in the table.\n *\n * @example\n * ```typescript\n * import {ExpandableRowsTableController} from '/static/js/table';\n *\n * const el = document .querySelector('.js-myTableElement')\n * new ExpandableRowsTableController(el));\n * ```\n */\nexport class ExpandableRowsTableController {\n  private rows: HTMLTableRowElement[];\n  private toggles: HTMLButtonElement[];\n\n  /**\n   * Create a table controller.\n   * @param table - The table element to which the controller binds.\n   */\n  constructor(private table: HTMLTableElement, private toggleAll?: HTMLButtonElement | null) {\n    this.rows = Array.from(table.querySelectorAll('[data-aria-controls]'));\n    this.toggles = Array.from(this.table.querySelectorAll('[aria-expanded]'));\n    this.setAttributes();\n    this.attachEventListeners();\n    this.update();\n  }\n\n  /**\n   * setAttributes sets data-aria-* and data-id attributes to regular\n   * html attributes as a workaround for limitations from safehtml.\n   */\n  private setAttributes() {\n    for (const a of ['data-aria-controls', 'data-aria-labelledby', 'data-id']) {\n      this.table.querySelectorAll(`[${a}]`).forEach(t => {\n        t.setAttribute(a.replace('data-', ''), t.getAttribute(a) ?? '');\n        t.removeAttribute(a);\n      });\n    }\n  }\n\n  private attachEventListeners() {\n    this.rows.forEach(t => {\n      t.addEventListener('click', e => {\n        this.handleToggleClick(e);\n      });\n    });\n    this.toggleAll?.addEventListener('click', () => {\n      this.expandAllItems();\n    });\n\n    document.addEventListener('keydown', e => {\n      if ((e.ctrlKey || e.metaKey) && e.key === 'f') {\n        this.expandAllItems();\n      }\n    });\n  }\n\n  private handleToggleClick(e: MouseEvent) {\n    let target = e.currentTarget as HTMLTableRowElement | null;\n    if (!target?.hasAttribute('aria-expanded')) {\n      target = this.table.querySelector(\n        `button[aria-controls=\"${target?.getAttribute('aria-controls')}\"]`\n      );\n    }\n    const isExpanded = target?.getAttribute('aria-expanded') === 'true';\n    target?.setAttribute('aria-expanded', isExpanded ? 'false' : 'true');\n    e.stopPropagation();\n    this.update();\n  }\n\n  expandAllItems = (): void => {\n    this.toggles.map(t => t.setAttribute('aria-expanded', 'true'));\n    this.update();\n  };\n\n  private collapseAllItems = () => {\n    this.toggles.map(t => t.setAttribute('aria-expanded', 'false'));\n    this.update();\n  };\n\n  private update = () => {\n    this.updateVisibleItems();\n    setTimeout(() => this.updateGlobalToggle());\n  };\n\n  private updateVisibleItems() {\n    this.rows.map(t => {\n      const isExpanded = t?.getAttribute('aria-expanded') === 'true';\n      const rowIds = t?.getAttribute('aria-controls')?.trimEnd().split(' ');\n      rowIds?.map(id => {\n        const target = document.getElementById(`${id}`);\n        if (isExpanded) {\n          target?.classList.add('visible');\n          target?.classList.remove('hidden');\n        } else {\n          target?.classList.add('hidden');\n          target?.classList.remove('visible');\n        }\n      });\n    });\n  }\n\n  private updateGlobalToggle() {\n    if (!this.toggleAll) return;\n    if (this.rows.some(t => t.hasAttribute('aria-expanded'))) {\n      this.toggleAll.style.display = 'block';\n    }\n    const someCollapsed = this.toggles.some(el => el.getAttribute('aria-expanded') === 'false');\n    if (someCollapsed) {\n      this.toggleAll.innerText = 'Expand all';\n      this.toggleAll.onclick = this.expandAllItems;\n      this.toggleAll.setAttribute('aria-label', 'Expand all directories');\n      this.toggleAll.setAttribute('aria-live', 'polite');\n    } else {\n      this.toggleAll.innerText = 'Collapse all';\n      this.toggleAll.onclick = this.collapseAllItems;\n      this.toggleAll.setAttribute('aria-label', 'Collapse all directories');\n      this.toggleAll.setAttribute('aria-live', 'polite');\n    }\n  }\n}\n", "import { initPlaygrounds } from 'static/shared/playground/playground';\nimport { SelectNavController, makeSelectNav } from 'static/shared/outline/select';\nimport { TreeNavController } from 'static/shared/outline/tree';\nimport { ExpandableRowsTableController } from 'static/shared/table/table';\n\ninitPlaygrounds();\n\nconst directories = document.querySelector('.js-expandableTable');\nif (directories) {\n  const table = new ExpandableRowsTableController(\n    directories,\n    document.querySelector('.js-expandAllDirectories')\n  );\n  // Expand directories on page load with expand-directories query param.\n  if (window.location.search.includes('expand-directories')) {\n    table.expandAllItems();\n  }\n\n  const internalToggle = document.querySelector('.js-showInternalDirectories');\n  if (internalToggle) {\n    if (document.querySelector('.UnitDirectories-internal')) {\n      internalToggle.style.display = 'block';\n      internalToggle.setAttribute('aria-label', 'Show Internal Directories');\n      internalToggle.setAttribute('aria-describedby', 'showInternal-description');\n    }\n    internalToggle.addEventListener('click', () => {\n      if (directories.classList.contains('UnitDirectories-showInternal')) {\n        directories.classList.remove('UnitDirectories-showInternal');\n        internalToggle.innerText = 'Show internal';\n        internalToggle.setAttribute('aria-label', 'Show Internal Directories');\n        internalToggle.setAttribute('aria-live', 'polite');\n        internalToggle.setAttribute('aria-describedby', 'showInternal-description');\n      } else {\n        directories.classList.add('UnitDirectories-showInternal');\n        internalToggle.innerText = 'Hide internal';\n        internalToggle.setAttribute('aria-label', 'Hide Internal Directories');\n        internalToggle.setAttribute('aria-live', 'polite');\n        internalToggle.setAttribute('aria-describedby', 'hideInternal-description');\n      }\n    });\n  }\n  if (document.querySelector('html[data-local=\"true\"]')) {\n    internalToggle?.click();\n  }\n}\n\nconst treeEl = document.querySelector('.js-tree');\nif (treeEl) {\n  const treeCtrl = new TreeNavController(treeEl);\n  const select = makeSelectNav(treeCtrl);\n  const mobileNav = document.querySelector('.js-mainNavMobile');\n  if (mobileNav && mobileNav.firstElementChild) {\n    mobileNav?.replaceChild(select, mobileNav.firstElementChild);\n  }\n  if (select.firstElementChild) {\n    new SelectNavController(select.firstElementChild);\n  }\n}\n\n/**\n * Event handlers for expanding and collapsing the readme section.\n */\nconst readme = document.querySelector('.js-readme');\nconst readmeContent = document.querySelector('.js-readmeContent');\nconst readmeOutline = document.querySelector('.js-readmeOutline');\nconst readmeExpand = document.querySelectorAll('.js-readmeExpand');\nconst readmeCollapse = document.querySelector('.js-readmeCollapse');\nconst mobileNavSelect = document.querySelector('.DocNavMobile-select');\nif (readme && readmeContent && readmeOutline && readmeExpand.length && readmeCollapse) {\n  if (readme.clientHeight > 320) {\n    readme?.classList.remove('UnitReadme--expanded');\n    readme?.classList.add('UnitReadme--toggle');\n  }\n  if (window.location.hash.includes('readme')) {\n    expandReadme();\n  }\n  mobileNavSelect?.addEventListener('change', e => {\n    if ((e.target as HTMLSelectElement).value.startsWith('readme-')) {\n      expandReadme();\n    }\n  });\n  readmeExpand.forEach(el =>\n    el.addEventListener('click', e => {\n      e.preventDefault();\n      expandReadme();\n      readme.scrollIntoView();\n    })\n  );\n  readmeCollapse.addEventListener('click', e => {\n    e.preventDefault();\n    readme.classList.remove('UnitReadme--expanded');\n    if (readmeExpand[1]) {\n      readmeExpand[1].scrollIntoView({ block: 'center' });\n    }\n  });\n  readmeContent.addEventListener('keyup', () => {\n    expandReadme();\n  });\n  readmeContent.addEventListener('click', () => {\n    expandReadme();\n  });\n  readmeOutline.addEventListener('click', () => {\n    expandReadme();\n  });\n  document.addEventListener('keydown', e => {\n    if ((e.ctrlKey || e.metaKey) && e.key === 'f') {\n      expandReadme();\n    }\n  });\n}\n\n/**\n * expandReadme expands the readme and adds the section-readme hash to the\n * URL so it stays expanded when navigating back from an external link.\n */\nfunction expandReadme() {\n  history.replaceState(null, '', `${location.pathname}${location.search}#section-readme`);\n  readme?.classList.add('UnitReadme--expanded');\n}\n\n/**\n * Expand details items that are focused. This will expand\n * deprecated symbols when they are navigated to from the index\n * or a direct link.\n */\nfunction openDeprecatedSymbol() {\n  if (!location.hash) return;\n  const heading = document.getElementById(location.hash.slice(1));\n  const grandParent = heading?.parentElement?.parentElement as HTMLDetailsElement | null;\n  if (grandParent?.nodeName === 'DETAILS') {\n    grandParent.open = true;\n  }\n}\nopenDeprecatedSymbol();\nwindow.addEventListener('hashchange', () => openDeprecatedSymbol());\n\n/**\n * Listen for changes in the build context dropdown.\n */\ndocument.querySelectorAll('.js-buildContextSelect').forEach(el => {\n  el.addEventListener('change', e => {\n    window.location.search = `?GOOS=${(e.target as HTMLSelectElement).value}`;\n  });\n});\n"],
    -  "mappings": "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",
    -  "names": ["getBasePath", "_a", "abs", "p", "PlayExampleClassName", "PlaygroundExampleController", "exampleEl", "_a", "_b", "_c", "_d", "e", "el", "numLineBreaks", "output", "err", "codeWithModFile", "moduleVars", "PLAYGROUND_BASE_URL", "abs", "res", "shareId", "href", "body", "Body", "Error", "Events", "Errors", "resolve", "initPlaygrounds", "exampleHashRegex", "exampleHashEl", "exampleHrefs", "findExampleHash", "playContainer", "ex", "exampleHref", "SelectNavController", "el", "e", "target", "href", "makeSelectNav", "tree", "label", "select", "outline", "groupMap", "group", "t", "o", "_a", "hash", "value", "TreeNavController", "el", "treeitem", "targets", "observer", "entries", "entry", "id", "isIntersecting", "active", "t", "_a", "fn", "href", "target", "delay", "debounce", "currentItem", "nextItem", "ti", "prevItem", "l1", "groupTreeitem", "char", "start", "index", "findItems", "group", "curr", "TreeItem", "idx", "parent", "focusEl", "startIndex", "treeObj", "_b", "_c", "_d", "_e", "groupId", "event", "captured", "func", "wait", "timeout", "args", "later", "ExpandableRowsTableController", "table", "toggleAll", "t", "a", "_a", "e", "target", "isExpanded", "rowIds", "id", "el", "initPlaygrounds", "directories", "table", "ExpandableRowsTableController", "internalToggle", "treeEl", "treeCtrl", "TreeNavController", "select", "makeSelectNav", "mobileNav", "SelectNavController", "readme", "readmeContent", "readmeOutline", "readmeExpand", "readmeCollapse", "mobileNavSelect", "expandReadme", "e", "el", "openDeprecatedSymbol", "_a", "heading", "grandParent"]
    +  "sourcesContent": ["/**\n * @license\n * Copyright 2024 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * \u7AD9\u70B9 URL \u5B50\u8DEF\u5F84\u524D\u7F00\uFF0C\u4ECE  \u8BFB\u51FA\u3002\n *\n * fork \u7528 -base-path=/gogodocs \u542F\u52A8\u65F6\uFF0Cserver \u7AEF\u6A21\u677F\u628A attribute \u5199\u6210\n * \"/gogodocs\"\uFF1B\u4E0A\u6E38\u516C\u7F51 pkg.go.dev \u9ED8\u8BA4\u6302\u6839\uFF0Cattribute \u4E3A\u7A7A\u5B57\u7B26\u4E32\u3002\n *\n * \u5728  \u4E4B\u5916\u7684\u811A\u672C\u521D\u59CB\u5316\u9636\u6BB5\u8C03\u7528\u90FD\u5B89\u5168\u2014\u2014documentElement \u4E00\u5B9A\u5B58\u5728\u3002\n */\nexport function getBasePath(): string {\n  return document.documentElement.dataset.basePath ?? '';\n}\n\n/**\n * \u7ED9\u4EE5 / \u5F00\u5934\u7684\u7EDD\u5BF9\u8DEF\u5F84\u52A0 BasePath \u524D\u7F00\u3002\n *\n * \u7528\u4E8E fetch URL / innerHTML src / location \u6BD4\u8F83\u7B49\u4EFB\u4F55\u9700\u8981\u62FC\u7AD9\u70B9\u7EDD\u5BF9 path\n * \u7684\u5730\u65B9\u3002\u975E\u7EDD\u5BF9\u8DEF\u5F84\uFF08\u4E0D\u4EE5 / \u5F00\u5934\uFF09\u539F\u6837\u8FD4\u56DE\u2014\u2014caller \u81EA\u5DF1\u4FDD\u8BC1\u8BED\u4E49\u3002\n *\n * \u4F8B\uFF1A\n *   abs('/play/share')       // \u2192 '/gogodocs/play/share' \u6216 '/play/share'\n *   abs('/static/foo.svg')   // \u2192 '/gogodocs/static/foo.svg' \u6216 '/static/foo.svg'\n *   abs('relative/x')        // \u2192 'relative/x'\uFF08\u4E0D\u52A8\uFF09\n */\nexport function abs(p: string): string {\n  if (!p.startsWith('/')) return p;\n  return getBasePath() + p;\n}\n", "/*!\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n// This file implements the playground implementation of the documentation\n// page. The playground involves a \"play\" button that allows you to open up\n// a new link to play.golang.org using the example code.\n\n// The CSS is in static/frontend/unit/main/_doc.css\n\nimport { abs } from '../base-path/base-path';\n\n/**\n * CSS classes used by PlaygroundExampleController\n */\nconst PlayExampleClassName = {\n  PLAY_HREF: '.js-exampleHref',\n  PLAY_CONTAINER: '.js-exampleContainer',\n  EXAMPLE_INPUT: '.Documentation-exampleCode',\n  EXAMPLE_OUTPUT: '.Documentation-exampleOutput',\n  EXAMPLE_OUTPUT_CONTAINER: '.js-exampleOutputContainer',\n  EXAMPLE_ERROR: '.Documentation-exampleError',\n  PLAY_BUTTON: '.Documentation-examplePlayButton',\n  SHARE_BUTTON: '.Documentation-exampleShareButton',\n  FORMAT_BUTTON: '.Documentation-exampleFormatButton',\n  RUN_BUTTON: '.Documentation-exampleRunButton',\n};\n\n/**\n * This controller enables playground examples to expand their dropdown or\n * generate shareable Go Playground URLs.\n */\nexport class PlaygroundExampleController {\n  /**\n   * The anchor tag used to identify the container with an example href.\n   * There is only one in an example container div.\n   */\n  private readonly anchorEl: HTMLAnchorElement | null;\n\n  /**\n   * The error element\n   */\n  private readonly errorEl: Element | null;\n\n  /**\n   * Buttons that redirect to an example's playground, this element\n   * only exists in executable examples.\n   */\n  private readonly playButtonEl: Element | null;\n  private readonly shareButtonEl: Element | null;\n\n  /**\n   * Button that formats the code in an example's playground.\n   */\n  private readonly formatButtonEl: Element | null;\n\n  /**\n   * Button that runs the code in an example's playground, this element\n   * only exists in executable examples.\n   */\n  private readonly runButtonEl: Element | null;\n\n  /**\n   * The executable code of an example.\n   */\n  private readonly inputEl: HTMLTextAreaElement | null;\n\n  /**\n   * The output of the given example code. This only exists if the\n   * author of the package provides an output for this example.\n   */\n  private readonly outputEl: Element | null;\n\n  /**\n   * The container for the output.\n   */\n  private readonly outputContainerEl: HTMLElement | null;\n\n  /**\n   * @param exampleEl The div that contains playground content for the given example.\n   */\n  constructor(private readonly exampleEl: HTMLDetailsElement) {\n    this.exampleEl = exampleEl;\n    this.anchorEl = exampleEl.querySelector('a');\n    this.errorEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_ERROR);\n    this.playButtonEl = exampleEl.querySelector(PlayExampleClassName.PLAY_BUTTON);\n    this.shareButtonEl = exampleEl.querySelector(PlayExampleClassName.SHARE_BUTTON);\n    this.formatButtonEl = exampleEl.querySelector(PlayExampleClassName.FORMAT_BUTTON);\n    this.runButtonEl = exampleEl.querySelector(PlayExampleClassName.RUN_BUTTON);\n    this.inputEl = this.makeTextArea(exampleEl.querySelector(PlayExampleClassName.EXAMPLE_INPUT));\n    this.outputEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_OUTPUT);\n    this.outputContainerEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_OUTPUT_CONTAINER);\n\n    // This is legacy listener to be replaced the listener for shareButtonEl.\n    this.playButtonEl?.addEventListener('click', () => this.handleShareButtonClick());\n    this.shareButtonEl?.addEventListener('click', () => this.handleShareButtonClick());\n    this.formatButtonEl?.addEventListener('click', () => this.handleFormatButtonClick());\n    this.runButtonEl?.addEventListener('click', () => this.handleRunButtonClick());\n\n    if (!this.inputEl) return;\n\n    this.resize();\n    this.inputEl.addEventListener('keyup', () => this.resize());\n    this.inputEl.addEventListener('keydown', e => this.onKeydown(e));\n  }\n\n  /**\n   * Replace the pre element with a textarea. The examples are initially rendered\n   * as pre elements so they're fully visible when JS is disabled.\n   */\n  makeTextArea(el: Element | null): HTMLTextAreaElement {\n    const t = document.createElement('textarea');\n    t.classList.add('Documentation-exampleCode', 'code');\n    t.spellcheck = false;\n    t.value = el?.textContent ?? '';\n    el?.parentElement?.replaceChild(t, el);\n    return t;\n  }\n\n  /**\n   * Retrieve the hash value of the anchor element.\n   */\n  getAnchorHash(): string | undefined {\n    return this.anchorEl?.hash;\n  }\n\n  /**\n   * Expands the current playground example.\n   */\n  expand(): void {\n    this.exampleEl.open = true;\n  }\n\n  /**\n   * Resizes the input element to accommodate the amount of text present.\n   */\n  private resize(): void {\n    if (this.inputEl?.value) {\n      const numLineBreaks = (this.inputEl.value.match(/\\n/g) || []).length;\n      // min-height + lines x line-height + padding + border\n      this.inputEl.style.height = `${(20 + numLineBreaks * 20 + 12 + 2) / 16}rem`;\n    }\n  }\n\n  /**\n   * Handler to override keyboard behavior in the playground's\n   * textarea element.\n   *\n   * Tab key inserts tabs into the example playground instead of\n   * switching to the next interactive element.\n   * @param e input element keyboard event.\n   */\n  private onKeydown(e: KeyboardEvent) {\n    if (e.key === 'Tab') {\n      document.execCommand('insertText', false, '\\t');\n      e.preventDefault();\n    }\n  }\n\n  /**\n   * Changes the text of the example's input box.\n   */\n  private setInputText(output: string) {\n    if (this.inputEl) {\n      this.inputEl.value = output;\n    }\n  }\n\n  /**\n   * Changes the text of the example's output box.\n   */\n  private setOutputText(output: string) {\n    if (this.outputEl) {\n      this.outputEl.textContent = output;\n    }\n    if (this.outputContainerEl) {\n      this.outputContainerEl.hidden = false;\n    }\n  }\n\n  /**\n   * Appends to the text of the example's output box.\n   */\n  private appendToOutputText(output: string) {\n    if (this.outputEl) {\n      this.outputEl.textContent += output;\n    }\n    if (this.outputContainerEl) {\n      this.outputContainerEl.hidden = false;\n    }\n  }\n\n  private setOutputHTML(output: string) {\n    if (this.outputEl) {\n      this.outputEl.innerHTML = output;\n    }\n  }\n\n  /**\n   * Sets the error message text and overwrites\n   * output box to indicate a failed response.\n   */\n  private setErrorText(err: string) {\n    if (this.errorEl) {\n      this.errorEl.textContent = err;\n    }\n    this.setOutputText('An error has occurred\u2026');\n  }\n\n  private getCodeWithModFile(): string {\n    let codeWithModFile = this.inputEl?.value ?? '';\n    const moduleVars = document.querySelector('.js-playgroundVars')?.dataset ?? {};\n    if (moduleVars.modulepath !== 'std') {\n      codeWithModFile = codeWithModFile.concat(`\n-- go.mod --\nmodule play.ground\n\nrequire ${moduleVars.modulepath} ${moduleVars.version}\n`);\n    }\n\n    return codeWithModFile;\n  }\n\n  /**\n   * Opens a new window to play.golang.org using the\n   * example snippet's code in the playground.\n   */\n  private handleShareButtonClick() {\n    const PLAYGROUND_BASE_URL = 'https://play.golang.org/p/';\n\n    this.setOutputText('Waiting for remote server\u2026');\n\n    fetch(abs('/play/share'), {\n      method: 'POST',\n      body: this.getCodeWithModFile(),\n    })\n      .then(res => res.text())\n      .then(shareId => {\n        const href = PLAYGROUND_BASE_URL + shareId;\n        this.setOutputHTML(`${href}`);\n        window.open(href);\n      })\n      .catch(err => {\n        this.setErrorText(err);\n      });\n  }\n\n  /**\n   * Runs gofmt on the example snippet in the playground.\n   */\n  private handleFormatButtonClick() {\n    this.setOutputText('Waiting for remote server\u2026');\n    const body = new FormData();\n    body.append('body', this.inputEl?.value ?? '');\n\n    fetch(abs('/play/fmt'), {\n      method: 'POST',\n      body: body,\n    })\n      .then(res => res.json())\n      .then(({ Body, Error }) => {\n        this.setOutputText(Error || 'Done.');\n        if (Body) {\n          this.setInputText(Body);\n          this.resize();\n        }\n      })\n      .catch(err => {\n        this.setErrorText(err);\n      });\n  }\n\n  /**\n   * Runs the code snippet in the example playground.\n   */\n  private handleRunButtonClick() {\n    this.setOutputText('Waiting for remote server\u2026');\n\n    fetch(abs('/play/compile'), {\n      method: 'POST',\n      body: JSON.stringify({ body: this.getCodeWithModFile(), version: 2 }),\n    })\n      .then(res => res.json())\n      .then(async ({ Events, Errors }) => {\n        this.setOutputText(Errors || '');\n        for (const e of Events || []) {\n          this.appendToOutputText(e.Message);\n          await new Promise(resolve => setTimeout(resolve, e.Delay / 1000000));\n        }\n      })\n      .catch(err => {\n        this.setErrorText(err);\n      });\n  }\n}\n\nexport function initPlaygrounds(): void {\n  const exampleHashRegex = location.hash.match(/^#(example-.*)$/);\n  if (exampleHashRegex) {\n    const exampleHashEl = document.getElementById(exampleHashRegex[1]) as HTMLDetailsElement;\n    if (exampleHashEl) {\n      exampleHashEl.open = true;\n    }\n  }\n\n  // We use a spread operator to convert a nodelist into an array of elements.\n  const exampleHrefs = [\n    ...document.querySelectorAll(PlayExampleClassName.PLAY_HREF),\n  ];\n\n  /**\n   * Sometimes exampleHrefs and playContainers are in different order, so we\n   * find an exampleHref from a common hash.\n   * @param playContainer - playground container\n   */\n  const findExampleHash = (playContainer: PlaygroundExampleController) =>\n    exampleHrefs.find(ex => {\n      return ex.hash === playContainer.getAnchorHash();\n    });\n\n  for (const el of document.querySelectorAll(PlayExampleClassName.PLAY_CONTAINER)) {\n    // There should be the same amount of hrefs referencing examples as example containers.\n    const playContainer = new PlaygroundExampleController(el as HTMLDetailsElement);\n    const exampleHref = findExampleHash(playContainer);\n    if (exampleHref) {\n      exampleHref.addEventListener('click', () => {\n        playContainer.expand();\n      });\n    } else {\n      console.warn('example href not found');\n    }\n  }\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { TreeNavController } from './tree.js';\n\nexport class SelectNavController {\n  constructor(private el: Element) {\n    this.el.addEventListener('change', e => {\n      const target = e.target as HTMLSelectElement;\n      let href = target.value;\n      if (!target.value.startsWith('/')) {\n        href = '/' + href;\n      }\n      window.location.href = href;\n    });\n  }\n}\n\nexport function makeSelectNav(tree: TreeNavController): HTMLLabelElement {\n  const label = document.createElement('label');\n  label.classList.add('go-Label');\n  label.setAttribute('aria-label', 'Menu');\n  const select = document.createElement('select');\n  select.classList.add('go-Select', 'js-selectNav');\n  label.appendChild(select);\n  const outline = document.createElement('optgroup');\n  outline.label = 'Outline';\n  select.appendChild(outline);\n  const groupMap: Record = {};\n  let group: HTMLOptGroupElement;\n  for (const t of tree.treeitems) {\n    if (Number(t.depth) > 4) continue;\n    if (t.groupTreeitem) {\n      group = groupMap[t.groupTreeitem.label];\n      if (!group) {\n        group = groupMap[t.groupTreeitem.label] = document.createElement('optgroup');\n        group.label = t.groupTreeitem.label;\n        select.appendChild(group);\n      }\n    } else {\n      group = outline;\n    }\n    const o = document.createElement('option');\n    o.label = t.label;\n    o.textContent = t.label;\n    o.value = (t.el as HTMLAnchorElement).href.replace(window.location.origin, '').replace('/', '');\n    group.appendChild(o);\n  }\n  tree.addObserver(t => {\n    const hash = (t.el as HTMLAnchorElement).hash;\n    const value = select.querySelector(`[value$=\"${hash}\"]`)?.value;\n    if (value) {\n      select.value = value;\n    }\n  }, 50);\n  return label;\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * TreeNavController is the navigation tree component of the documentation page.\n * It adds accessiblity attributes to a tree, observes the heading elements\n * focus the topmost link for headings visible on the page, and implements the\n * WAI-ARIA Treeview Design Pattern with full\n * [keyboard support](https://www.w3.org/TR/wai-aria-practices/examples/treeview/treeview-2/treeview-2a.html#kbd_label).\n */\nexport class TreeNavController {\n  treeitems: TreeItem[];\n\n  /**\n   * firstChars is the first character of each treeitem in the same order\n   * as this.treeitems. We use this array to set focus by character when\n   * navigating the tree with a keyboard.\n   */\n  private firstChars: string[];\n  private firstTreeitem: TreeItem | null;\n  private lastTreeitem: TreeItem | null;\n  private observerCallbacks: ((t: TreeItem) => void)[];\n\n  constructor(private el: HTMLElement) {\n    this.treeitems = [];\n    this.firstChars = [];\n    this.firstTreeitem = null;\n    this.lastTreeitem = null;\n    this.observerCallbacks = [];\n    this.init();\n  }\n\n  private init(): void {\n    this.handleResize();\n    window.addEventListener('resize', this.handleResize);\n    this.findTreeItems();\n    this.updateVisibleTreeitems();\n    this.observeTargets();\n    if (this.firstTreeitem) {\n      this.firstTreeitem.el.tabIndex = 0;\n    }\n  }\n\n  private handleResize = (): void => {\n    this.el.style.setProperty('--js-tree-height', '100vh');\n    this.el.style.setProperty('--js-tree-height', this.el.clientHeight + 'px');\n  };\n\n  private observeTargets() {\n    this.addObserver(treeitem => {\n      this.expandTreeitem(treeitem);\n      this.setSelected(treeitem);\n      // TODO: Fix scroll issue in https://golang.org/issue/47450.\n      // treeitem.el.scrollIntoView({ block: 'nearest' });\n    });\n\n    const targets = new Map();\n    const observer = new IntersectionObserver(\n      entries => {\n        for (const entry of entries) {\n          targets.set(entry.target.id, entry.isIntersecting || entry.intersectionRatio === 1);\n        }\n        for (const [id, isIntersecting] of targets) {\n          if (isIntersecting) {\n            const active = this.treeitems.find(t =>\n              (t.el as HTMLAnchorElement)?.href.endsWith(`#${id}`)\n            );\n            if (active) {\n              for (const fn of this.observerCallbacks) {\n                fn(active);\n              }\n            }\n            break;\n          }\n        }\n      },\n      {\n        threshold: 1.0,\n        rootMargin: '-60px 0px 0px 0px',\n      }\n    );\n\n    for (const href of this.treeitems.map(t => t.el.getAttribute('href'))) {\n      if (href) {\n        const id = href.replace(window.location.origin, '').replace('/', '').replace('#', '');\n        const target = document.getElementById(id);\n        if (target && !id.startsWith('example-')) {\n          observer.observe(target);\n        }\n      }\n    }\n  }\n\n  addObserver(fn: (t: TreeItem) => void, delay = 200): void {\n    this.observerCallbacks.push(debounce(fn, delay));\n  }\n\n  setFocusToNextItem(currentItem: TreeItem): void {\n    let nextItem = null;\n    for (let i = currentItem.index + 1; i < this.treeitems.length; i++) {\n      const ti = this.treeitems[i];\n      if (ti.isVisible) {\n        nextItem = ti;\n        break;\n      }\n    }\n    if (nextItem) {\n      this.setFocusToItem(nextItem);\n    }\n  }\n\n  setFocusToPreviousItem(currentItem: TreeItem): void {\n    let prevItem = null;\n    for (let i = currentItem.index - 1; i > -1; i--) {\n      const ti = this.treeitems[i];\n      if (ti.isVisible) {\n        prevItem = ti;\n        break;\n      }\n    }\n    if (prevItem) {\n      this.setFocusToItem(prevItem);\n    }\n  }\n\n  setFocusToParentItem(currentItem: TreeItem): void {\n    if (currentItem.groupTreeitem) {\n      this.setFocusToItem(currentItem.groupTreeitem);\n    }\n  }\n\n  setFocusToFirstItem(): void {\n    this.firstTreeitem && this.setFocusToItem(this.firstTreeitem);\n  }\n\n  setFocusToLastItem(): void {\n    this.lastTreeitem && this.setFocusToItem(this.lastTreeitem);\n  }\n\n  setSelected(currentItem: TreeItem): void {\n    for (const l1 of this.el.querySelectorAll('[aria-expanded=\"true\"]')) {\n      if (l1 === currentItem.el) continue;\n      if (!l1.nextElementSibling?.contains(currentItem.el)) {\n        l1.setAttribute('aria-expanded', 'false');\n      }\n    }\n    for (const l1 of this.el.querySelectorAll('[aria-selected]')) {\n      if (l1 !== currentItem.el) {\n        l1.setAttribute('aria-selected', 'false');\n      }\n    }\n    currentItem.el.setAttribute('aria-selected', 'true');\n    this.updateVisibleTreeitems();\n    this.setFocusToItem(currentItem, false);\n  }\n\n  expandTreeitem(treeitem: TreeItem): void {\n    let currentItem: TreeItem | null = treeitem;\n    while (currentItem) {\n      if (currentItem.isExpandable) {\n        currentItem.el.setAttribute('aria-expanded', 'true');\n      }\n      currentItem = currentItem.groupTreeitem;\n    }\n    this.updateVisibleTreeitems();\n  }\n\n  expandAllSiblingItems(currentItem: TreeItem): void {\n    for (const ti of this.treeitems) {\n      if (ti.groupTreeitem === currentItem.groupTreeitem && ti.isExpandable) {\n        this.expandTreeitem(ti);\n      }\n    }\n  }\n\n  collapseTreeitem(currentItem: TreeItem): void {\n    let groupTreeitem = null;\n\n    if (currentItem.isExpanded()) {\n      groupTreeitem = currentItem;\n    } else {\n      groupTreeitem = currentItem.groupTreeitem;\n    }\n\n    if (groupTreeitem) {\n      groupTreeitem.el.setAttribute('aria-expanded', 'false');\n      this.updateVisibleTreeitems();\n      this.setFocusToItem(groupTreeitem);\n    }\n  }\n\n  setFocusByFirstCharacter(currentItem: TreeItem, char: string): void {\n    let start: number, index: number;\n    char = char.toLowerCase();\n\n    // Get start index for search based on position of currentItem\n    start = currentItem.index + 1;\n    if (start === this.treeitems.length) {\n      start = 0;\n    }\n\n    // Check remaining slots in the menu\n    index = this.getIndexFirstChars(start, char);\n\n    // If not found in remaining slots, check from beginning\n    if (index === -1) {\n      index = this.getIndexFirstChars(0, char);\n    }\n\n    // If match was found...\n    if (index > -1) {\n      this.setFocusToItem(this.treeitems[index]);\n    }\n  }\n\n  private findTreeItems() {\n    const findItems = (el: HTMLElement, group: TreeItem | null) => {\n      let ti = group;\n      let curr = el.firstElementChild as HTMLElement;\n      while (curr) {\n        if (curr.tagName === 'A' || curr.tagName === 'SPAN') {\n          ti = new TreeItem(curr, this, group);\n          this.treeitems.push(ti);\n          this.firstChars.push(ti.label.substring(0, 1).toLowerCase());\n        }\n        if (curr.firstElementChild) {\n          findItems(curr, ti);\n        }\n        curr = curr.nextElementSibling as HTMLElement;\n      }\n    };\n    findItems(this.el as HTMLElement, null);\n    this.treeitems.map((ti, idx) => (ti.index = idx));\n  }\n\n  private updateVisibleTreeitems(): void {\n    this.firstTreeitem = this.treeitems[0];\n\n    for (const ti of this.treeitems) {\n      let parent = ti.groupTreeitem;\n      ti.isVisible = true;\n      while (parent && parent.el !== this.el) {\n        if (!parent.isExpanded()) {\n          ti.isVisible = false;\n        }\n        parent = parent.groupTreeitem;\n      }\n      if (ti.isVisible) {\n        this.lastTreeitem = ti;\n      }\n    }\n  }\n\n  private setFocusToItem(treeitem: TreeItem, focusEl = true) {\n    treeitem.el.tabIndex = 0;\n    if (focusEl) {\n      treeitem.el.focus();\n    }\n    for (const ti of this.treeitems) {\n      if (ti !== treeitem) {\n        ti.el.tabIndex = -1;\n      }\n    }\n  }\n\n  private getIndexFirstChars(startIndex: number, char: string): number {\n    for (let i = startIndex; i < this.firstChars.length; i++) {\n      if (this.treeitems[i].isVisible && char === this.firstChars[i]) {\n        return i;\n      }\n    }\n    return -1;\n  }\n}\n\nclass TreeItem {\n  el: HTMLElement;\n  groupTreeitem: TreeItem | null;\n  label: string;\n  isExpandable: boolean;\n  isVisible: boolean;\n  depth: number;\n  index: number;\n\n  private tree: TreeNavController;\n  private isInGroup: boolean;\n\n  constructor(el: HTMLElement, treeObj: TreeNavController, group: TreeItem | null) {\n    el.tabIndex = -1;\n    this.el = el;\n    this.groupTreeitem = group;\n    this.label = el.textContent?.trim() ?? '';\n    this.tree = treeObj;\n    this.depth = (group?.depth || 0) + 1;\n    this.index = 0;\n\n    const parent = el.parentElement;\n    if (parent?.tagName.toLowerCase() === 'li') {\n      parent?.setAttribute('role', 'none');\n    }\n    el.setAttribute('aria-level', this.depth + '');\n    if (el.getAttribute('aria-label')) {\n      this.label = el?.getAttribute('aria-label')?.trim() ?? '';\n    }\n\n    this.isExpandable = false;\n    this.isVisible = false;\n    this.isInGroup = !!group;\n\n    let curr = el.nextElementSibling;\n    while (curr) {\n      if (curr.tagName.toLowerCase() == 'ul') {\n        const groupId = `${group?.label ?? ''} nav group ${this.label}`.replace(/[\\W_]+/g, '_');\n        el.setAttribute('aria-owns', groupId);\n        el.setAttribute('aria-expanded', 'false');\n        curr.setAttribute('role', 'group');\n        curr.setAttribute('id', groupId);\n        this.isExpandable = true;\n        break;\n      }\n\n      curr = curr.nextElementSibling;\n    }\n    this.init();\n  }\n\n  private init() {\n    this.el.tabIndex = -1;\n    if (!this.el.getAttribute('role')) {\n      this.el.setAttribute('role', 'treeitem');\n    }\n    this.el.addEventListener('keydown', this.handleKeydown.bind(this));\n    this.el.addEventListener('click', this.handleClick.bind(this));\n    this.el.addEventListener('focus', this.handleFocus.bind(this));\n    this.el.addEventListener('blur', this.handleBlur.bind(this));\n  }\n\n  isExpanded() {\n    if (this.isExpandable) {\n      return this.el.getAttribute('aria-expanded') === 'true';\n    }\n\n    return false;\n  }\n\n  isSelected() {\n    return this.el.getAttribute('aria-selected') === 'true';\n  }\n\n  private handleClick(event: MouseEvent) {\n    // only process click events that directly happened on this treeitem\n    if (event.target !== this.el && event.target !== this.el.firstElementChild) {\n      return;\n    }\n    if (this.isExpandable) {\n      if (this.isExpanded() && this.isSelected()) {\n        this.tree.collapseTreeitem(this);\n      } else {\n        this.tree.expandTreeitem(this);\n      }\n      event.stopPropagation();\n    }\n    this.tree.setSelected(this);\n  }\n\n  private handleFocus() {\n    let el = this.el;\n    if (this.isExpandable) {\n      el = (el.firstElementChild as HTMLElement) ?? el;\n    }\n    el.classList.add('focus');\n  }\n\n  private handleBlur() {\n    let el = this.el;\n    if (this.isExpandable) {\n      el = (el.firstElementChild as HTMLElement) ?? el;\n    }\n    el.classList.remove('focus');\n  }\n\n  private handleKeydown(event: KeyboardEvent) {\n    if (event.altKey || event.ctrlKey || event.metaKey) {\n      return;\n    }\n\n    let captured = false;\n    switch (event.key) {\n      case ' ':\n      case 'Enter':\n        if (this.isExpandable) {\n          if (this.isExpanded() && this.isSelected()) {\n            this.tree.collapseTreeitem(this);\n          } else {\n            this.tree.expandTreeitem(this);\n          }\n          captured = true;\n        } else {\n          event.stopPropagation();\n        }\n        this.tree.setSelected(this);\n        break;\n\n      case 'ArrowUp':\n        this.tree.setFocusToPreviousItem(this);\n        captured = true;\n        break;\n\n      case 'ArrowDown':\n        this.tree.setFocusToNextItem(this);\n        captured = true;\n        break;\n\n      case 'ArrowRight':\n        if (this.isExpandable) {\n          if (this.isExpanded()) {\n            this.tree.setFocusToNextItem(this);\n          } else {\n            this.tree.expandTreeitem(this);\n          }\n        }\n        captured = true;\n        break;\n\n      case 'ArrowLeft':\n        if (this.isExpandable && this.isExpanded()) {\n          this.tree.collapseTreeitem(this);\n          captured = true;\n        } else {\n          if (this.isInGroup) {\n            this.tree.setFocusToParentItem(this);\n            captured = true;\n          }\n        }\n        break;\n\n      case 'Home':\n        this.tree.setFocusToFirstItem();\n        captured = true;\n        break;\n\n      case 'End':\n        this.tree.setFocusToLastItem();\n        captured = true;\n        break;\n\n      default:\n        if (event.key.length === 1 && event.key.match(/\\S/)) {\n          if (event.key == '*') {\n            this.tree.expandAllSiblingItems(this);\n          } else {\n            this.tree.setFocusByFirstCharacter(this, event.key);\n          }\n          captured = true;\n        }\n        break;\n    }\n\n    if (captured) {\n      event.stopPropagation();\n      event.preventDefault();\n    }\n  }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction debounce any>(func: T, wait: number) {\n  let timeout: ReturnType | null;\n  return (...args: Parameters) => {\n    const later = () => {\n      timeout = null;\n      func(...args);\n    };\n    if (timeout) {\n      clearTimeout(timeout);\n    }\n    timeout = setTimeout(later, wait);\n  };\n}\n", "/*!\n * @license\n * Copyright 2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * Controller for a table element with expandable rows. Adds event listeners to\n * a toggle within a table row that controls visiblity of additional related\n * rows in the table.\n *\n * @example\n * ```typescript\n * import {ExpandableRowsTableController} from '/static/js/table';\n *\n * const el = document .querySelector('.js-myTableElement')\n * new ExpandableRowsTableController(el));\n * ```\n */\nexport class ExpandableRowsTableController {\n  private rows: HTMLTableRowElement[];\n  private toggles: HTMLButtonElement[];\n\n  /**\n   * Create a table controller.\n   * @param table - The table element to which the controller binds.\n   */\n  constructor(private table: HTMLTableElement, private toggleAll?: HTMLButtonElement | null) {\n    this.rows = Array.from(table.querySelectorAll('[data-aria-controls]'));\n    this.toggles = Array.from(this.table.querySelectorAll('[aria-expanded]'));\n    this.setAttributes();\n    this.attachEventListeners();\n    this.update();\n  }\n\n  /**\n   * setAttributes sets data-aria-* and data-id attributes to regular\n   * html attributes as a workaround for limitations from safehtml.\n   */\n  private setAttributes() {\n    for (const a of ['data-aria-controls', 'data-aria-labelledby', 'data-id']) {\n      this.table.querySelectorAll(`[${a}]`).forEach(t => {\n        t.setAttribute(a.replace('data-', ''), t.getAttribute(a) ?? '');\n        t.removeAttribute(a);\n      });\n    }\n  }\n\n  private attachEventListeners() {\n    this.rows.forEach(t => {\n      t.addEventListener('click', e => {\n        this.handleToggleClick(e);\n      });\n    });\n    this.toggleAll?.addEventListener('click', () => {\n      this.expandAllItems();\n    });\n\n    document.addEventListener('keydown', e => {\n      if ((e.ctrlKey || e.metaKey) && e.key === 'f') {\n        this.expandAllItems();\n      }\n    });\n  }\n\n  private handleToggleClick(e: MouseEvent) {\n    let target = e.currentTarget as HTMLTableRowElement | null;\n    if (!target?.hasAttribute('aria-expanded')) {\n      target = this.table.querySelector(\n        `button[aria-controls=\"${target?.getAttribute('aria-controls')}\"]`\n      );\n    }\n    const isExpanded = target?.getAttribute('aria-expanded') === 'true';\n    target?.setAttribute('aria-expanded', isExpanded ? 'false' : 'true');\n    e.stopPropagation();\n    this.update();\n  }\n\n  expandAllItems = (): void => {\n    this.toggles.map(t => t.setAttribute('aria-expanded', 'true'));\n    this.update();\n  };\n\n  private collapseAllItems = () => {\n    this.toggles.map(t => t.setAttribute('aria-expanded', 'false'));\n    this.update();\n  };\n\n  private update = () => {\n    this.updateVisibleItems();\n    setTimeout(() => this.updateGlobalToggle());\n  };\n\n  private updateVisibleItems() {\n    this.rows.map(t => {\n      const isExpanded = t?.getAttribute('aria-expanded') === 'true';\n      const rowIds = t?.getAttribute('aria-controls')?.trimEnd().split(' ');\n      rowIds?.map(id => {\n        const target = document.getElementById(`${id}`);\n        if (isExpanded) {\n          target?.classList.add('visible');\n          target?.classList.remove('hidden');\n        } else {\n          target?.classList.add('hidden');\n          target?.classList.remove('visible');\n        }\n      });\n    });\n  }\n\n  private updateGlobalToggle() {\n    if (!this.toggleAll) return;\n    if (this.rows.some(t => t.hasAttribute('aria-expanded'))) {\n      this.toggleAll.style.display = 'block';\n    }\n    const someCollapsed = this.toggles.some(el => el.getAttribute('aria-expanded') === 'false');\n    if (someCollapsed) {\n      this.toggleAll.innerText = 'Expand all';\n      this.toggleAll.onclick = this.expandAllItems;\n      this.toggleAll.setAttribute('aria-label', 'Expand all directories');\n      this.toggleAll.setAttribute('aria-live', 'polite');\n    } else {\n      this.toggleAll.innerText = 'Collapse all';\n      this.toggleAll.onclick = this.collapseAllItems;\n      this.toggleAll.setAttribute('aria-label', 'Collapse all directories');\n      this.toggleAll.setAttribute('aria-live', 'polite');\n    }\n  }\n}\n", "import { initPlaygrounds } from 'static/shared/playground/playground';\nimport { SelectNavController, makeSelectNav } from 'static/shared/outline/select';\nimport { TreeNavController } from 'static/shared/outline/tree';\nimport { ExpandableRowsTableController } from 'static/shared/table/table';\n\ninitPlaygrounds();\n\nconst directories = document.querySelector('.js-expandableTable');\nif (directories) {\n  const table = new ExpandableRowsTableController(\n    directories,\n    document.querySelector('.js-expandAllDirectories')\n  );\n  // Expand directories on page load with expand-directories query param.\n  if (window.location.search.includes('expand-directories')) {\n    table.expandAllItems();\n  }\n\n  const internalToggle = document.querySelector('.js-showInternalDirectories');\n  if (internalToggle) {\n    if (document.querySelector('.UnitDirectories-internal')) {\n      internalToggle.style.display = 'block';\n      internalToggle.setAttribute('aria-label', 'Show Internal Directories');\n      internalToggle.setAttribute('aria-describedby', 'showInternal-description');\n    }\n    internalToggle.addEventListener('click', () => {\n      if (directories.classList.contains('UnitDirectories-showInternal')) {\n        directories.classList.remove('UnitDirectories-showInternal');\n        internalToggle.innerText = 'Show internal';\n        internalToggle.setAttribute('aria-label', 'Show Internal Directories');\n        internalToggle.setAttribute('aria-live', 'polite');\n        internalToggle.setAttribute('aria-describedby', 'showInternal-description');\n      } else {\n        directories.classList.add('UnitDirectories-showInternal');\n        internalToggle.innerText = 'Hide internal';\n        internalToggle.setAttribute('aria-label', 'Hide Internal Directories');\n        internalToggle.setAttribute('aria-live', 'polite');\n        internalToggle.setAttribute('aria-describedby', 'hideInternal-description');\n      }\n    });\n  }\n  if (document.querySelector('html[data-local=\"true\"]')) {\n    internalToggle?.click();\n  }\n}\n\nconst treeEl = document.querySelector('.js-tree');\nif (treeEl) {\n  const treeCtrl = new TreeNavController(treeEl);\n  const select = makeSelectNav(treeCtrl);\n  const mobileNav = document.querySelector('.js-mainNavMobile');\n  if (mobileNav && mobileNav.firstElementChild) {\n    mobileNav?.replaceChild(select, mobileNav.firstElementChild);\n  }\n  if (select.firstElementChild) {\n    new SelectNavController(select.firstElementChild);\n  }\n}\n\n/**\n * Event handlers for expanding and collapsing the readme section.\n */\nconst readme = document.querySelector('.js-readme');\nconst readmeContent = document.querySelector('.js-readmeContent');\nconst readmeOutline = document.querySelector('.js-readmeOutline');\nconst readmeExpand = document.querySelectorAll('.js-readmeExpand');\nconst readmeCollapse = document.querySelector('.js-readmeCollapse');\nconst mobileNavSelect = document.querySelector('.DocNavMobile-select');\nif (readme && readmeContent && readmeOutline && readmeExpand.length && readmeCollapse) {\n  if (readme.clientHeight > 320) {\n    readme?.classList.remove('UnitReadme--expanded');\n    readme?.classList.add('UnitReadme--toggle');\n  }\n  if (window.location.hash.includes('readme')) {\n    expandReadme();\n  }\n  mobileNavSelect?.addEventListener('change', e => {\n    if ((e.target as HTMLSelectElement).value.startsWith('readme-')) {\n      expandReadme();\n    }\n  });\n  readmeExpand.forEach(el =>\n    el.addEventListener('click', e => {\n      e.preventDefault();\n      expandReadme();\n      readme.scrollIntoView();\n    })\n  );\n  readmeCollapse.addEventListener('click', e => {\n    e.preventDefault();\n    readme.classList.remove('UnitReadme--expanded');\n    if (readmeExpand[1]) {\n      readmeExpand[1].scrollIntoView({ block: 'center' });\n    }\n  });\n  readmeContent.addEventListener('keyup', () => {\n    expandReadme();\n  });\n  readmeContent.addEventListener('click', () => {\n    expandReadme();\n  });\n  readmeOutline.addEventListener('click', () => {\n    expandReadme();\n  });\n  document.addEventListener('keydown', e => {\n    if ((e.ctrlKey || e.metaKey) && e.key === 'f') {\n      expandReadme();\n    }\n  });\n}\n\n/**\n * expandReadme expands the readme and adds the section-readme hash to the\n * URL so it stays expanded when navigating back from an external link.\n */\nfunction expandReadme() {\n  history.replaceState(null, '', `${location.pathname}${location.search}#section-readme`);\n  readme?.classList.add('UnitReadme--expanded');\n}\n\n/**\n * Expand details items that are focused. This will expand\n * deprecated symbols when they are navigated to from the index\n * or a direct link.\n */\nfunction openDeprecatedSymbol() {\n  if (!location.hash) return;\n  const heading = document.getElementById(location.hash.slice(1));\n  const grandParent = heading?.parentElement?.parentElement as HTMLDetailsElement | null;\n  if (grandParent?.nodeName === 'DETAILS') {\n    grandParent.open = true;\n  }\n}\nopenDeprecatedSymbol();\nwindow.addEventListener('hashchange', () => openDeprecatedSymbol());\n\n/**\n * Listen for changes in the build context dropdown.\n */\ndocument.querySelectorAll('.js-buildContextSelect').forEach(el => {\n  el.addEventListener('change', e => {\n    window.location.search = `?GOOS=${(e.target as HTMLSelectElement).value}`;\n  });\n});\n\n/**\n * fork\uFF1Aunexported\uFF08\u79C1\u6709\uFF09\u7B26\u53F7 toggle\u3002\n *\n * \u80CC\u666F\uFF1Apkgsite -show-unexported flag \u8BA9 godoc \u628A\u79C1\u6709 type/func/method\n * \u90FD\u6E32\u5230 page\u3002\u4F46\u8BFB\u8005\u5927\u591A\u6570\u65F6\u5019\u53EA\u5173\u5FC3 public API\uFF0C\u79C1\u6709\u7684\u592A\u591A\u53CD\u800C\u62D6\u6162\n * \u9605\u8BFB\u3002\u8FD9\u5C42\u5728 client \u7AEF\u6309 id \u9996\u5B57\u6BCD\u5927\u5C0F\u5199\u81EA\u52A8 hide \u79C1\u6709 declaration +\n * index \u94FE\u63A5\uFF0C\u518D\u6CE8\u5165\u4E00\u4E2A toggle button \u4E00\u952E\u5207\u663E\u793A\u3002\u72B6\u6001\u7528 localStorage\n * \u8BB0\u4E0B\uFF0C\u8DE8\u9875\u4FDD\u7559\u3002\n */\n(() => {\n  if (!document.querySelector('h4[data-kind]')) return; // \u975E godoc \u8BE6\u60C5\u9875\n\n  // method id \u5F62\u5982 \"Type.method\"\uFF0C\u53D6\u6700\u540E\u6BB5\u5224\u79C1\u6709\uFF1B\u5176\u4ED6\u76F4\u63A5\u5224 id \u672C\u8EAB\u3002\n  const isUnexported = (id: string): boolean => {\n    const last = id.split('.').pop() ?? id;\n    return /^[a-z]/.test(last);\n  };\n\n  // \u6807 declaration wrapper\uFF1Afunc \u5305\u5728 .Documentation-function\uFF1Btype / method\n  // \u5305\u5728 .Documentation-type / .Documentation-typeFunc / .Documentation-typeMethod\u3002\n  document.querySelectorAll('h4[data-kind][id]').forEach(h => {\n    if (!isUnexported(h.id)) return;\n    const wrapper = h.closest(\n      '.Documentation-function, .Documentation-type, .Documentation-typeFunc, .Documentation-typeMethod'\n    );\n    wrapper?.classList.add('Documentation-unexported');\n  });\n\n  // index \u5217\u8868\u9879\u4E5F\u6309\u94FE\u63A5\u9996\u5B57\u6BCD\u5224\n  document\n    .querySelectorAll(\n      '.Documentation-indexFunction a[href^=\"#\"], ' +\n        '.Documentation-indexType a[href^=\"#\"], ' +\n        '.Documentation-indexTypeFunctions a[href^=\"#\"], ' +\n        '.Documentation-indexTypeMethods a[href^=\"#\"]'\n    )\n    .forEach(a => {\n      if (isUnexported(a.getAttribute('href')!.slice(1))) {\n        a.closest('li')?.classList.add('Documentation-unexported');\n      }\n    });\n\n  // \u6CE8\u5165 CSS\uFF08\u4E0D\u52A8 main.css build pipeline\uFF0C\u907F\u514D\u589E\u91CF\u6539 esbuild \u8F93\u51FA\uFF09\n  const style = document.createElement('style');\n  style.textContent =\n    'body:not(.show-unexported) .Documentation-unexported{display:none}';\n  document.head.appendChild(style);\n\n  // \u6CE8\u5165 toggle button\u2014\u2014\u653E Index \u6807\u9898\u65C1\u8FB9\u6700\u663E\u773C\uFF0C\u8DDF \"Show internal\" \u540C\u4F4D\n  const indexHeader = document.querySelector('#pkg-index');\n  if (!indexHeader) return;\n  const btn = document.createElement('button');\n  btn.type = 'button';\n  btn.className = 'go-Button go-Button--inline';\n  btn.style.marginLeft = '0.75rem';\n  btn.style.fontSize = '0.875rem';\n  btn.style.verticalAlign = 'middle';\n\n  const STORE_KEY = 'gogodocs:showUnexported';\n  const apply = (show: boolean) => {\n    document.body.classList.toggle('show-unexported', show);\n    btn.textContent = show ? 'Hide unexported' : 'Show unexported';\n    try {\n      localStorage.setItem(STORE_KEY, show ? '1' : '0');\n    } catch {\n      /* localStorage \u4E0D\u53EF\u7528\uFF08\u9690\u79C1\u6A21\u5F0F / \u6587\u4EF6\u534F\u8BAE\uFF09\u65F6\u5FFD\u7565\uFF0C\u53EA\u4E22\u5931\u8DE8\u9875\u8BB0\u5FC6 */\n    }\n  };\n\n  apply(localStorage.getItem(STORE_KEY) === '1');\n  btn.addEventListener('click', () =>\n    apply(!document.body.classList.contains('show-unexported'))\n  );\n  indexHeader.appendChild(btn);\n})();\n"],
    +  "mappings": "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",
    +  "names": ["getBasePath", "_a", "abs", "p", "PlayExampleClassName", "PlaygroundExampleController", "exampleEl", "_a", "_b", "_c", "_d", "e", "el", "numLineBreaks", "output", "err", "codeWithModFile", "moduleVars", "PLAYGROUND_BASE_URL", "abs", "res", "shareId", "href", "body", "Body", "Error", "Events", "Errors", "resolve", "initPlaygrounds", "exampleHashRegex", "exampleHashEl", "exampleHrefs", "findExampleHash", "playContainer", "ex", "exampleHref", "SelectNavController", "el", "e", "target", "href", "makeSelectNav", "tree", "label", "select", "outline", "groupMap", "group", "t", "o", "_a", "hash", "value", "TreeNavController", "el", "treeitem", "targets", "observer", "entries", "entry", "id", "isIntersecting", "active", "t", "_a", "fn", "href", "target", "delay", "debounce", "currentItem", "nextItem", "ti", "prevItem", "l1", "groupTreeitem", "char", "start", "index", "findItems", "group", "curr", "TreeItem", "idx", "parent", "focusEl", "startIndex", "treeObj", "_b", "_c", "_d", "_e", "groupId", "event", "captured", "func", "wait", "timeout", "args", "later", "ExpandableRowsTableController", "table", "toggleAll", "t", "a", "_a", "e", "target", "isExpanded", "rowIds", "id", "el", "initPlaygrounds", "directories", "table", "ExpandableRowsTableController", "internalToggle", "treeEl", "treeCtrl", "TreeNavController", "select", "makeSelectNav", "mobileNav", "SelectNavController", "readme", "readmeContent", "readmeOutline", "readmeExpand", "readmeCollapse", "mobileNavSelect", "expandReadme", "e", "el", "openDeprecatedSymbol", "_a", "heading", "grandParent", "isUnexported", "id", "last", "h", "wrapper", "a", "style", "indexHeader", "btn", "STORE_KEY", "apply", "show"]
     }
    diff --git a/static/frontend/unit/main/main.ts b/static/frontend/unit/main/main.ts
    index 63ff89949..64ab596b3 100644
    --- a/static/frontend/unit/main/main.ts
    +++ b/static/frontend/unit/main/main.ts
    @@ -142,3 +142,79 @@ document.querySelectorAll('.js-buildContextSelect').forEach(el => {
         window.location.search = `?GOOS=${(e.target as HTMLSelectElement).value}`;
       });
     });
    +
    +/**
    + * fork:unexported(私有)符号 toggle。
    + *
    + * 背景:pkgsite -show-unexported flag 让 godoc 把私有 type/func/method
    + * 都渲到 page。但读者大多数时候只关心 public API,私有的太多反而拖慢
    + * 阅读。这层在 client 端按 id 首字母大小写自动 hide 私有 declaration +
    + * index 链接,再注入一个 toggle button 一键切显示。状态用 localStorage
    + * 记下,跨页保留。
    + */
    +(() => {
    +  if (!document.querySelector('h4[data-kind]')) return; // 非 godoc 详情页
    +
    +  // method id 形如 "Type.method",取最后段判私有;其他直接判 id 本身。
    +  const isUnexported = (id: string): boolean => {
    +    const last = id.split('.').pop() ?? id;
    +    return /^[a-z]/.test(last);
    +  };
    +
    +  // 标 declaration wrapper:func 包在 .Documentation-function;type / method
    +  // 包在 .Documentation-type / .Documentation-typeFunc / .Documentation-typeMethod。
    +  document.querySelectorAll('h4[data-kind][id]').forEach(h => {
    +    if (!isUnexported(h.id)) return;
    +    const wrapper = h.closest(
    +      '.Documentation-function, .Documentation-type, .Documentation-typeFunc, .Documentation-typeMethod'
    +    );
    +    wrapper?.classList.add('Documentation-unexported');
    +  });
    +
    +  // index 列表项也按链接首字母判
    +  document
    +    .querySelectorAll(
    +      '.Documentation-indexFunction a[href^="#"], ' +
    +        '.Documentation-indexType a[href^="#"], ' +
    +        '.Documentation-indexTypeFunctions a[href^="#"], ' +
    +        '.Documentation-indexTypeMethods a[href^="#"]'
    +    )
    +    .forEach(a => {
    +      if (isUnexported(a.getAttribute('href')!.slice(1))) {
    +        a.closest('li')?.classList.add('Documentation-unexported');
    +      }
    +    });
    +
    +  // 注入 CSS(不动 main.css build pipeline,避免增量改 esbuild 输出)
    +  const style = document.createElement('style');
    +  style.textContent =
    +    'body:not(.show-unexported) .Documentation-unexported{display:none}';
    +  document.head.appendChild(style);
    +
    +  // 注入 toggle button——放 Index 标题旁边最显眼,跟 "Show internal" 同位
    +  const indexHeader = document.querySelector('#pkg-index');
    +  if (!indexHeader) return;
    +  const btn = document.createElement('button');
    +  btn.type = 'button';
    +  btn.className = 'go-Button go-Button--inline';
    +  btn.style.marginLeft = '0.75rem';
    +  btn.style.fontSize = '0.875rem';
    +  btn.style.verticalAlign = 'middle';
    +
    +  const STORE_KEY = 'gogodocs:showUnexported';
    +  const apply = (show: boolean) => {
    +    document.body.classList.toggle('show-unexported', show);
    +    btn.textContent = show ? 'Hide unexported' : 'Show unexported';
    +    try {
    +      localStorage.setItem(STORE_KEY, show ? '1' : '0');
    +    } catch {
    +      /* localStorage 不可用(隐私模式 / 文件协议)时忽略,只丢失跨页记忆 */
    +    }
    +  };
    +
    +  apply(localStorage.getItem(STORE_KEY) === '1');
    +  btn.addEventListener('click', () =>
    +    apply(!document.body.classList.contains('show-unexported'))
    +  );
    +  indexHeader.appendChild(btn);
    +})();
    
    From 68b3f107a29aa060a23267c4c69ec82f50c56790 Mon Sep 17 00:00:00 2001
    From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com>
    Date: Thu, 7 May 2026 17:12:16 +0800
    Subject: [PATCH 10/26] =?UTF-8?q?fix(toggle):=20unexported=20toggle=20?=
     =?UTF-8?q?=E8=A6=86=E7=9B=96=E4=BE=A7=E8=BE=B9=E6=A0=8F=20+=20=E6=8E=92?=
     =?UTF-8?q?=E9=99=A4=20page=20anchor=20=E8=AF=AF=E6=A0=87?=
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    
    之前只标 .Documentation-index* 中央列表,左侧边栏 .go-Tree outline 没
    覆盖——侧边栏私有函数链接还是显示。selector 加 '.go-Tree a[href^="#"]'。
    
    isUnexported 加头部 anchor 排除:pkg- / section- / hdr- 起头的是页面
    导航锚点(pkg-overview / section-readme / hdr-... 等),全小写但不是
    Go 符号,扫到会误标整段隐藏。
    ---
     static/frontend/unit/main/main.js     |  2 +-
     static/frontend/unit/main/main.js.map |  4 ++--
     static/frontend/unit/main/main.ts     | 12 ++++++++++--
     3 files changed, 13 insertions(+), 5 deletions(-)
    
    diff --git a/static/frontend/unit/main/main.js b/static/frontend/unit/main/main.js
    index 67dc3cbe3..c1a1cbba7 100644
    --- a/static/frontend/unit/main/main.js
    +++ b/static/frontend/unit/main/main.js
    @@ -3,7 +3,7 @@ function H(){var l;return(l=document.documentElement.dataset.basePath)!=null?l:"
     module play.ground
     
     require ${t.modulepath} ${t.version}
    -`)),e}handleShareButtonClick(){let e="https://play.golang.org/p/";this.setOutputText("Waiting for remote server\u2026"),fetch(p("/play/share"),{method:"POST",body:this.getCodeWithModFile()}).then(t=>t.text()).then(t=>{let i=e+t;this.setOutputHTML(`${i}`),window.open(i)}).catch(t=>{this.setErrorText(t)})}handleFormatButtonClick(){var t,i;this.setOutputText("Waiting for remote server\u2026");let e=new FormData;e.append("body",(i=(t=this.inputEl)==null?void 0:t.value)!=null?i:""),fetch(p("/play/fmt"),{method:"POST",body:e}).then(s=>s.json()).then(({Body:s,Error:n})=>{this.setOutputText(n||"Done."),s&&(this.setInputText(s),this.resize())}).catch(s=>{this.setErrorText(s)})}handleRunButtonClick(){this.setOutputText("Waiting for remote server\u2026"),fetch(p("/play/compile"),{method:"POST",body:JSON.stringify({body:this.getCodeWithModFile(),version:2})}).then(e=>e.json()).then(async({Events:e,Errors:t})=>{this.setOutputText(t||"");for(let i of e||[])this.appendToOutputText(i.Message),await new Promise(s=>setTimeout(s,i.Delay/1e6))}).catch(e=>{this.setErrorText(e)})}};function C(){let l=location.hash.match(/^#(example-.*)$/);if(l){let i=document.getElementById(l[1]);i&&(i.open=!0)}let e=[...document.querySelectorAll(u.PLAY_HREF)],t=i=>e.find(s=>s.hash===i.getAnchorHash());for(let i of document.querySelectorAll(u.PLAY_CONTAINER)){let s=new x(i),n=t(s);n?n.addEventListener("click",()=>{s.expand()}):console.warn("example href not found")}}var f=class{constructor(e){this.el=e;this.el.addEventListener("change",t=>{let i=t.target,s=i.value;i.value.startsWith("/")||(s="/"+s),window.location.href=s})}};function I(l){let e=document.createElement("label");e.classList.add("go-Label"),e.setAttribute("aria-label","Menu");let t=document.createElement("select");t.classList.add("go-Select","js-selectNav"),e.appendChild(t);let i=document.createElement("optgroup");i.label="Outline",t.appendChild(i);let s={},n;for(let r of l.treeitems){if(Number(r.depth)>4)continue;r.groupTreeitem?(n=s[r.groupTreeitem.label],n||(n=s[r.groupTreeitem.label]=document.createElement("optgroup"),n.label=r.groupTreeitem.label,t.appendChild(n))):n=i;let a=document.createElement("option");a.label=r.label,a.textContent=r.label,a.value=r.el.href.replace(window.location.origin,"").replace("/",""),n.appendChild(a)}return l.addObserver(r=>{var h;let a=r.el.hash,d=(h=t.querySelector(`[value$="${a}"]`))==null?void 0:h.value;d&&(t.value=d)},50),e}var E=class{constructor(e){this.el=e;this.handleResize=()=>{this.el.style.setProperty("--js-tree-height","100vh"),this.el.style.setProperty("--js-tree-height",this.el.clientHeight+"px")};this.treeitems=[],this.firstChars=[],this.firstTreeitem=null,this.lastTreeitem=null,this.observerCallbacks=[],this.init()}init(){this.handleResize(),window.addEventListener("resize",this.handleResize),this.findTreeItems(),this.updateVisibleTreeitems(),this.observeTargets(),this.firstTreeitem&&(this.firstTreeitem.el.tabIndex=0)}observeTargets(){this.addObserver(i=>{this.expandTreeitem(i),this.setSelected(i)});let e=new Map,t=new IntersectionObserver(i=>{for(let s of i)e.set(s.target.id,s.isIntersecting||s.intersectionRatio===1);for(let[s,n]of e)if(n){let r=this.treeitems.find(a=>{var d;return(d=a.el)==null?void 0:d.href.endsWith(`#${s}`)});if(r)for(let a of this.observerCallbacks)a(r);break}},{threshold:1,rootMargin:"-60px 0px 0px 0px"});for(let i of this.treeitems.map(s=>s.el.getAttribute("href")))if(i){let s=i.replace(window.location.origin,"").replace("/","").replace("#",""),n=document.getElementById(s);n&&!s.startsWith("example-")&&t.observe(n)}}addObserver(e,t=200){this.observerCallbacks.push(O(e,t))}setFocusToNextItem(e){let t=null;for(let i=e.index+1;i-1;i--){let s=this.treeitems[i];if(s.isVisible){t=s;break}}t&&this.setFocusToItem(t)}setFocusToParentItem(e){e.groupTreeitem&&this.setFocusToItem(e.groupTreeitem)}setFocusToFirstItem(){this.firstTreeitem&&this.setFocusToItem(this.firstTreeitem)}setFocusToLastItem(){this.lastTreeitem&&this.setFocusToItem(this.lastTreeitem)}setSelected(e){var t;for(let i of this.el.querySelectorAll('[aria-expanded="true"]'))i!==e.el&&((t=i.nextElementSibling)!=null&&t.contains(e.el)||i.setAttribute("aria-expanded","false"));for(let i of this.el.querySelectorAll("[aria-selected]"))i!==e.el&&i.setAttribute("aria-selected","false");e.el.setAttribute("aria-selected","true"),this.updateVisibleTreeitems(),this.setFocusToItem(e,!1)}expandTreeitem(e){let t=e;for(;t;)t.isExpandable&&t.el.setAttribute("aria-expanded","true"),t=t.groupTreeitem;this.updateVisibleTreeitems()}expandAllSiblingItems(e){for(let t of this.treeitems)t.groupTreeitem===e.groupTreeitem&&t.isExpandable&&this.expandTreeitem(t)}collapseTreeitem(e){let t=null;e.isExpanded()?t=e:t=e.groupTreeitem,t&&(t.el.setAttribute("aria-expanded","false"),this.updateVisibleTreeitems(),this.setFocusToItem(t))}setFocusByFirstCharacter(e,t){let i,s;t=t.toLowerCase(),i=e.index+1,i===this.treeitems.length&&(i=0),s=this.getIndexFirstChars(i,t),s===-1&&(s=this.getIndexFirstChars(0,t)),s>-1&&this.setFocusToItem(this.treeitems[s])}findTreeItems(){let e=(t,i)=>{let s=i,n=t.firstElementChild;for(;n;)(n.tagName==="A"||n.tagName==="SPAN")&&(s=new g(n,this,i),this.treeitems.push(s),this.firstChars.push(s.label.substring(0,1).toLowerCase())),n.firstElementChild&&e(n,s),n=n.nextElementSibling};e(this.el,null),this.treeitems.map((t,i)=>t.index=i)}updateVisibleTreeitems(){this.firstTreeitem=this.treeitems[0];for(let e of this.treeitems){let t=e.groupTreeitem;for(e.isVisible=!0;t&&t.el!==this.el;)t.isExpanded()||(e.isVisible=!1),t=t.groupTreeitem;e.isVisible&&(this.lastTreeitem=e)}}setFocusToItem(e,t=!0){e.el.tabIndex=0,t&&e.el.focus();for(let i of this.treeitems)i!==e&&(i.el.tabIndex=-1)}getIndexFirstChars(e,t){for(let i=e;i{let s=()=>{t=null,l(...i)};t&&clearTimeout(t),t=setTimeout(s,e)}}var T=class{constructor(e,t){this.table=e;this.toggleAll=t;this.expandAllItems=()=>{this.toggles.map(e=>e.setAttribute("aria-expanded","true")),this.update()};this.collapseAllItems=()=>{this.toggles.map(e=>e.setAttribute("aria-expanded","false")),this.update()};this.update=()=>{this.updateVisibleItems(),setTimeout(()=>this.updateGlobalToggle())};this.rows=Array.from(e.querySelectorAll("[data-aria-controls]")),this.toggles=Array.from(this.table.querySelectorAll("[aria-expanded]")),this.setAttributes(),this.attachEventListeners(),this.update()}setAttributes(){for(let e of["data-aria-controls","data-aria-labelledby","data-id"])this.table.querySelectorAll(`[${e}]`).forEach(t=>{var i;t.setAttribute(e.replace("data-",""),(i=t.getAttribute(e))!=null?i:""),t.removeAttribute(e)})}attachEventListeners(){var e;this.rows.forEach(t=>{t.addEventListener("click",i=>{this.handleToggleClick(i)})}),(e=this.toggleAll)==null||e.addEventListener("click",()=>{this.expandAllItems()}),document.addEventListener("keydown",t=>{(t.ctrlKey||t.metaKey)&&t.key==="f"&&this.expandAllItems()})}handleToggleClick(e){let t=e.currentTarget;t!=null&&t.hasAttribute("aria-expanded")||(t=this.table.querySelector(`button[aria-controls="${t==null?void 0:t.getAttribute("aria-controls")}"]`));let i=(t==null?void 0:t.getAttribute("aria-expanded"))==="true";t==null||t.setAttribute("aria-expanded",i?"false":"true"),e.stopPropagation(),this.update()}updateVisibleItems(){this.rows.map(e=>{var s;let t=(e==null?void 0:e.getAttribute("aria-expanded"))==="true",i=(s=e==null?void 0:e.getAttribute("aria-controls"))==null?void 0:s.trimEnd().split(" ");i==null||i.map(n=>{let r=document.getElementById(`${n}`);t?(r==null||r.classList.add("visible"),r==null||r.classList.remove("hidden")):(r==null||r.classList.add("hidden"),r==null||r.classList.remove("visible"))})})}updateGlobalToggle(){if(!this.toggleAll)return;this.rows.some(t=>t.hasAttribute("aria-expanded"))&&(this.toggleAll.style.display="block"),this.toggles.some(t=>t.getAttribute("aria-expanded")==="false")?(this.toggleAll.innerText="Expand all",this.toggleAll.onclick=this.expandAllItems,this.toggleAll.setAttribute("aria-label","Expand all directories"),this.toggleAll.setAttribute("aria-live","polite")):(this.toggleAll.innerText="Collapse all",this.toggleAll.onclick=this.collapseAllItems,this.toggleAll.setAttribute("aria-label","Collapse all directories"),this.toggleAll.setAttribute("aria-live","polite"))}};C();var m=document.querySelector(".js-expandableTable");if(m){let l=new T(m,document.querySelector(".js-expandAllDirectories"));window.location.search.includes("expand-directories")&&l.expandAllItems();let e=document.querySelector(".js-showInternalDirectories");e&&(document.querySelector(".UnitDirectories-internal")&&(e.style.display="block",e.setAttribute("aria-label","Show Internal Directories"),e.setAttribute("aria-describedby","showInternal-description")),e.addEventListener("click",()=>{m.classList.contains("UnitDirectories-showInternal")?(m.classList.remove("UnitDirectories-showInternal"),e.innerText="Show internal",e.setAttribute("aria-label","Show Internal Directories"),e.setAttribute("aria-live","polite"),e.setAttribute("aria-describedby","showInternal-description")):(m.classList.add("UnitDirectories-showInternal"),e.innerText="Hide internal",e.setAttribute("aria-label","Hide Internal Directories"),e.setAttribute("aria-live","polite"),e.setAttribute("aria-describedby","hideInternal-description"))})),document.querySelector('html[data-local="true"]')&&(e==null||e.click())}var S=document.querySelector(".js-tree");if(S){let l=new E(S),e=I(l),t=document.querySelector(".js-mainNavMobile");t&&t.firstElementChild&&(t==null||t.replaceChild(e,t.firstElementChild)),e.firstElementChild&&new f(e.firstElementChild)}var o=document.querySelector(".js-readme"),v=document.querySelector(".js-readmeContent"),w=document.querySelector(".js-readmeOutline"),b=document.querySelectorAll(".js-readmeExpand"),M=document.querySelector(".js-readmeCollapse"),y=document.querySelector(".DocNavMobile-select");o&&v&&w&&b.length&&M&&(o.clientHeight>320&&(o==null||o.classList.remove("UnitReadme--expanded"),o==null||o.classList.add("UnitReadme--toggle")),window.location.hash.includes("readme")&&c(),y==null||y.addEventListener("change",l=>{l.target.value.startsWith("readme-")&&c()}),b.forEach(l=>l.addEventListener("click",e=>{e.preventDefault(),c(),o.scrollIntoView()})),M.addEventListener("click",l=>{l.preventDefault(),o.classList.remove("UnitReadme--expanded"),b[1]&&b[1].scrollIntoView({block:"center"})}),v.addEventListener("keyup",()=>{c()}),v.addEventListener("click",()=>{c()}),w.addEventListener("click",()=>{c()}),document.addEventListener("keydown",l=>{(l.ctrlKey||l.metaKey)&&l.key==="f"&&c()}));function c(){history.replaceState(null,"",`${location.pathname}${location.search}#section-readme`),o==null||o.classList.add("UnitReadme--expanded")}function k(){var t;if(!location.hash)return;let l=document.getElementById(location.hash.slice(1)),e=(t=l==null?void 0:l.parentElement)==null?void 0:t.parentElement;(e==null?void 0:e.nodeName)==="DETAILS"&&(e.open=!0)}k();window.addEventListener("hashchange",()=>k());document.querySelectorAll(".js-buildContextSelect").forEach(l=>{l.addEventListener("change",e=>{window.location.search=`?GOOS=${e.target.value}`})});(()=>{if(!document.querySelector("h4[data-kind]"))return;let l=r=>{var d;let a=(d=r.split(".").pop())!=null?d:r;return/^[a-z]/.test(a)};document.querySelectorAll("h4[data-kind][id]").forEach(r=>{if(!l(r.id))return;let a=r.closest(".Documentation-function, .Documentation-type, .Documentation-typeFunc, .Documentation-typeMethod");a==null||a.classList.add("Documentation-unexported")}),document.querySelectorAll('.Documentation-indexFunction a[href^="#"], .Documentation-indexType a[href^="#"], .Documentation-indexTypeFunctions a[href^="#"], .Documentation-indexTypeMethods a[href^="#"]').forEach(r=>{var a;l(r.getAttribute("href").slice(1))&&((a=r.closest("li"))==null||a.classList.add("Documentation-unexported"))});let e=document.createElement("style");e.textContent="body:not(.show-unexported) .Documentation-unexported{display:none}",document.head.appendChild(e);let t=document.querySelector("#pkg-index");if(!t)return;let i=document.createElement("button");i.type="button",i.className="go-Button go-Button--inline",i.style.marginLeft="0.75rem",i.style.fontSize="0.875rem",i.style.verticalAlign="middle";let s="gogodocs:showUnexported",n=r=>{document.body.classList.toggle("show-unexported",r),i.textContent=r?"Hide unexported":"Show unexported";try{localStorage.setItem(s,r?"1":"0")}catch{}};n(localStorage.getItem(s)==="1"),i.addEventListener("click",()=>n(!document.body.classList.contains("show-unexported"))),t.appendChild(i)})();
    +`)),e}handleShareButtonClick(){let e="https://play.golang.org/p/";this.setOutputText("Waiting for remote server\u2026"),fetch(p("/play/share"),{method:"POST",body:this.getCodeWithModFile()}).then(t=>t.text()).then(t=>{let i=e+t;this.setOutputHTML(`${i}`),window.open(i)}).catch(t=>{this.setErrorText(t)})}handleFormatButtonClick(){var t,i;this.setOutputText("Waiting for remote server\u2026");let e=new FormData;e.append("body",(i=(t=this.inputEl)==null?void 0:t.value)!=null?i:""),fetch(p("/play/fmt"),{method:"POST",body:e}).then(s=>s.json()).then(({Body:s,Error:n})=>{this.setOutputText(n||"Done."),s&&(this.setInputText(s),this.resize())}).catch(s=>{this.setErrorText(s)})}handleRunButtonClick(){this.setOutputText("Waiting for remote server\u2026"),fetch(p("/play/compile"),{method:"POST",body:JSON.stringify({body:this.getCodeWithModFile(),version:2})}).then(e=>e.json()).then(async({Events:e,Errors:t})=>{this.setOutputText(t||"");for(let i of e||[])this.appendToOutputText(i.Message),await new Promise(s=>setTimeout(s,i.Delay/1e6))}).catch(e=>{this.setErrorText(e)})}};function C(){let l=location.hash.match(/^#(example-.*)$/);if(l){let i=document.getElementById(l[1]);i&&(i.open=!0)}let e=[...document.querySelectorAll(u.PLAY_HREF)],t=i=>e.find(s=>s.hash===i.getAnchorHash());for(let i of document.querySelectorAll(u.PLAY_CONTAINER)){let s=new x(i),n=t(s);n?n.addEventListener("click",()=>{s.expand()}):console.warn("example href not found")}}var f=class{constructor(e){this.el=e;this.el.addEventListener("change",t=>{let i=t.target,s=i.value;i.value.startsWith("/")||(s="/"+s),window.location.href=s})}};function I(l){let e=document.createElement("label");e.classList.add("go-Label"),e.setAttribute("aria-label","Menu");let t=document.createElement("select");t.classList.add("go-Select","js-selectNav"),e.appendChild(t);let i=document.createElement("optgroup");i.label="Outline",t.appendChild(i);let s={},n;for(let r of l.treeitems){if(Number(r.depth)>4)continue;r.groupTreeitem?(n=s[r.groupTreeitem.label],n||(n=s[r.groupTreeitem.label]=document.createElement("optgroup"),n.label=r.groupTreeitem.label,t.appendChild(n))):n=i;let a=document.createElement("option");a.label=r.label,a.textContent=r.label,a.value=r.el.href.replace(window.location.origin,"").replace("/",""),n.appendChild(a)}return l.addObserver(r=>{var h;let a=r.el.hash,d=(h=t.querySelector(`[value$="${a}"]`))==null?void 0:h.value;d&&(t.value=d)},50),e}var E=class{constructor(e){this.el=e;this.handleResize=()=>{this.el.style.setProperty("--js-tree-height","100vh"),this.el.style.setProperty("--js-tree-height",this.el.clientHeight+"px")};this.treeitems=[],this.firstChars=[],this.firstTreeitem=null,this.lastTreeitem=null,this.observerCallbacks=[],this.init()}init(){this.handleResize(),window.addEventListener("resize",this.handleResize),this.findTreeItems(),this.updateVisibleTreeitems(),this.observeTargets(),this.firstTreeitem&&(this.firstTreeitem.el.tabIndex=0)}observeTargets(){this.addObserver(i=>{this.expandTreeitem(i),this.setSelected(i)});let e=new Map,t=new IntersectionObserver(i=>{for(let s of i)e.set(s.target.id,s.isIntersecting||s.intersectionRatio===1);for(let[s,n]of e)if(n){let r=this.treeitems.find(a=>{var d;return(d=a.el)==null?void 0:d.href.endsWith(`#${s}`)});if(r)for(let a of this.observerCallbacks)a(r);break}},{threshold:1,rootMargin:"-60px 0px 0px 0px"});for(let i of this.treeitems.map(s=>s.el.getAttribute("href")))if(i){let s=i.replace(window.location.origin,"").replace("/","").replace("#",""),n=document.getElementById(s);n&&!s.startsWith("example-")&&t.observe(n)}}addObserver(e,t=200){this.observerCallbacks.push(O(e,t))}setFocusToNextItem(e){let t=null;for(let i=e.index+1;i-1;i--){let s=this.treeitems[i];if(s.isVisible){t=s;break}}t&&this.setFocusToItem(t)}setFocusToParentItem(e){e.groupTreeitem&&this.setFocusToItem(e.groupTreeitem)}setFocusToFirstItem(){this.firstTreeitem&&this.setFocusToItem(this.firstTreeitem)}setFocusToLastItem(){this.lastTreeitem&&this.setFocusToItem(this.lastTreeitem)}setSelected(e){var t;for(let i of this.el.querySelectorAll('[aria-expanded="true"]'))i!==e.el&&((t=i.nextElementSibling)!=null&&t.contains(e.el)||i.setAttribute("aria-expanded","false"));for(let i of this.el.querySelectorAll("[aria-selected]"))i!==e.el&&i.setAttribute("aria-selected","false");e.el.setAttribute("aria-selected","true"),this.updateVisibleTreeitems(),this.setFocusToItem(e,!1)}expandTreeitem(e){let t=e;for(;t;)t.isExpandable&&t.el.setAttribute("aria-expanded","true"),t=t.groupTreeitem;this.updateVisibleTreeitems()}expandAllSiblingItems(e){for(let t of this.treeitems)t.groupTreeitem===e.groupTreeitem&&t.isExpandable&&this.expandTreeitem(t)}collapseTreeitem(e){let t=null;e.isExpanded()?t=e:t=e.groupTreeitem,t&&(t.el.setAttribute("aria-expanded","false"),this.updateVisibleTreeitems(),this.setFocusToItem(t))}setFocusByFirstCharacter(e,t){let i,s;t=t.toLowerCase(),i=e.index+1,i===this.treeitems.length&&(i=0),s=this.getIndexFirstChars(i,t),s===-1&&(s=this.getIndexFirstChars(0,t)),s>-1&&this.setFocusToItem(this.treeitems[s])}findTreeItems(){let e=(t,i)=>{let s=i,n=t.firstElementChild;for(;n;)(n.tagName==="A"||n.tagName==="SPAN")&&(s=new g(n,this,i),this.treeitems.push(s),this.firstChars.push(s.label.substring(0,1).toLowerCase())),n.firstElementChild&&e(n,s),n=n.nextElementSibling};e(this.el,null),this.treeitems.map((t,i)=>t.index=i)}updateVisibleTreeitems(){this.firstTreeitem=this.treeitems[0];for(let e of this.treeitems){let t=e.groupTreeitem;for(e.isVisible=!0;t&&t.el!==this.el;)t.isExpanded()||(e.isVisible=!1),t=t.groupTreeitem;e.isVisible&&(this.lastTreeitem=e)}}setFocusToItem(e,t=!0){e.el.tabIndex=0,t&&e.el.focus();for(let i of this.treeitems)i!==e&&(i.el.tabIndex=-1)}getIndexFirstChars(e,t){for(let i=e;i{let s=()=>{t=null,l(...i)};t&&clearTimeout(t),t=setTimeout(s,e)}}var T=class{constructor(e,t){this.table=e;this.toggleAll=t;this.expandAllItems=()=>{this.toggles.map(e=>e.setAttribute("aria-expanded","true")),this.update()};this.collapseAllItems=()=>{this.toggles.map(e=>e.setAttribute("aria-expanded","false")),this.update()};this.update=()=>{this.updateVisibleItems(),setTimeout(()=>this.updateGlobalToggle())};this.rows=Array.from(e.querySelectorAll("[data-aria-controls]")),this.toggles=Array.from(this.table.querySelectorAll("[aria-expanded]")),this.setAttributes(),this.attachEventListeners(),this.update()}setAttributes(){for(let e of["data-aria-controls","data-aria-labelledby","data-id"])this.table.querySelectorAll(`[${e}]`).forEach(t=>{var i;t.setAttribute(e.replace("data-",""),(i=t.getAttribute(e))!=null?i:""),t.removeAttribute(e)})}attachEventListeners(){var e;this.rows.forEach(t=>{t.addEventListener("click",i=>{this.handleToggleClick(i)})}),(e=this.toggleAll)==null||e.addEventListener("click",()=>{this.expandAllItems()}),document.addEventListener("keydown",t=>{(t.ctrlKey||t.metaKey)&&t.key==="f"&&this.expandAllItems()})}handleToggleClick(e){let t=e.currentTarget;t!=null&&t.hasAttribute("aria-expanded")||(t=this.table.querySelector(`button[aria-controls="${t==null?void 0:t.getAttribute("aria-controls")}"]`));let i=(t==null?void 0:t.getAttribute("aria-expanded"))==="true";t==null||t.setAttribute("aria-expanded",i?"false":"true"),e.stopPropagation(),this.update()}updateVisibleItems(){this.rows.map(e=>{var s;let t=(e==null?void 0:e.getAttribute("aria-expanded"))==="true",i=(s=e==null?void 0:e.getAttribute("aria-controls"))==null?void 0:s.trimEnd().split(" ");i==null||i.map(n=>{let r=document.getElementById(`${n}`);t?(r==null||r.classList.add("visible"),r==null||r.classList.remove("hidden")):(r==null||r.classList.add("hidden"),r==null||r.classList.remove("visible"))})})}updateGlobalToggle(){if(!this.toggleAll)return;this.rows.some(t=>t.hasAttribute("aria-expanded"))&&(this.toggleAll.style.display="block"),this.toggles.some(t=>t.getAttribute("aria-expanded")==="false")?(this.toggleAll.innerText="Expand all",this.toggleAll.onclick=this.expandAllItems,this.toggleAll.setAttribute("aria-label","Expand all directories"),this.toggleAll.setAttribute("aria-live","polite")):(this.toggleAll.innerText="Collapse all",this.toggleAll.onclick=this.collapseAllItems,this.toggleAll.setAttribute("aria-label","Collapse all directories"),this.toggleAll.setAttribute("aria-live","polite"))}};C();var m=document.querySelector(".js-expandableTable");if(m){let l=new T(m,document.querySelector(".js-expandAllDirectories"));window.location.search.includes("expand-directories")&&l.expandAllItems();let e=document.querySelector(".js-showInternalDirectories");e&&(document.querySelector(".UnitDirectories-internal")&&(e.style.display="block",e.setAttribute("aria-label","Show Internal Directories"),e.setAttribute("aria-describedby","showInternal-description")),e.addEventListener("click",()=>{m.classList.contains("UnitDirectories-showInternal")?(m.classList.remove("UnitDirectories-showInternal"),e.innerText="Show internal",e.setAttribute("aria-label","Show Internal Directories"),e.setAttribute("aria-live","polite"),e.setAttribute("aria-describedby","showInternal-description")):(m.classList.add("UnitDirectories-showInternal"),e.innerText="Hide internal",e.setAttribute("aria-label","Hide Internal Directories"),e.setAttribute("aria-live","polite"),e.setAttribute("aria-describedby","hideInternal-description"))})),document.querySelector('html[data-local="true"]')&&(e==null||e.click())}var S=document.querySelector(".js-tree");if(S){let l=new E(S),e=I(l),t=document.querySelector(".js-mainNavMobile");t&&t.firstElementChild&&(t==null||t.replaceChild(e,t.firstElementChild)),e.firstElementChild&&new f(e.firstElementChild)}var o=document.querySelector(".js-readme"),v=document.querySelector(".js-readmeContent"),w=document.querySelector(".js-readmeOutline"),b=document.querySelectorAll(".js-readmeExpand"),k=document.querySelector(".js-readmeCollapse"),y=document.querySelector(".DocNavMobile-select");o&&v&&w&&b.length&&k&&(o.clientHeight>320&&(o==null||o.classList.remove("UnitReadme--expanded"),o==null||o.classList.add("UnitReadme--toggle")),window.location.hash.includes("readme")&&c(),y==null||y.addEventListener("change",l=>{l.target.value.startsWith("readme-")&&c()}),b.forEach(l=>l.addEventListener("click",e=>{e.preventDefault(),c(),o.scrollIntoView()})),k.addEventListener("click",l=>{l.preventDefault(),o.classList.remove("UnitReadme--expanded"),b[1]&&b[1].scrollIntoView({block:"center"})}),v.addEventListener("keyup",()=>{c()}),v.addEventListener("click",()=>{c()}),w.addEventListener("click",()=>{c()}),document.addEventListener("keydown",l=>{(l.ctrlKey||l.metaKey)&&l.key==="f"&&c()}));function c(){history.replaceState(null,"",`${location.pathname}${location.search}#section-readme`),o==null||o.classList.add("UnitReadme--expanded")}function M(){var t;if(!location.hash)return;let l=document.getElementById(location.hash.slice(1)),e=(t=l==null?void 0:l.parentElement)==null?void 0:t.parentElement;(e==null?void 0:e.nodeName)==="DETAILS"&&(e.open=!0)}M();window.addEventListener("hashchange",()=>M());document.querySelectorAll(".js-buildContextSelect").forEach(l=>{l.addEventListener("change",e=>{window.location.search=`?GOOS=${e.target.value}`})});(()=>{if(!document.querySelector("h4[data-kind]"))return;let l=r=>{var d;if(r.startsWith("pkg-")||r.startsWith("section-")||r.startsWith("hdr-"))return!1;let a=(d=r.split(".").pop())!=null?d:r;return/^[a-z]/.test(a)};document.querySelectorAll("h4[data-kind][id]").forEach(r=>{if(!l(r.id))return;let a=r.closest(".Documentation-function, .Documentation-type, .Documentation-typeFunc, .Documentation-typeMethod");a==null||a.classList.add("Documentation-unexported")}),document.querySelectorAll('.Documentation-indexFunction a[href^="#"], .Documentation-indexType a[href^="#"], .Documentation-indexTypeFunctions a[href^="#"], .Documentation-indexTypeMethods a[href^="#"], .go-Tree a[href^="#"]').forEach(r=>{var a;l(r.getAttribute("href").slice(1))&&((a=r.closest("li"))==null||a.classList.add("Documentation-unexported"))});let e=document.createElement("style");e.textContent="body:not(.show-unexported) .Documentation-unexported{display:none}",document.head.appendChild(e);let t=document.querySelector("#pkg-index");if(!t)return;let i=document.createElement("button");i.type="button",i.className="go-Button go-Button--inline",i.style.marginLeft="0.75rem",i.style.fontSize="0.875rem",i.style.verticalAlign="middle";let s="gogodocs:showUnexported",n=r=>{document.body.classList.toggle("show-unexported",r),i.textContent=r?"Hide unexported":"Show unexported";try{localStorage.setItem(s,r?"1":"0")}catch{}};n(localStorage.getItem(s)==="1"),i.addEventListener("click",()=>n(!document.body.classList.contains("show-unexported"))),t.appendChild(i)})();
     /**
      * @license
      * Copyright 2024 The Go Authors. All rights reserved.
    diff --git a/static/frontend/unit/main/main.js.map b/static/frontend/unit/main/main.js.map
    index 154bedf34..fbe0b88dd 100644
    --- a/static/frontend/unit/main/main.js.map
    +++ b/static/frontend/unit/main/main.js.map
    @@ -1,7 +1,7 @@
     {
       "version": 3,
       "sources": ["../../../shared/base-path/base-path.ts", "../../../shared/playground/playground.ts", "../../../shared/outline/select.ts", "../../../shared/outline/tree.ts", "../../../shared/table/table.ts", "main.ts"],
    -  "sourcesContent": ["/**\n * @license\n * Copyright 2024 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * \u7AD9\u70B9 URL \u5B50\u8DEF\u5F84\u524D\u7F00\uFF0C\u4ECE  \u8BFB\u51FA\u3002\n *\n * fork \u7528 -base-path=/gogodocs \u542F\u52A8\u65F6\uFF0Cserver \u7AEF\u6A21\u677F\u628A attribute \u5199\u6210\n * \"/gogodocs\"\uFF1B\u4E0A\u6E38\u516C\u7F51 pkg.go.dev \u9ED8\u8BA4\u6302\u6839\uFF0Cattribute \u4E3A\u7A7A\u5B57\u7B26\u4E32\u3002\n *\n * \u5728  \u4E4B\u5916\u7684\u811A\u672C\u521D\u59CB\u5316\u9636\u6BB5\u8C03\u7528\u90FD\u5B89\u5168\u2014\u2014documentElement \u4E00\u5B9A\u5B58\u5728\u3002\n */\nexport function getBasePath(): string {\n  return document.documentElement.dataset.basePath ?? '';\n}\n\n/**\n * \u7ED9\u4EE5 / \u5F00\u5934\u7684\u7EDD\u5BF9\u8DEF\u5F84\u52A0 BasePath \u524D\u7F00\u3002\n *\n * \u7528\u4E8E fetch URL / innerHTML src / location \u6BD4\u8F83\u7B49\u4EFB\u4F55\u9700\u8981\u62FC\u7AD9\u70B9\u7EDD\u5BF9 path\n * \u7684\u5730\u65B9\u3002\u975E\u7EDD\u5BF9\u8DEF\u5F84\uFF08\u4E0D\u4EE5 / \u5F00\u5934\uFF09\u539F\u6837\u8FD4\u56DE\u2014\u2014caller \u81EA\u5DF1\u4FDD\u8BC1\u8BED\u4E49\u3002\n *\n * \u4F8B\uFF1A\n *   abs('/play/share')       // \u2192 '/gogodocs/play/share' \u6216 '/play/share'\n *   abs('/static/foo.svg')   // \u2192 '/gogodocs/static/foo.svg' \u6216 '/static/foo.svg'\n *   abs('relative/x')        // \u2192 'relative/x'\uFF08\u4E0D\u52A8\uFF09\n */\nexport function abs(p: string): string {\n  if (!p.startsWith('/')) return p;\n  return getBasePath() + p;\n}\n", "/*!\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n// This file implements the playground implementation of the documentation\n// page. The playground involves a \"play\" button that allows you to open up\n// a new link to play.golang.org using the example code.\n\n// The CSS is in static/frontend/unit/main/_doc.css\n\nimport { abs } from '../base-path/base-path';\n\n/**\n * CSS classes used by PlaygroundExampleController\n */\nconst PlayExampleClassName = {\n  PLAY_HREF: '.js-exampleHref',\n  PLAY_CONTAINER: '.js-exampleContainer',\n  EXAMPLE_INPUT: '.Documentation-exampleCode',\n  EXAMPLE_OUTPUT: '.Documentation-exampleOutput',\n  EXAMPLE_OUTPUT_CONTAINER: '.js-exampleOutputContainer',\n  EXAMPLE_ERROR: '.Documentation-exampleError',\n  PLAY_BUTTON: '.Documentation-examplePlayButton',\n  SHARE_BUTTON: '.Documentation-exampleShareButton',\n  FORMAT_BUTTON: '.Documentation-exampleFormatButton',\n  RUN_BUTTON: '.Documentation-exampleRunButton',\n};\n\n/**\n * This controller enables playground examples to expand their dropdown or\n * generate shareable Go Playground URLs.\n */\nexport class PlaygroundExampleController {\n  /**\n   * The anchor tag used to identify the container with an example href.\n   * There is only one in an example container div.\n   */\n  private readonly anchorEl: HTMLAnchorElement | null;\n\n  /**\n   * The error element\n   */\n  private readonly errorEl: Element | null;\n\n  /**\n   * Buttons that redirect to an example's playground, this element\n   * only exists in executable examples.\n   */\n  private readonly playButtonEl: Element | null;\n  private readonly shareButtonEl: Element | null;\n\n  /**\n   * Button that formats the code in an example's playground.\n   */\n  private readonly formatButtonEl: Element | null;\n\n  /**\n   * Button that runs the code in an example's playground, this element\n   * only exists in executable examples.\n   */\n  private readonly runButtonEl: Element | null;\n\n  /**\n   * The executable code of an example.\n   */\n  private readonly inputEl: HTMLTextAreaElement | null;\n\n  /**\n   * The output of the given example code. This only exists if the\n   * author of the package provides an output for this example.\n   */\n  private readonly outputEl: Element | null;\n\n  /**\n   * The container for the output.\n   */\n  private readonly outputContainerEl: HTMLElement | null;\n\n  /**\n   * @param exampleEl The div that contains playground content for the given example.\n   */\n  constructor(private readonly exampleEl: HTMLDetailsElement) {\n    this.exampleEl = exampleEl;\n    this.anchorEl = exampleEl.querySelector('a');\n    this.errorEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_ERROR);\n    this.playButtonEl = exampleEl.querySelector(PlayExampleClassName.PLAY_BUTTON);\n    this.shareButtonEl = exampleEl.querySelector(PlayExampleClassName.SHARE_BUTTON);\n    this.formatButtonEl = exampleEl.querySelector(PlayExampleClassName.FORMAT_BUTTON);\n    this.runButtonEl = exampleEl.querySelector(PlayExampleClassName.RUN_BUTTON);\n    this.inputEl = this.makeTextArea(exampleEl.querySelector(PlayExampleClassName.EXAMPLE_INPUT));\n    this.outputEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_OUTPUT);\n    this.outputContainerEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_OUTPUT_CONTAINER);\n\n    // This is legacy listener to be replaced the listener for shareButtonEl.\n    this.playButtonEl?.addEventListener('click', () => this.handleShareButtonClick());\n    this.shareButtonEl?.addEventListener('click', () => this.handleShareButtonClick());\n    this.formatButtonEl?.addEventListener('click', () => this.handleFormatButtonClick());\n    this.runButtonEl?.addEventListener('click', () => this.handleRunButtonClick());\n\n    if (!this.inputEl) return;\n\n    this.resize();\n    this.inputEl.addEventListener('keyup', () => this.resize());\n    this.inputEl.addEventListener('keydown', e => this.onKeydown(e));\n  }\n\n  /**\n   * Replace the pre element with a textarea. The examples are initially rendered\n   * as pre elements so they're fully visible when JS is disabled.\n   */\n  makeTextArea(el: Element | null): HTMLTextAreaElement {\n    const t = document.createElement('textarea');\n    t.classList.add('Documentation-exampleCode', 'code');\n    t.spellcheck = false;\n    t.value = el?.textContent ?? '';\n    el?.parentElement?.replaceChild(t, el);\n    return t;\n  }\n\n  /**\n   * Retrieve the hash value of the anchor element.\n   */\n  getAnchorHash(): string | undefined {\n    return this.anchorEl?.hash;\n  }\n\n  /**\n   * Expands the current playground example.\n   */\n  expand(): void {\n    this.exampleEl.open = true;\n  }\n\n  /**\n   * Resizes the input element to accommodate the amount of text present.\n   */\n  private resize(): void {\n    if (this.inputEl?.value) {\n      const numLineBreaks = (this.inputEl.value.match(/\\n/g) || []).length;\n      // min-height + lines x line-height + padding + border\n      this.inputEl.style.height = `${(20 + numLineBreaks * 20 + 12 + 2) / 16}rem`;\n    }\n  }\n\n  /**\n   * Handler to override keyboard behavior in the playground's\n   * textarea element.\n   *\n   * Tab key inserts tabs into the example playground instead of\n   * switching to the next interactive element.\n   * @param e input element keyboard event.\n   */\n  private onKeydown(e: KeyboardEvent) {\n    if (e.key === 'Tab') {\n      document.execCommand('insertText', false, '\\t');\n      e.preventDefault();\n    }\n  }\n\n  /**\n   * Changes the text of the example's input box.\n   */\n  private setInputText(output: string) {\n    if (this.inputEl) {\n      this.inputEl.value = output;\n    }\n  }\n\n  /**\n   * Changes the text of the example's output box.\n   */\n  private setOutputText(output: string) {\n    if (this.outputEl) {\n      this.outputEl.textContent = output;\n    }\n    if (this.outputContainerEl) {\n      this.outputContainerEl.hidden = false;\n    }\n  }\n\n  /**\n   * Appends to the text of the example's output box.\n   */\n  private appendToOutputText(output: string) {\n    if (this.outputEl) {\n      this.outputEl.textContent += output;\n    }\n    if (this.outputContainerEl) {\n      this.outputContainerEl.hidden = false;\n    }\n  }\n\n  private setOutputHTML(output: string) {\n    if (this.outputEl) {\n      this.outputEl.innerHTML = output;\n    }\n  }\n\n  /**\n   * Sets the error message text and overwrites\n   * output box to indicate a failed response.\n   */\n  private setErrorText(err: string) {\n    if (this.errorEl) {\n      this.errorEl.textContent = err;\n    }\n    this.setOutputText('An error has occurred\u2026');\n  }\n\n  private getCodeWithModFile(): string {\n    let codeWithModFile = this.inputEl?.value ?? '';\n    const moduleVars = document.querySelector('.js-playgroundVars')?.dataset ?? {};\n    if (moduleVars.modulepath !== 'std') {\n      codeWithModFile = codeWithModFile.concat(`\n-- go.mod --\nmodule play.ground\n\nrequire ${moduleVars.modulepath} ${moduleVars.version}\n`);\n    }\n\n    return codeWithModFile;\n  }\n\n  /**\n   * Opens a new window to play.golang.org using the\n   * example snippet's code in the playground.\n   */\n  private handleShareButtonClick() {\n    const PLAYGROUND_BASE_URL = 'https://play.golang.org/p/';\n\n    this.setOutputText('Waiting for remote server\u2026');\n\n    fetch(abs('/play/share'), {\n      method: 'POST',\n      body: this.getCodeWithModFile(),\n    })\n      .then(res => res.text())\n      .then(shareId => {\n        const href = PLAYGROUND_BASE_URL + shareId;\n        this.setOutputHTML(`${href}`);\n        window.open(href);\n      })\n      .catch(err => {\n        this.setErrorText(err);\n      });\n  }\n\n  /**\n   * Runs gofmt on the example snippet in the playground.\n   */\n  private handleFormatButtonClick() {\n    this.setOutputText('Waiting for remote server\u2026');\n    const body = new FormData();\n    body.append('body', this.inputEl?.value ?? '');\n\n    fetch(abs('/play/fmt'), {\n      method: 'POST',\n      body: body,\n    })\n      .then(res => res.json())\n      .then(({ Body, Error }) => {\n        this.setOutputText(Error || 'Done.');\n        if (Body) {\n          this.setInputText(Body);\n          this.resize();\n        }\n      })\n      .catch(err => {\n        this.setErrorText(err);\n      });\n  }\n\n  /**\n   * Runs the code snippet in the example playground.\n   */\n  private handleRunButtonClick() {\n    this.setOutputText('Waiting for remote server\u2026');\n\n    fetch(abs('/play/compile'), {\n      method: 'POST',\n      body: JSON.stringify({ body: this.getCodeWithModFile(), version: 2 }),\n    })\n      .then(res => res.json())\n      .then(async ({ Events, Errors }) => {\n        this.setOutputText(Errors || '');\n        for (const e of Events || []) {\n          this.appendToOutputText(e.Message);\n          await new Promise(resolve => setTimeout(resolve, e.Delay / 1000000));\n        }\n      })\n      .catch(err => {\n        this.setErrorText(err);\n      });\n  }\n}\n\nexport function initPlaygrounds(): void {\n  const exampleHashRegex = location.hash.match(/^#(example-.*)$/);\n  if (exampleHashRegex) {\n    const exampleHashEl = document.getElementById(exampleHashRegex[1]) as HTMLDetailsElement;\n    if (exampleHashEl) {\n      exampleHashEl.open = true;\n    }\n  }\n\n  // We use a spread operator to convert a nodelist into an array of elements.\n  const exampleHrefs = [\n    ...document.querySelectorAll(PlayExampleClassName.PLAY_HREF),\n  ];\n\n  /**\n   * Sometimes exampleHrefs and playContainers are in different order, so we\n   * find an exampleHref from a common hash.\n   * @param playContainer - playground container\n   */\n  const findExampleHash = (playContainer: PlaygroundExampleController) =>\n    exampleHrefs.find(ex => {\n      return ex.hash === playContainer.getAnchorHash();\n    });\n\n  for (const el of document.querySelectorAll(PlayExampleClassName.PLAY_CONTAINER)) {\n    // There should be the same amount of hrefs referencing examples as example containers.\n    const playContainer = new PlaygroundExampleController(el as HTMLDetailsElement);\n    const exampleHref = findExampleHash(playContainer);\n    if (exampleHref) {\n      exampleHref.addEventListener('click', () => {\n        playContainer.expand();\n      });\n    } else {\n      console.warn('example href not found');\n    }\n  }\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { TreeNavController } from './tree.js';\n\nexport class SelectNavController {\n  constructor(private el: Element) {\n    this.el.addEventListener('change', e => {\n      const target = e.target as HTMLSelectElement;\n      let href = target.value;\n      if (!target.value.startsWith('/')) {\n        href = '/' + href;\n      }\n      window.location.href = href;\n    });\n  }\n}\n\nexport function makeSelectNav(tree: TreeNavController): HTMLLabelElement {\n  const label = document.createElement('label');\n  label.classList.add('go-Label');\n  label.setAttribute('aria-label', 'Menu');\n  const select = document.createElement('select');\n  select.classList.add('go-Select', 'js-selectNav');\n  label.appendChild(select);\n  const outline = document.createElement('optgroup');\n  outline.label = 'Outline';\n  select.appendChild(outline);\n  const groupMap: Record = {};\n  let group: HTMLOptGroupElement;\n  for (const t of tree.treeitems) {\n    if (Number(t.depth) > 4) continue;\n    if (t.groupTreeitem) {\n      group = groupMap[t.groupTreeitem.label];\n      if (!group) {\n        group = groupMap[t.groupTreeitem.label] = document.createElement('optgroup');\n        group.label = t.groupTreeitem.label;\n        select.appendChild(group);\n      }\n    } else {\n      group = outline;\n    }\n    const o = document.createElement('option');\n    o.label = t.label;\n    o.textContent = t.label;\n    o.value = (t.el as HTMLAnchorElement).href.replace(window.location.origin, '').replace('/', '');\n    group.appendChild(o);\n  }\n  tree.addObserver(t => {\n    const hash = (t.el as HTMLAnchorElement).hash;\n    const value = select.querySelector(`[value$=\"${hash}\"]`)?.value;\n    if (value) {\n      select.value = value;\n    }\n  }, 50);\n  return label;\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * TreeNavController is the navigation tree component of the documentation page.\n * It adds accessiblity attributes to a tree, observes the heading elements\n * focus the topmost link for headings visible on the page, and implements the\n * WAI-ARIA Treeview Design Pattern with full\n * [keyboard support](https://www.w3.org/TR/wai-aria-practices/examples/treeview/treeview-2/treeview-2a.html#kbd_label).\n */\nexport class TreeNavController {\n  treeitems: TreeItem[];\n\n  /**\n   * firstChars is the first character of each treeitem in the same order\n   * as this.treeitems. We use this array to set focus by character when\n   * navigating the tree with a keyboard.\n   */\n  private firstChars: string[];\n  private firstTreeitem: TreeItem | null;\n  private lastTreeitem: TreeItem | null;\n  private observerCallbacks: ((t: TreeItem) => void)[];\n\n  constructor(private el: HTMLElement) {\n    this.treeitems = [];\n    this.firstChars = [];\n    this.firstTreeitem = null;\n    this.lastTreeitem = null;\n    this.observerCallbacks = [];\n    this.init();\n  }\n\n  private init(): void {\n    this.handleResize();\n    window.addEventListener('resize', this.handleResize);\n    this.findTreeItems();\n    this.updateVisibleTreeitems();\n    this.observeTargets();\n    if (this.firstTreeitem) {\n      this.firstTreeitem.el.tabIndex = 0;\n    }\n  }\n\n  private handleResize = (): void => {\n    this.el.style.setProperty('--js-tree-height', '100vh');\n    this.el.style.setProperty('--js-tree-height', this.el.clientHeight + 'px');\n  };\n\n  private observeTargets() {\n    this.addObserver(treeitem => {\n      this.expandTreeitem(treeitem);\n      this.setSelected(treeitem);\n      // TODO: Fix scroll issue in https://golang.org/issue/47450.\n      // treeitem.el.scrollIntoView({ block: 'nearest' });\n    });\n\n    const targets = new Map();\n    const observer = new IntersectionObserver(\n      entries => {\n        for (const entry of entries) {\n          targets.set(entry.target.id, entry.isIntersecting || entry.intersectionRatio === 1);\n        }\n        for (const [id, isIntersecting] of targets) {\n          if (isIntersecting) {\n            const active = this.treeitems.find(t =>\n              (t.el as HTMLAnchorElement)?.href.endsWith(`#${id}`)\n            );\n            if (active) {\n              for (const fn of this.observerCallbacks) {\n                fn(active);\n              }\n            }\n            break;\n          }\n        }\n      },\n      {\n        threshold: 1.0,\n        rootMargin: '-60px 0px 0px 0px',\n      }\n    );\n\n    for (const href of this.treeitems.map(t => t.el.getAttribute('href'))) {\n      if (href) {\n        const id = href.replace(window.location.origin, '').replace('/', '').replace('#', '');\n        const target = document.getElementById(id);\n        if (target && !id.startsWith('example-')) {\n          observer.observe(target);\n        }\n      }\n    }\n  }\n\n  addObserver(fn: (t: TreeItem) => void, delay = 200): void {\n    this.observerCallbacks.push(debounce(fn, delay));\n  }\n\n  setFocusToNextItem(currentItem: TreeItem): void {\n    let nextItem = null;\n    for (let i = currentItem.index + 1; i < this.treeitems.length; i++) {\n      const ti = this.treeitems[i];\n      if (ti.isVisible) {\n        nextItem = ti;\n        break;\n      }\n    }\n    if (nextItem) {\n      this.setFocusToItem(nextItem);\n    }\n  }\n\n  setFocusToPreviousItem(currentItem: TreeItem): void {\n    let prevItem = null;\n    for (let i = currentItem.index - 1; i > -1; i--) {\n      const ti = this.treeitems[i];\n      if (ti.isVisible) {\n        prevItem = ti;\n        break;\n      }\n    }\n    if (prevItem) {\n      this.setFocusToItem(prevItem);\n    }\n  }\n\n  setFocusToParentItem(currentItem: TreeItem): void {\n    if (currentItem.groupTreeitem) {\n      this.setFocusToItem(currentItem.groupTreeitem);\n    }\n  }\n\n  setFocusToFirstItem(): void {\n    this.firstTreeitem && this.setFocusToItem(this.firstTreeitem);\n  }\n\n  setFocusToLastItem(): void {\n    this.lastTreeitem && this.setFocusToItem(this.lastTreeitem);\n  }\n\n  setSelected(currentItem: TreeItem): void {\n    for (const l1 of this.el.querySelectorAll('[aria-expanded=\"true\"]')) {\n      if (l1 === currentItem.el) continue;\n      if (!l1.nextElementSibling?.contains(currentItem.el)) {\n        l1.setAttribute('aria-expanded', 'false');\n      }\n    }\n    for (const l1 of this.el.querySelectorAll('[aria-selected]')) {\n      if (l1 !== currentItem.el) {\n        l1.setAttribute('aria-selected', 'false');\n      }\n    }\n    currentItem.el.setAttribute('aria-selected', 'true');\n    this.updateVisibleTreeitems();\n    this.setFocusToItem(currentItem, false);\n  }\n\n  expandTreeitem(treeitem: TreeItem): void {\n    let currentItem: TreeItem | null = treeitem;\n    while (currentItem) {\n      if (currentItem.isExpandable) {\n        currentItem.el.setAttribute('aria-expanded', 'true');\n      }\n      currentItem = currentItem.groupTreeitem;\n    }\n    this.updateVisibleTreeitems();\n  }\n\n  expandAllSiblingItems(currentItem: TreeItem): void {\n    for (const ti of this.treeitems) {\n      if (ti.groupTreeitem === currentItem.groupTreeitem && ti.isExpandable) {\n        this.expandTreeitem(ti);\n      }\n    }\n  }\n\n  collapseTreeitem(currentItem: TreeItem): void {\n    let groupTreeitem = null;\n\n    if (currentItem.isExpanded()) {\n      groupTreeitem = currentItem;\n    } else {\n      groupTreeitem = currentItem.groupTreeitem;\n    }\n\n    if (groupTreeitem) {\n      groupTreeitem.el.setAttribute('aria-expanded', 'false');\n      this.updateVisibleTreeitems();\n      this.setFocusToItem(groupTreeitem);\n    }\n  }\n\n  setFocusByFirstCharacter(currentItem: TreeItem, char: string): void {\n    let start: number, index: number;\n    char = char.toLowerCase();\n\n    // Get start index for search based on position of currentItem\n    start = currentItem.index + 1;\n    if (start === this.treeitems.length) {\n      start = 0;\n    }\n\n    // Check remaining slots in the menu\n    index = this.getIndexFirstChars(start, char);\n\n    // If not found in remaining slots, check from beginning\n    if (index === -1) {\n      index = this.getIndexFirstChars(0, char);\n    }\n\n    // If match was found...\n    if (index > -1) {\n      this.setFocusToItem(this.treeitems[index]);\n    }\n  }\n\n  private findTreeItems() {\n    const findItems = (el: HTMLElement, group: TreeItem | null) => {\n      let ti = group;\n      let curr = el.firstElementChild as HTMLElement;\n      while (curr) {\n        if (curr.tagName === 'A' || curr.tagName === 'SPAN') {\n          ti = new TreeItem(curr, this, group);\n          this.treeitems.push(ti);\n          this.firstChars.push(ti.label.substring(0, 1).toLowerCase());\n        }\n        if (curr.firstElementChild) {\n          findItems(curr, ti);\n        }\n        curr = curr.nextElementSibling as HTMLElement;\n      }\n    };\n    findItems(this.el as HTMLElement, null);\n    this.treeitems.map((ti, idx) => (ti.index = idx));\n  }\n\n  private updateVisibleTreeitems(): void {\n    this.firstTreeitem = this.treeitems[0];\n\n    for (const ti of this.treeitems) {\n      let parent = ti.groupTreeitem;\n      ti.isVisible = true;\n      while (parent && parent.el !== this.el) {\n        if (!parent.isExpanded()) {\n          ti.isVisible = false;\n        }\n        parent = parent.groupTreeitem;\n      }\n      if (ti.isVisible) {\n        this.lastTreeitem = ti;\n      }\n    }\n  }\n\n  private setFocusToItem(treeitem: TreeItem, focusEl = true) {\n    treeitem.el.tabIndex = 0;\n    if (focusEl) {\n      treeitem.el.focus();\n    }\n    for (const ti of this.treeitems) {\n      if (ti !== treeitem) {\n        ti.el.tabIndex = -1;\n      }\n    }\n  }\n\n  private getIndexFirstChars(startIndex: number, char: string): number {\n    for (let i = startIndex; i < this.firstChars.length; i++) {\n      if (this.treeitems[i].isVisible && char === this.firstChars[i]) {\n        return i;\n      }\n    }\n    return -1;\n  }\n}\n\nclass TreeItem {\n  el: HTMLElement;\n  groupTreeitem: TreeItem | null;\n  label: string;\n  isExpandable: boolean;\n  isVisible: boolean;\n  depth: number;\n  index: number;\n\n  private tree: TreeNavController;\n  private isInGroup: boolean;\n\n  constructor(el: HTMLElement, treeObj: TreeNavController, group: TreeItem | null) {\n    el.tabIndex = -1;\n    this.el = el;\n    this.groupTreeitem = group;\n    this.label = el.textContent?.trim() ?? '';\n    this.tree = treeObj;\n    this.depth = (group?.depth || 0) + 1;\n    this.index = 0;\n\n    const parent = el.parentElement;\n    if (parent?.tagName.toLowerCase() === 'li') {\n      parent?.setAttribute('role', 'none');\n    }\n    el.setAttribute('aria-level', this.depth + '');\n    if (el.getAttribute('aria-label')) {\n      this.label = el?.getAttribute('aria-label')?.trim() ?? '';\n    }\n\n    this.isExpandable = false;\n    this.isVisible = false;\n    this.isInGroup = !!group;\n\n    let curr = el.nextElementSibling;\n    while (curr) {\n      if (curr.tagName.toLowerCase() == 'ul') {\n        const groupId = `${group?.label ?? ''} nav group ${this.label}`.replace(/[\\W_]+/g, '_');\n        el.setAttribute('aria-owns', groupId);\n        el.setAttribute('aria-expanded', 'false');\n        curr.setAttribute('role', 'group');\n        curr.setAttribute('id', groupId);\n        this.isExpandable = true;\n        break;\n      }\n\n      curr = curr.nextElementSibling;\n    }\n    this.init();\n  }\n\n  private init() {\n    this.el.tabIndex = -1;\n    if (!this.el.getAttribute('role')) {\n      this.el.setAttribute('role', 'treeitem');\n    }\n    this.el.addEventListener('keydown', this.handleKeydown.bind(this));\n    this.el.addEventListener('click', this.handleClick.bind(this));\n    this.el.addEventListener('focus', this.handleFocus.bind(this));\n    this.el.addEventListener('blur', this.handleBlur.bind(this));\n  }\n\n  isExpanded() {\n    if (this.isExpandable) {\n      return this.el.getAttribute('aria-expanded') === 'true';\n    }\n\n    return false;\n  }\n\n  isSelected() {\n    return this.el.getAttribute('aria-selected') === 'true';\n  }\n\n  private handleClick(event: MouseEvent) {\n    // only process click events that directly happened on this treeitem\n    if (event.target !== this.el && event.target !== this.el.firstElementChild) {\n      return;\n    }\n    if (this.isExpandable) {\n      if (this.isExpanded() && this.isSelected()) {\n        this.tree.collapseTreeitem(this);\n      } else {\n        this.tree.expandTreeitem(this);\n      }\n      event.stopPropagation();\n    }\n    this.tree.setSelected(this);\n  }\n\n  private handleFocus() {\n    let el = this.el;\n    if (this.isExpandable) {\n      el = (el.firstElementChild as HTMLElement) ?? el;\n    }\n    el.classList.add('focus');\n  }\n\n  private handleBlur() {\n    let el = this.el;\n    if (this.isExpandable) {\n      el = (el.firstElementChild as HTMLElement) ?? el;\n    }\n    el.classList.remove('focus');\n  }\n\n  private handleKeydown(event: KeyboardEvent) {\n    if (event.altKey || event.ctrlKey || event.metaKey) {\n      return;\n    }\n\n    let captured = false;\n    switch (event.key) {\n      case ' ':\n      case 'Enter':\n        if (this.isExpandable) {\n          if (this.isExpanded() && this.isSelected()) {\n            this.tree.collapseTreeitem(this);\n          } else {\n            this.tree.expandTreeitem(this);\n          }\n          captured = true;\n        } else {\n          event.stopPropagation();\n        }\n        this.tree.setSelected(this);\n        break;\n\n      case 'ArrowUp':\n        this.tree.setFocusToPreviousItem(this);\n        captured = true;\n        break;\n\n      case 'ArrowDown':\n        this.tree.setFocusToNextItem(this);\n        captured = true;\n        break;\n\n      case 'ArrowRight':\n        if (this.isExpandable) {\n          if (this.isExpanded()) {\n            this.tree.setFocusToNextItem(this);\n          } else {\n            this.tree.expandTreeitem(this);\n          }\n        }\n        captured = true;\n        break;\n\n      case 'ArrowLeft':\n        if (this.isExpandable && this.isExpanded()) {\n          this.tree.collapseTreeitem(this);\n          captured = true;\n        } else {\n          if (this.isInGroup) {\n            this.tree.setFocusToParentItem(this);\n            captured = true;\n          }\n        }\n        break;\n\n      case 'Home':\n        this.tree.setFocusToFirstItem();\n        captured = true;\n        break;\n\n      case 'End':\n        this.tree.setFocusToLastItem();\n        captured = true;\n        break;\n\n      default:\n        if (event.key.length === 1 && event.key.match(/\\S/)) {\n          if (event.key == '*') {\n            this.tree.expandAllSiblingItems(this);\n          } else {\n            this.tree.setFocusByFirstCharacter(this, event.key);\n          }\n          captured = true;\n        }\n        break;\n    }\n\n    if (captured) {\n      event.stopPropagation();\n      event.preventDefault();\n    }\n  }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction debounce any>(func: T, wait: number) {\n  let timeout: ReturnType | null;\n  return (...args: Parameters) => {\n    const later = () => {\n      timeout = null;\n      func(...args);\n    };\n    if (timeout) {\n      clearTimeout(timeout);\n    }\n    timeout = setTimeout(later, wait);\n  };\n}\n", "/*!\n * @license\n * Copyright 2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * Controller for a table element with expandable rows. Adds event listeners to\n * a toggle within a table row that controls visiblity of additional related\n * rows in the table.\n *\n * @example\n * ```typescript\n * import {ExpandableRowsTableController} from '/static/js/table';\n *\n * const el = document .querySelector('.js-myTableElement')\n * new ExpandableRowsTableController(el));\n * ```\n */\nexport class ExpandableRowsTableController {\n  private rows: HTMLTableRowElement[];\n  private toggles: HTMLButtonElement[];\n\n  /**\n   * Create a table controller.\n   * @param table - The table element to which the controller binds.\n   */\n  constructor(private table: HTMLTableElement, private toggleAll?: HTMLButtonElement | null) {\n    this.rows = Array.from(table.querySelectorAll('[data-aria-controls]'));\n    this.toggles = Array.from(this.table.querySelectorAll('[aria-expanded]'));\n    this.setAttributes();\n    this.attachEventListeners();\n    this.update();\n  }\n\n  /**\n   * setAttributes sets data-aria-* and data-id attributes to regular\n   * html attributes as a workaround for limitations from safehtml.\n   */\n  private setAttributes() {\n    for (const a of ['data-aria-controls', 'data-aria-labelledby', 'data-id']) {\n      this.table.querySelectorAll(`[${a}]`).forEach(t => {\n        t.setAttribute(a.replace('data-', ''), t.getAttribute(a) ?? '');\n        t.removeAttribute(a);\n      });\n    }\n  }\n\n  private attachEventListeners() {\n    this.rows.forEach(t => {\n      t.addEventListener('click', e => {\n        this.handleToggleClick(e);\n      });\n    });\n    this.toggleAll?.addEventListener('click', () => {\n      this.expandAllItems();\n    });\n\n    document.addEventListener('keydown', e => {\n      if ((e.ctrlKey || e.metaKey) && e.key === 'f') {\n        this.expandAllItems();\n      }\n    });\n  }\n\n  private handleToggleClick(e: MouseEvent) {\n    let target = e.currentTarget as HTMLTableRowElement | null;\n    if (!target?.hasAttribute('aria-expanded')) {\n      target = this.table.querySelector(\n        `button[aria-controls=\"${target?.getAttribute('aria-controls')}\"]`\n      );\n    }\n    const isExpanded = target?.getAttribute('aria-expanded') === 'true';\n    target?.setAttribute('aria-expanded', isExpanded ? 'false' : 'true');\n    e.stopPropagation();\n    this.update();\n  }\n\n  expandAllItems = (): void => {\n    this.toggles.map(t => t.setAttribute('aria-expanded', 'true'));\n    this.update();\n  };\n\n  private collapseAllItems = () => {\n    this.toggles.map(t => t.setAttribute('aria-expanded', 'false'));\n    this.update();\n  };\n\n  private update = () => {\n    this.updateVisibleItems();\n    setTimeout(() => this.updateGlobalToggle());\n  };\n\n  private updateVisibleItems() {\n    this.rows.map(t => {\n      const isExpanded = t?.getAttribute('aria-expanded') === 'true';\n      const rowIds = t?.getAttribute('aria-controls')?.trimEnd().split(' ');\n      rowIds?.map(id => {\n        const target = document.getElementById(`${id}`);\n        if (isExpanded) {\n          target?.classList.add('visible');\n          target?.classList.remove('hidden');\n        } else {\n          target?.classList.add('hidden');\n          target?.classList.remove('visible');\n        }\n      });\n    });\n  }\n\n  private updateGlobalToggle() {\n    if (!this.toggleAll) return;\n    if (this.rows.some(t => t.hasAttribute('aria-expanded'))) {\n      this.toggleAll.style.display = 'block';\n    }\n    const someCollapsed = this.toggles.some(el => el.getAttribute('aria-expanded') === 'false');\n    if (someCollapsed) {\n      this.toggleAll.innerText = 'Expand all';\n      this.toggleAll.onclick = this.expandAllItems;\n      this.toggleAll.setAttribute('aria-label', 'Expand all directories');\n      this.toggleAll.setAttribute('aria-live', 'polite');\n    } else {\n      this.toggleAll.innerText = 'Collapse all';\n      this.toggleAll.onclick = this.collapseAllItems;\n      this.toggleAll.setAttribute('aria-label', 'Collapse all directories');\n      this.toggleAll.setAttribute('aria-live', 'polite');\n    }\n  }\n}\n", "import { initPlaygrounds } from 'static/shared/playground/playground';\nimport { SelectNavController, makeSelectNav } from 'static/shared/outline/select';\nimport { TreeNavController } from 'static/shared/outline/tree';\nimport { ExpandableRowsTableController } from 'static/shared/table/table';\n\ninitPlaygrounds();\n\nconst directories = document.querySelector('.js-expandableTable');\nif (directories) {\n  const table = new ExpandableRowsTableController(\n    directories,\n    document.querySelector('.js-expandAllDirectories')\n  );\n  // Expand directories on page load with expand-directories query param.\n  if (window.location.search.includes('expand-directories')) {\n    table.expandAllItems();\n  }\n\n  const internalToggle = document.querySelector('.js-showInternalDirectories');\n  if (internalToggle) {\n    if (document.querySelector('.UnitDirectories-internal')) {\n      internalToggle.style.display = 'block';\n      internalToggle.setAttribute('aria-label', 'Show Internal Directories');\n      internalToggle.setAttribute('aria-describedby', 'showInternal-description');\n    }\n    internalToggle.addEventListener('click', () => {\n      if (directories.classList.contains('UnitDirectories-showInternal')) {\n        directories.classList.remove('UnitDirectories-showInternal');\n        internalToggle.innerText = 'Show internal';\n        internalToggle.setAttribute('aria-label', 'Show Internal Directories');\n        internalToggle.setAttribute('aria-live', 'polite');\n        internalToggle.setAttribute('aria-describedby', 'showInternal-description');\n      } else {\n        directories.classList.add('UnitDirectories-showInternal');\n        internalToggle.innerText = 'Hide internal';\n        internalToggle.setAttribute('aria-label', 'Hide Internal Directories');\n        internalToggle.setAttribute('aria-live', 'polite');\n        internalToggle.setAttribute('aria-describedby', 'hideInternal-description');\n      }\n    });\n  }\n  if (document.querySelector('html[data-local=\"true\"]')) {\n    internalToggle?.click();\n  }\n}\n\nconst treeEl = document.querySelector('.js-tree');\nif (treeEl) {\n  const treeCtrl = new TreeNavController(treeEl);\n  const select = makeSelectNav(treeCtrl);\n  const mobileNav = document.querySelector('.js-mainNavMobile');\n  if (mobileNav && mobileNav.firstElementChild) {\n    mobileNav?.replaceChild(select, mobileNav.firstElementChild);\n  }\n  if (select.firstElementChild) {\n    new SelectNavController(select.firstElementChild);\n  }\n}\n\n/**\n * Event handlers for expanding and collapsing the readme section.\n */\nconst readme = document.querySelector('.js-readme');\nconst readmeContent = document.querySelector('.js-readmeContent');\nconst readmeOutline = document.querySelector('.js-readmeOutline');\nconst readmeExpand = document.querySelectorAll('.js-readmeExpand');\nconst readmeCollapse = document.querySelector('.js-readmeCollapse');\nconst mobileNavSelect = document.querySelector('.DocNavMobile-select');\nif (readme && readmeContent && readmeOutline && readmeExpand.length && readmeCollapse) {\n  if (readme.clientHeight > 320) {\n    readme?.classList.remove('UnitReadme--expanded');\n    readme?.classList.add('UnitReadme--toggle');\n  }\n  if (window.location.hash.includes('readme')) {\n    expandReadme();\n  }\n  mobileNavSelect?.addEventListener('change', e => {\n    if ((e.target as HTMLSelectElement).value.startsWith('readme-')) {\n      expandReadme();\n    }\n  });\n  readmeExpand.forEach(el =>\n    el.addEventListener('click', e => {\n      e.preventDefault();\n      expandReadme();\n      readme.scrollIntoView();\n    })\n  );\n  readmeCollapse.addEventListener('click', e => {\n    e.preventDefault();\n    readme.classList.remove('UnitReadme--expanded');\n    if (readmeExpand[1]) {\n      readmeExpand[1].scrollIntoView({ block: 'center' });\n    }\n  });\n  readmeContent.addEventListener('keyup', () => {\n    expandReadme();\n  });\n  readmeContent.addEventListener('click', () => {\n    expandReadme();\n  });\n  readmeOutline.addEventListener('click', () => {\n    expandReadme();\n  });\n  document.addEventListener('keydown', e => {\n    if ((e.ctrlKey || e.metaKey) && e.key === 'f') {\n      expandReadme();\n    }\n  });\n}\n\n/**\n * expandReadme expands the readme and adds the section-readme hash to the\n * URL so it stays expanded when navigating back from an external link.\n */\nfunction expandReadme() {\n  history.replaceState(null, '', `${location.pathname}${location.search}#section-readme`);\n  readme?.classList.add('UnitReadme--expanded');\n}\n\n/**\n * Expand details items that are focused. This will expand\n * deprecated symbols when they are navigated to from the index\n * or a direct link.\n */\nfunction openDeprecatedSymbol() {\n  if (!location.hash) return;\n  const heading = document.getElementById(location.hash.slice(1));\n  const grandParent = heading?.parentElement?.parentElement as HTMLDetailsElement | null;\n  if (grandParent?.nodeName === 'DETAILS') {\n    grandParent.open = true;\n  }\n}\nopenDeprecatedSymbol();\nwindow.addEventListener('hashchange', () => openDeprecatedSymbol());\n\n/**\n * Listen for changes in the build context dropdown.\n */\ndocument.querySelectorAll('.js-buildContextSelect').forEach(el => {\n  el.addEventListener('change', e => {\n    window.location.search = `?GOOS=${(e.target as HTMLSelectElement).value}`;\n  });\n});\n\n/**\n * fork\uFF1Aunexported\uFF08\u79C1\u6709\uFF09\u7B26\u53F7 toggle\u3002\n *\n * \u80CC\u666F\uFF1Apkgsite -show-unexported flag \u8BA9 godoc \u628A\u79C1\u6709 type/func/method\n * \u90FD\u6E32\u5230 page\u3002\u4F46\u8BFB\u8005\u5927\u591A\u6570\u65F6\u5019\u53EA\u5173\u5FC3 public API\uFF0C\u79C1\u6709\u7684\u592A\u591A\u53CD\u800C\u62D6\u6162\n * \u9605\u8BFB\u3002\u8FD9\u5C42\u5728 client \u7AEF\u6309 id \u9996\u5B57\u6BCD\u5927\u5C0F\u5199\u81EA\u52A8 hide \u79C1\u6709 declaration +\n * index \u94FE\u63A5\uFF0C\u518D\u6CE8\u5165\u4E00\u4E2A toggle button \u4E00\u952E\u5207\u663E\u793A\u3002\u72B6\u6001\u7528 localStorage\n * \u8BB0\u4E0B\uFF0C\u8DE8\u9875\u4FDD\u7559\u3002\n */\n(() => {\n  if (!document.querySelector('h4[data-kind]')) return; // \u975E godoc \u8BE6\u60C5\u9875\n\n  // method id \u5F62\u5982 \"Type.method\"\uFF0C\u53D6\u6700\u540E\u6BB5\u5224\u79C1\u6709\uFF1B\u5176\u4ED6\u76F4\u63A5\u5224 id \u672C\u8EAB\u3002\n  const isUnexported = (id: string): boolean => {\n    const last = id.split('.').pop() ?? id;\n    return /^[a-z]/.test(last);\n  };\n\n  // \u6807 declaration wrapper\uFF1Afunc \u5305\u5728 .Documentation-function\uFF1Btype / method\n  // \u5305\u5728 .Documentation-type / .Documentation-typeFunc / .Documentation-typeMethod\u3002\n  document.querySelectorAll('h4[data-kind][id]').forEach(h => {\n    if (!isUnexported(h.id)) return;\n    const wrapper = h.closest(\n      '.Documentation-function, .Documentation-type, .Documentation-typeFunc, .Documentation-typeMethod'\n    );\n    wrapper?.classList.add('Documentation-unexported');\n  });\n\n  // index \u5217\u8868\u9879\u4E5F\u6309\u94FE\u63A5\u9996\u5B57\u6BCD\u5224\n  document\n    .querySelectorAll(\n      '.Documentation-indexFunction a[href^=\"#\"], ' +\n        '.Documentation-indexType a[href^=\"#\"], ' +\n        '.Documentation-indexTypeFunctions a[href^=\"#\"], ' +\n        '.Documentation-indexTypeMethods a[href^=\"#\"]'\n    )\n    .forEach(a => {\n      if (isUnexported(a.getAttribute('href')!.slice(1))) {\n        a.closest('li')?.classList.add('Documentation-unexported');\n      }\n    });\n\n  // \u6CE8\u5165 CSS\uFF08\u4E0D\u52A8 main.css build pipeline\uFF0C\u907F\u514D\u589E\u91CF\u6539 esbuild \u8F93\u51FA\uFF09\n  const style = document.createElement('style');\n  style.textContent =\n    'body:not(.show-unexported) .Documentation-unexported{display:none}';\n  document.head.appendChild(style);\n\n  // \u6CE8\u5165 toggle button\u2014\u2014\u653E Index \u6807\u9898\u65C1\u8FB9\u6700\u663E\u773C\uFF0C\u8DDF \"Show internal\" \u540C\u4F4D\n  const indexHeader = document.querySelector('#pkg-index');\n  if (!indexHeader) return;\n  const btn = document.createElement('button');\n  btn.type = 'button';\n  btn.className = 'go-Button go-Button--inline';\n  btn.style.marginLeft = '0.75rem';\n  btn.style.fontSize = '0.875rem';\n  btn.style.verticalAlign = 'middle';\n\n  const STORE_KEY = 'gogodocs:showUnexported';\n  const apply = (show: boolean) => {\n    document.body.classList.toggle('show-unexported', show);\n    btn.textContent = show ? 'Hide unexported' : 'Show unexported';\n    try {\n      localStorage.setItem(STORE_KEY, show ? '1' : '0');\n    } catch {\n      /* localStorage \u4E0D\u53EF\u7528\uFF08\u9690\u79C1\u6A21\u5F0F / \u6587\u4EF6\u534F\u8BAE\uFF09\u65F6\u5FFD\u7565\uFF0C\u53EA\u4E22\u5931\u8DE8\u9875\u8BB0\u5FC6 */\n    }\n  };\n\n  apply(localStorage.getItem(STORE_KEY) === '1');\n  btn.addEventListener('click', () =>\n    apply(!document.body.classList.contains('show-unexported'))\n  );\n  indexHeader.appendChild(btn);\n})();\n"],
    -  "mappings": "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",
    +  "sourcesContent": ["/**\n * @license\n * Copyright 2024 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * \u7AD9\u70B9 URL \u5B50\u8DEF\u5F84\u524D\u7F00\uFF0C\u4ECE  \u8BFB\u51FA\u3002\n *\n * fork \u7528 -base-path=/gogodocs \u542F\u52A8\u65F6\uFF0Cserver \u7AEF\u6A21\u677F\u628A attribute \u5199\u6210\n * \"/gogodocs\"\uFF1B\u4E0A\u6E38\u516C\u7F51 pkg.go.dev \u9ED8\u8BA4\u6302\u6839\uFF0Cattribute \u4E3A\u7A7A\u5B57\u7B26\u4E32\u3002\n *\n * \u5728  \u4E4B\u5916\u7684\u811A\u672C\u521D\u59CB\u5316\u9636\u6BB5\u8C03\u7528\u90FD\u5B89\u5168\u2014\u2014documentElement \u4E00\u5B9A\u5B58\u5728\u3002\n */\nexport function getBasePath(): string {\n  return document.documentElement.dataset.basePath ?? '';\n}\n\n/**\n * \u7ED9\u4EE5 / \u5F00\u5934\u7684\u7EDD\u5BF9\u8DEF\u5F84\u52A0 BasePath \u524D\u7F00\u3002\n *\n * \u7528\u4E8E fetch URL / innerHTML src / location \u6BD4\u8F83\u7B49\u4EFB\u4F55\u9700\u8981\u62FC\u7AD9\u70B9\u7EDD\u5BF9 path\n * \u7684\u5730\u65B9\u3002\u975E\u7EDD\u5BF9\u8DEF\u5F84\uFF08\u4E0D\u4EE5 / \u5F00\u5934\uFF09\u539F\u6837\u8FD4\u56DE\u2014\u2014caller \u81EA\u5DF1\u4FDD\u8BC1\u8BED\u4E49\u3002\n *\n * \u4F8B\uFF1A\n *   abs('/play/share')       // \u2192 '/gogodocs/play/share' \u6216 '/play/share'\n *   abs('/static/foo.svg')   // \u2192 '/gogodocs/static/foo.svg' \u6216 '/static/foo.svg'\n *   abs('relative/x')        // \u2192 'relative/x'\uFF08\u4E0D\u52A8\uFF09\n */\nexport function abs(p: string): string {\n  if (!p.startsWith('/')) return p;\n  return getBasePath() + p;\n}\n", "/*!\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n// This file implements the playground implementation of the documentation\n// page. The playground involves a \"play\" button that allows you to open up\n// a new link to play.golang.org using the example code.\n\n// The CSS is in static/frontend/unit/main/_doc.css\n\nimport { abs } from '../base-path/base-path';\n\n/**\n * CSS classes used by PlaygroundExampleController\n */\nconst PlayExampleClassName = {\n  PLAY_HREF: '.js-exampleHref',\n  PLAY_CONTAINER: '.js-exampleContainer',\n  EXAMPLE_INPUT: '.Documentation-exampleCode',\n  EXAMPLE_OUTPUT: '.Documentation-exampleOutput',\n  EXAMPLE_OUTPUT_CONTAINER: '.js-exampleOutputContainer',\n  EXAMPLE_ERROR: '.Documentation-exampleError',\n  PLAY_BUTTON: '.Documentation-examplePlayButton',\n  SHARE_BUTTON: '.Documentation-exampleShareButton',\n  FORMAT_BUTTON: '.Documentation-exampleFormatButton',\n  RUN_BUTTON: '.Documentation-exampleRunButton',\n};\n\n/**\n * This controller enables playground examples to expand their dropdown or\n * generate shareable Go Playground URLs.\n */\nexport class PlaygroundExampleController {\n  /**\n   * The anchor tag used to identify the container with an example href.\n   * There is only one in an example container div.\n   */\n  private readonly anchorEl: HTMLAnchorElement | null;\n\n  /**\n   * The error element\n   */\n  private readonly errorEl: Element | null;\n\n  /**\n   * Buttons that redirect to an example's playground, this element\n   * only exists in executable examples.\n   */\n  private readonly playButtonEl: Element | null;\n  private readonly shareButtonEl: Element | null;\n\n  /**\n   * Button that formats the code in an example's playground.\n   */\n  private readonly formatButtonEl: Element | null;\n\n  /**\n   * Button that runs the code in an example's playground, this element\n   * only exists in executable examples.\n   */\n  private readonly runButtonEl: Element | null;\n\n  /**\n   * The executable code of an example.\n   */\n  private readonly inputEl: HTMLTextAreaElement | null;\n\n  /**\n   * The output of the given example code. This only exists if the\n   * author of the package provides an output for this example.\n   */\n  private readonly outputEl: Element | null;\n\n  /**\n   * The container for the output.\n   */\n  private readonly outputContainerEl: HTMLElement | null;\n\n  /**\n   * @param exampleEl The div that contains playground content for the given example.\n   */\n  constructor(private readonly exampleEl: HTMLDetailsElement) {\n    this.exampleEl = exampleEl;\n    this.anchorEl = exampleEl.querySelector('a');\n    this.errorEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_ERROR);\n    this.playButtonEl = exampleEl.querySelector(PlayExampleClassName.PLAY_BUTTON);\n    this.shareButtonEl = exampleEl.querySelector(PlayExampleClassName.SHARE_BUTTON);\n    this.formatButtonEl = exampleEl.querySelector(PlayExampleClassName.FORMAT_BUTTON);\n    this.runButtonEl = exampleEl.querySelector(PlayExampleClassName.RUN_BUTTON);\n    this.inputEl = this.makeTextArea(exampleEl.querySelector(PlayExampleClassName.EXAMPLE_INPUT));\n    this.outputEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_OUTPUT);\n    this.outputContainerEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_OUTPUT_CONTAINER);\n\n    // This is legacy listener to be replaced the listener for shareButtonEl.\n    this.playButtonEl?.addEventListener('click', () => this.handleShareButtonClick());\n    this.shareButtonEl?.addEventListener('click', () => this.handleShareButtonClick());\n    this.formatButtonEl?.addEventListener('click', () => this.handleFormatButtonClick());\n    this.runButtonEl?.addEventListener('click', () => this.handleRunButtonClick());\n\n    if (!this.inputEl) return;\n\n    this.resize();\n    this.inputEl.addEventListener('keyup', () => this.resize());\n    this.inputEl.addEventListener('keydown', e => this.onKeydown(e));\n  }\n\n  /**\n   * Replace the pre element with a textarea. The examples are initially rendered\n   * as pre elements so they're fully visible when JS is disabled.\n   */\n  makeTextArea(el: Element | null): HTMLTextAreaElement {\n    const t = document.createElement('textarea');\n    t.classList.add('Documentation-exampleCode', 'code');\n    t.spellcheck = false;\n    t.value = el?.textContent ?? '';\n    el?.parentElement?.replaceChild(t, el);\n    return t;\n  }\n\n  /**\n   * Retrieve the hash value of the anchor element.\n   */\n  getAnchorHash(): string | undefined {\n    return this.anchorEl?.hash;\n  }\n\n  /**\n   * Expands the current playground example.\n   */\n  expand(): void {\n    this.exampleEl.open = true;\n  }\n\n  /**\n   * Resizes the input element to accommodate the amount of text present.\n   */\n  private resize(): void {\n    if (this.inputEl?.value) {\n      const numLineBreaks = (this.inputEl.value.match(/\\n/g) || []).length;\n      // min-height + lines x line-height + padding + border\n      this.inputEl.style.height = `${(20 + numLineBreaks * 20 + 12 + 2) / 16}rem`;\n    }\n  }\n\n  /**\n   * Handler to override keyboard behavior in the playground's\n   * textarea element.\n   *\n   * Tab key inserts tabs into the example playground instead of\n   * switching to the next interactive element.\n   * @param e input element keyboard event.\n   */\n  private onKeydown(e: KeyboardEvent) {\n    if (e.key === 'Tab') {\n      document.execCommand('insertText', false, '\\t');\n      e.preventDefault();\n    }\n  }\n\n  /**\n   * Changes the text of the example's input box.\n   */\n  private setInputText(output: string) {\n    if (this.inputEl) {\n      this.inputEl.value = output;\n    }\n  }\n\n  /**\n   * Changes the text of the example's output box.\n   */\n  private setOutputText(output: string) {\n    if (this.outputEl) {\n      this.outputEl.textContent = output;\n    }\n    if (this.outputContainerEl) {\n      this.outputContainerEl.hidden = false;\n    }\n  }\n\n  /**\n   * Appends to the text of the example's output box.\n   */\n  private appendToOutputText(output: string) {\n    if (this.outputEl) {\n      this.outputEl.textContent += output;\n    }\n    if (this.outputContainerEl) {\n      this.outputContainerEl.hidden = false;\n    }\n  }\n\n  private setOutputHTML(output: string) {\n    if (this.outputEl) {\n      this.outputEl.innerHTML = output;\n    }\n  }\n\n  /**\n   * Sets the error message text and overwrites\n   * output box to indicate a failed response.\n   */\n  private setErrorText(err: string) {\n    if (this.errorEl) {\n      this.errorEl.textContent = err;\n    }\n    this.setOutputText('An error has occurred\u2026');\n  }\n\n  private getCodeWithModFile(): string {\n    let codeWithModFile = this.inputEl?.value ?? '';\n    const moduleVars = document.querySelector('.js-playgroundVars')?.dataset ?? {};\n    if (moduleVars.modulepath !== 'std') {\n      codeWithModFile = codeWithModFile.concat(`\n-- go.mod --\nmodule play.ground\n\nrequire ${moduleVars.modulepath} ${moduleVars.version}\n`);\n    }\n\n    return codeWithModFile;\n  }\n\n  /**\n   * Opens a new window to play.golang.org using the\n   * example snippet's code in the playground.\n   */\n  private handleShareButtonClick() {\n    const PLAYGROUND_BASE_URL = 'https://play.golang.org/p/';\n\n    this.setOutputText('Waiting for remote server\u2026');\n\n    fetch(abs('/play/share'), {\n      method: 'POST',\n      body: this.getCodeWithModFile(),\n    })\n      .then(res => res.text())\n      .then(shareId => {\n        const href = PLAYGROUND_BASE_URL + shareId;\n        this.setOutputHTML(`${href}`);\n        window.open(href);\n      })\n      .catch(err => {\n        this.setErrorText(err);\n      });\n  }\n\n  /**\n   * Runs gofmt on the example snippet in the playground.\n   */\n  private handleFormatButtonClick() {\n    this.setOutputText('Waiting for remote server\u2026');\n    const body = new FormData();\n    body.append('body', this.inputEl?.value ?? '');\n\n    fetch(abs('/play/fmt'), {\n      method: 'POST',\n      body: body,\n    })\n      .then(res => res.json())\n      .then(({ Body, Error }) => {\n        this.setOutputText(Error || 'Done.');\n        if (Body) {\n          this.setInputText(Body);\n          this.resize();\n        }\n      })\n      .catch(err => {\n        this.setErrorText(err);\n      });\n  }\n\n  /**\n   * Runs the code snippet in the example playground.\n   */\n  private handleRunButtonClick() {\n    this.setOutputText('Waiting for remote server\u2026');\n\n    fetch(abs('/play/compile'), {\n      method: 'POST',\n      body: JSON.stringify({ body: this.getCodeWithModFile(), version: 2 }),\n    })\n      .then(res => res.json())\n      .then(async ({ Events, Errors }) => {\n        this.setOutputText(Errors || '');\n        for (const e of Events || []) {\n          this.appendToOutputText(e.Message);\n          await new Promise(resolve => setTimeout(resolve, e.Delay / 1000000));\n        }\n      })\n      .catch(err => {\n        this.setErrorText(err);\n      });\n  }\n}\n\nexport function initPlaygrounds(): void {\n  const exampleHashRegex = location.hash.match(/^#(example-.*)$/);\n  if (exampleHashRegex) {\n    const exampleHashEl = document.getElementById(exampleHashRegex[1]) as HTMLDetailsElement;\n    if (exampleHashEl) {\n      exampleHashEl.open = true;\n    }\n  }\n\n  // We use a spread operator to convert a nodelist into an array of elements.\n  const exampleHrefs = [\n    ...document.querySelectorAll(PlayExampleClassName.PLAY_HREF),\n  ];\n\n  /**\n   * Sometimes exampleHrefs and playContainers are in different order, so we\n   * find an exampleHref from a common hash.\n   * @param playContainer - playground container\n   */\n  const findExampleHash = (playContainer: PlaygroundExampleController) =>\n    exampleHrefs.find(ex => {\n      return ex.hash === playContainer.getAnchorHash();\n    });\n\n  for (const el of document.querySelectorAll(PlayExampleClassName.PLAY_CONTAINER)) {\n    // There should be the same amount of hrefs referencing examples as example containers.\n    const playContainer = new PlaygroundExampleController(el as HTMLDetailsElement);\n    const exampleHref = findExampleHash(playContainer);\n    if (exampleHref) {\n      exampleHref.addEventListener('click', () => {\n        playContainer.expand();\n      });\n    } else {\n      console.warn('example href not found');\n    }\n  }\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { TreeNavController } from './tree.js';\n\nexport class SelectNavController {\n  constructor(private el: Element) {\n    this.el.addEventListener('change', e => {\n      const target = e.target as HTMLSelectElement;\n      let href = target.value;\n      if (!target.value.startsWith('/')) {\n        href = '/' + href;\n      }\n      window.location.href = href;\n    });\n  }\n}\n\nexport function makeSelectNav(tree: TreeNavController): HTMLLabelElement {\n  const label = document.createElement('label');\n  label.classList.add('go-Label');\n  label.setAttribute('aria-label', 'Menu');\n  const select = document.createElement('select');\n  select.classList.add('go-Select', 'js-selectNav');\n  label.appendChild(select);\n  const outline = document.createElement('optgroup');\n  outline.label = 'Outline';\n  select.appendChild(outline);\n  const groupMap: Record = {};\n  let group: HTMLOptGroupElement;\n  for (const t of tree.treeitems) {\n    if (Number(t.depth) > 4) continue;\n    if (t.groupTreeitem) {\n      group = groupMap[t.groupTreeitem.label];\n      if (!group) {\n        group = groupMap[t.groupTreeitem.label] = document.createElement('optgroup');\n        group.label = t.groupTreeitem.label;\n        select.appendChild(group);\n      }\n    } else {\n      group = outline;\n    }\n    const o = document.createElement('option');\n    o.label = t.label;\n    o.textContent = t.label;\n    o.value = (t.el as HTMLAnchorElement).href.replace(window.location.origin, '').replace('/', '');\n    group.appendChild(o);\n  }\n  tree.addObserver(t => {\n    const hash = (t.el as HTMLAnchorElement).hash;\n    const value = select.querySelector(`[value$=\"${hash}\"]`)?.value;\n    if (value) {\n      select.value = value;\n    }\n  }, 50);\n  return label;\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * TreeNavController is the navigation tree component of the documentation page.\n * It adds accessiblity attributes to a tree, observes the heading elements\n * focus the topmost link for headings visible on the page, and implements the\n * WAI-ARIA Treeview Design Pattern with full\n * [keyboard support](https://www.w3.org/TR/wai-aria-practices/examples/treeview/treeview-2/treeview-2a.html#kbd_label).\n */\nexport class TreeNavController {\n  treeitems: TreeItem[];\n\n  /**\n   * firstChars is the first character of each treeitem in the same order\n   * as this.treeitems. We use this array to set focus by character when\n   * navigating the tree with a keyboard.\n   */\n  private firstChars: string[];\n  private firstTreeitem: TreeItem | null;\n  private lastTreeitem: TreeItem | null;\n  private observerCallbacks: ((t: TreeItem) => void)[];\n\n  constructor(private el: HTMLElement) {\n    this.treeitems = [];\n    this.firstChars = [];\n    this.firstTreeitem = null;\n    this.lastTreeitem = null;\n    this.observerCallbacks = [];\n    this.init();\n  }\n\n  private init(): void {\n    this.handleResize();\n    window.addEventListener('resize', this.handleResize);\n    this.findTreeItems();\n    this.updateVisibleTreeitems();\n    this.observeTargets();\n    if (this.firstTreeitem) {\n      this.firstTreeitem.el.tabIndex = 0;\n    }\n  }\n\n  private handleResize = (): void => {\n    this.el.style.setProperty('--js-tree-height', '100vh');\n    this.el.style.setProperty('--js-tree-height', this.el.clientHeight + 'px');\n  };\n\n  private observeTargets() {\n    this.addObserver(treeitem => {\n      this.expandTreeitem(treeitem);\n      this.setSelected(treeitem);\n      // TODO: Fix scroll issue in https://golang.org/issue/47450.\n      // treeitem.el.scrollIntoView({ block: 'nearest' });\n    });\n\n    const targets = new Map();\n    const observer = new IntersectionObserver(\n      entries => {\n        for (const entry of entries) {\n          targets.set(entry.target.id, entry.isIntersecting || entry.intersectionRatio === 1);\n        }\n        for (const [id, isIntersecting] of targets) {\n          if (isIntersecting) {\n            const active = this.treeitems.find(t =>\n              (t.el as HTMLAnchorElement)?.href.endsWith(`#${id}`)\n            );\n            if (active) {\n              for (const fn of this.observerCallbacks) {\n                fn(active);\n              }\n            }\n            break;\n          }\n        }\n      },\n      {\n        threshold: 1.0,\n        rootMargin: '-60px 0px 0px 0px',\n      }\n    );\n\n    for (const href of this.treeitems.map(t => t.el.getAttribute('href'))) {\n      if (href) {\n        const id = href.replace(window.location.origin, '').replace('/', '').replace('#', '');\n        const target = document.getElementById(id);\n        if (target && !id.startsWith('example-')) {\n          observer.observe(target);\n        }\n      }\n    }\n  }\n\n  addObserver(fn: (t: TreeItem) => void, delay = 200): void {\n    this.observerCallbacks.push(debounce(fn, delay));\n  }\n\n  setFocusToNextItem(currentItem: TreeItem): void {\n    let nextItem = null;\n    for (let i = currentItem.index + 1; i < this.treeitems.length; i++) {\n      const ti = this.treeitems[i];\n      if (ti.isVisible) {\n        nextItem = ti;\n        break;\n      }\n    }\n    if (nextItem) {\n      this.setFocusToItem(nextItem);\n    }\n  }\n\n  setFocusToPreviousItem(currentItem: TreeItem): void {\n    let prevItem = null;\n    for (let i = currentItem.index - 1; i > -1; i--) {\n      const ti = this.treeitems[i];\n      if (ti.isVisible) {\n        prevItem = ti;\n        break;\n      }\n    }\n    if (prevItem) {\n      this.setFocusToItem(prevItem);\n    }\n  }\n\n  setFocusToParentItem(currentItem: TreeItem): void {\n    if (currentItem.groupTreeitem) {\n      this.setFocusToItem(currentItem.groupTreeitem);\n    }\n  }\n\n  setFocusToFirstItem(): void {\n    this.firstTreeitem && this.setFocusToItem(this.firstTreeitem);\n  }\n\n  setFocusToLastItem(): void {\n    this.lastTreeitem && this.setFocusToItem(this.lastTreeitem);\n  }\n\n  setSelected(currentItem: TreeItem): void {\n    for (const l1 of this.el.querySelectorAll('[aria-expanded=\"true\"]')) {\n      if (l1 === currentItem.el) continue;\n      if (!l1.nextElementSibling?.contains(currentItem.el)) {\n        l1.setAttribute('aria-expanded', 'false');\n      }\n    }\n    for (const l1 of this.el.querySelectorAll('[aria-selected]')) {\n      if (l1 !== currentItem.el) {\n        l1.setAttribute('aria-selected', 'false');\n      }\n    }\n    currentItem.el.setAttribute('aria-selected', 'true');\n    this.updateVisibleTreeitems();\n    this.setFocusToItem(currentItem, false);\n  }\n\n  expandTreeitem(treeitem: TreeItem): void {\n    let currentItem: TreeItem | null = treeitem;\n    while (currentItem) {\n      if (currentItem.isExpandable) {\n        currentItem.el.setAttribute('aria-expanded', 'true');\n      }\n      currentItem = currentItem.groupTreeitem;\n    }\n    this.updateVisibleTreeitems();\n  }\n\n  expandAllSiblingItems(currentItem: TreeItem): void {\n    for (const ti of this.treeitems) {\n      if (ti.groupTreeitem === currentItem.groupTreeitem && ti.isExpandable) {\n        this.expandTreeitem(ti);\n      }\n    }\n  }\n\n  collapseTreeitem(currentItem: TreeItem): void {\n    let groupTreeitem = null;\n\n    if (currentItem.isExpanded()) {\n      groupTreeitem = currentItem;\n    } else {\n      groupTreeitem = currentItem.groupTreeitem;\n    }\n\n    if (groupTreeitem) {\n      groupTreeitem.el.setAttribute('aria-expanded', 'false');\n      this.updateVisibleTreeitems();\n      this.setFocusToItem(groupTreeitem);\n    }\n  }\n\n  setFocusByFirstCharacter(currentItem: TreeItem, char: string): void {\n    let start: number, index: number;\n    char = char.toLowerCase();\n\n    // Get start index for search based on position of currentItem\n    start = currentItem.index + 1;\n    if (start === this.treeitems.length) {\n      start = 0;\n    }\n\n    // Check remaining slots in the menu\n    index = this.getIndexFirstChars(start, char);\n\n    // If not found in remaining slots, check from beginning\n    if (index === -1) {\n      index = this.getIndexFirstChars(0, char);\n    }\n\n    // If match was found...\n    if (index > -1) {\n      this.setFocusToItem(this.treeitems[index]);\n    }\n  }\n\n  private findTreeItems() {\n    const findItems = (el: HTMLElement, group: TreeItem | null) => {\n      let ti = group;\n      let curr = el.firstElementChild as HTMLElement;\n      while (curr) {\n        if (curr.tagName === 'A' || curr.tagName === 'SPAN') {\n          ti = new TreeItem(curr, this, group);\n          this.treeitems.push(ti);\n          this.firstChars.push(ti.label.substring(0, 1).toLowerCase());\n        }\n        if (curr.firstElementChild) {\n          findItems(curr, ti);\n        }\n        curr = curr.nextElementSibling as HTMLElement;\n      }\n    };\n    findItems(this.el as HTMLElement, null);\n    this.treeitems.map((ti, idx) => (ti.index = idx));\n  }\n\n  private updateVisibleTreeitems(): void {\n    this.firstTreeitem = this.treeitems[0];\n\n    for (const ti of this.treeitems) {\n      let parent = ti.groupTreeitem;\n      ti.isVisible = true;\n      while (parent && parent.el !== this.el) {\n        if (!parent.isExpanded()) {\n          ti.isVisible = false;\n        }\n        parent = parent.groupTreeitem;\n      }\n      if (ti.isVisible) {\n        this.lastTreeitem = ti;\n      }\n    }\n  }\n\n  private setFocusToItem(treeitem: TreeItem, focusEl = true) {\n    treeitem.el.tabIndex = 0;\n    if (focusEl) {\n      treeitem.el.focus();\n    }\n    for (const ti of this.treeitems) {\n      if (ti !== treeitem) {\n        ti.el.tabIndex = -1;\n      }\n    }\n  }\n\n  private getIndexFirstChars(startIndex: number, char: string): number {\n    for (let i = startIndex; i < this.firstChars.length; i++) {\n      if (this.treeitems[i].isVisible && char === this.firstChars[i]) {\n        return i;\n      }\n    }\n    return -1;\n  }\n}\n\nclass TreeItem {\n  el: HTMLElement;\n  groupTreeitem: TreeItem | null;\n  label: string;\n  isExpandable: boolean;\n  isVisible: boolean;\n  depth: number;\n  index: number;\n\n  private tree: TreeNavController;\n  private isInGroup: boolean;\n\n  constructor(el: HTMLElement, treeObj: TreeNavController, group: TreeItem | null) {\n    el.tabIndex = -1;\n    this.el = el;\n    this.groupTreeitem = group;\n    this.label = el.textContent?.trim() ?? '';\n    this.tree = treeObj;\n    this.depth = (group?.depth || 0) + 1;\n    this.index = 0;\n\n    const parent = el.parentElement;\n    if (parent?.tagName.toLowerCase() === 'li') {\n      parent?.setAttribute('role', 'none');\n    }\n    el.setAttribute('aria-level', this.depth + '');\n    if (el.getAttribute('aria-label')) {\n      this.label = el?.getAttribute('aria-label')?.trim() ?? '';\n    }\n\n    this.isExpandable = false;\n    this.isVisible = false;\n    this.isInGroup = !!group;\n\n    let curr = el.nextElementSibling;\n    while (curr) {\n      if (curr.tagName.toLowerCase() == 'ul') {\n        const groupId = `${group?.label ?? ''} nav group ${this.label}`.replace(/[\\W_]+/g, '_');\n        el.setAttribute('aria-owns', groupId);\n        el.setAttribute('aria-expanded', 'false');\n        curr.setAttribute('role', 'group');\n        curr.setAttribute('id', groupId);\n        this.isExpandable = true;\n        break;\n      }\n\n      curr = curr.nextElementSibling;\n    }\n    this.init();\n  }\n\n  private init() {\n    this.el.tabIndex = -1;\n    if (!this.el.getAttribute('role')) {\n      this.el.setAttribute('role', 'treeitem');\n    }\n    this.el.addEventListener('keydown', this.handleKeydown.bind(this));\n    this.el.addEventListener('click', this.handleClick.bind(this));\n    this.el.addEventListener('focus', this.handleFocus.bind(this));\n    this.el.addEventListener('blur', this.handleBlur.bind(this));\n  }\n\n  isExpanded() {\n    if (this.isExpandable) {\n      return this.el.getAttribute('aria-expanded') === 'true';\n    }\n\n    return false;\n  }\n\n  isSelected() {\n    return this.el.getAttribute('aria-selected') === 'true';\n  }\n\n  private handleClick(event: MouseEvent) {\n    // only process click events that directly happened on this treeitem\n    if (event.target !== this.el && event.target !== this.el.firstElementChild) {\n      return;\n    }\n    if (this.isExpandable) {\n      if (this.isExpanded() && this.isSelected()) {\n        this.tree.collapseTreeitem(this);\n      } else {\n        this.tree.expandTreeitem(this);\n      }\n      event.stopPropagation();\n    }\n    this.tree.setSelected(this);\n  }\n\n  private handleFocus() {\n    let el = this.el;\n    if (this.isExpandable) {\n      el = (el.firstElementChild as HTMLElement) ?? el;\n    }\n    el.classList.add('focus');\n  }\n\n  private handleBlur() {\n    let el = this.el;\n    if (this.isExpandable) {\n      el = (el.firstElementChild as HTMLElement) ?? el;\n    }\n    el.classList.remove('focus');\n  }\n\n  private handleKeydown(event: KeyboardEvent) {\n    if (event.altKey || event.ctrlKey || event.metaKey) {\n      return;\n    }\n\n    let captured = false;\n    switch (event.key) {\n      case ' ':\n      case 'Enter':\n        if (this.isExpandable) {\n          if (this.isExpanded() && this.isSelected()) {\n            this.tree.collapseTreeitem(this);\n          } else {\n            this.tree.expandTreeitem(this);\n          }\n          captured = true;\n        } else {\n          event.stopPropagation();\n        }\n        this.tree.setSelected(this);\n        break;\n\n      case 'ArrowUp':\n        this.tree.setFocusToPreviousItem(this);\n        captured = true;\n        break;\n\n      case 'ArrowDown':\n        this.tree.setFocusToNextItem(this);\n        captured = true;\n        break;\n\n      case 'ArrowRight':\n        if (this.isExpandable) {\n          if (this.isExpanded()) {\n            this.tree.setFocusToNextItem(this);\n          } else {\n            this.tree.expandTreeitem(this);\n          }\n        }\n        captured = true;\n        break;\n\n      case 'ArrowLeft':\n        if (this.isExpandable && this.isExpanded()) {\n          this.tree.collapseTreeitem(this);\n          captured = true;\n        } else {\n          if (this.isInGroup) {\n            this.tree.setFocusToParentItem(this);\n            captured = true;\n          }\n        }\n        break;\n\n      case 'Home':\n        this.tree.setFocusToFirstItem();\n        captured = true;\n        break;\n\n      case 'End':\n        this.tree.setFocusToLastItem();\n        captured = true;\n        break;\n\n      default:\n        if (event.key.length === 1 && event.key.match(/\\S/)) {\n          if (event.key == '*') {\n            this.tree.expandAllSiblingItems(this);\n          } else {\n            this.tree.setFocusByFirstCharacter(this, event.key);\n          }\n          captured = true;\n        }\n        break;\n    }\n\n    if (captured) {\n      event.stopPropagation();\n      event.preventDefault();\n    }\n  }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction debounce any>(func: T, wait: number) {\n  let timeout: ReturnType | null;\n  return (...args: Parameters) => {\n    const later = () => {\n      timeout = null;\n      func(...args);\n    };\n    if (timeout) {\n      clearTimeout(timeout);\n    }\n    timeout = setTimeout(later, wait);\n  };\n}\n", "/*!\n * @license\n * Copyright 2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * Controller for a table element with expandable rows. Adds event listeners to\n * a toggle within a table row that controls visiblity of additional related\n * rows in the table.\n *\n * @example\n * ```typescript\n * import {ExpandableRowsTableController} from '/static/js/table';\n *\n * const el = document .querySelector('.js-myTableElement')\n * new ExpandableRowsTableController(el));\n * ```\n */\nexport class ExpandableRowsTableController {\n  private rows: HTMLTableRowElement[];\n  private toggles: HTMLButtonElement[];\n\n  /**\n   * Create a table controller.\n   * @param table - The table element to which the controller binds.\n   */\n  constructor(private table: HTMLTableElement, private toggleAll?: HTMLButtonElement | null) {\n    this.rows = Array.from(table.querySelectorAll('[data-aria-controls]'));\n    this.toggles = Array.from(this.table.querySelectorAll('[aria-expanded]'));\n    this.setAttributes();\n    this.attachEventListeners();\n    this.update();\n  }\n\n  /**\n   * setAttributes sets data-aria-* and data-id attributes to regular\n   * html attributes as a workaround for limitations from safehtml.\n   */\n  private setAttributes() {\n    for (const a of ['data-aria-controls', 'data-aria-labelledby', 'data-id']) {\n      this.table.querySelectorAll(`[${a}]`).forEach(t => {\n        t.setAttribute(a.replace('data-', ''), t.getAttribute(a) ?? '');\n        t.removeAttribute(a);\n      });\n    }\n  }\n\n  private attachEventListeners() {\n    this.rows.forEach(t => {\n      t.addEventListener('click', e => {\n        this.handleToggleClick(e);\n      });\n    });\n    this.toggleAll?.addEventListener('click', () => {\n      this.expandAllItems();\n    });\n\n    document.addEventListener('keydown', e => {\n      if ((e.ctrlKey || e.metaKey) && e.key === 'f') {\n        this.expandAllItems();\n      }\n    });\n  }\n\n  private handleToggleClick(e: MouseEvent) {\n    let target = e.currentTarget as HTMLTableRowElement | null;\n    if (!target?.hasAttribute('aria-expanded')) {\n      target = this.table.querySelector(\n        `button[aria-controls=\"${target?.getAttribute('aria-controls')}\"]`\n      );\n    }\n    const isExpanded = target?.getAttribute('aria-expanded') === 'true';\n    target?.setAttribute('aria-expanded', isExpanded ? 'false' : 'true');\n    e.stopPropagation();\n    this.update();\n  }\n\n  expandAllItems = (): void => {\n    this.toggles.map(t => t.setAttribute('aria-expanded', 'true'));\n    this.update();\n  };\n\n  private collapseAllItems = () => {\n    this.toggles.map(t => t.setAttribute('aria-expanded', 'false'));\n    this.update();\n  };\n\n  private update = () => {\n    this.updateVisibleItems();\n    setTimeout(() => this.updateGlobalToggle());\n  };\n\n  private updateVisibleItems() {\n    this.rows.map(t => {\n      const isExpanded = t?.getAttribute('aria-expanded') === 'true';\n      const rowIds = t?.getAttribute('aria-controls')?.trimEnd().split(' ');\n      rowIds?.map(id => {\n        const target = document.getElementById(`${id}`);\n        if (isExpanded) {\n          target?.classList.add('visible');\n          target?.classList.remove('hidden');\n        } else {\n          target?.classList.add('hidden');\n          target?.classList.remove('visible');\n        }\n      });\n    });\n  }\n\n  private updateGlobalToggle() {\n    if (!this.toggleAll) return;\n    if (this.rows.some(t => t.hasAttribute('aria-expanded'))) {\n      this.toggleAll.style.display = 'block';\n    }\n    const someCollapsed = this.toggles.some(el => el.getAttribute('aria-expanded') === 'false');\n    if (someCollapsed) {\n      this.toggleAll.innerText = 'Expand all';\n      this.toggleAll.onclick = this.expandAllItems;\n      this.toggleAll.setAttribute('aria-label', 'Expand all directories');\n      this.toggleAll.setAttribute('aria-live', 'polite');\n    } else {\n      this.toggleAll.innerText = 'Collapse all';\n      this.toggleAll.onclick = this.collapseAllItems;\n      this.toggleAll.setAttribute('aria-label', 'Collapse all directories');\n      this.toggleAll.setAttribute('aria-live', 'polite');\n    }\n  }\n}\n", "import { initPlaygrounds } from 'static/shared/playground/playground';\nimport { SelectNavController, makeSelectNav } from 'static/shared/outline/select';\nimport { TreeNavController } from 'static/shared/outline/tree';\nimport { ExpandableRowsTableController } from 'static/shared/table/table';\n\ninitPlaygrounds();\n\nconst directories = document.querySelector('.js-expandableTable');\nif (directories) {\n  const table = new ExpandableRowsTableController(\n    directories,\n    document.querySelector('.js-expandAllDirectories')\n  );\n  // Expand directories on page load with expand-directories query param.\n  if (window.location.search.includes('expand-directories')) {\n    table.expandAllItems();\n  }\n\n  const internalToggle = document.querySelector('.js-showInternalDirectories');\n  if (internalToggle) {\n    if (document.querySelector('.UnitDirectories-internal')) {\n      internalToggle.style.display = 'block';\n      internalToggle.setAttribute('aria-label', 'Show Internal Directories');\n      internalToggle.setAttribute('aria-describedby', 'showInternal-description');\n    }\n    internalToggle.addEventListener('click', () => {\n      if (directories.classList.contains('UnitDirectories-showInternal')) {\n        directories.classList.remove('UnitDirectories-showInternal');\n        internalToggle.innerText = 'Show internal';\n        internalToggle.setAttribute('aria-label', 'Show Internal Directories');\n        internalToggle.setAttribute('aria-live', 'polite');\n        internalToggle.setAttribute('aria-describedby', 'showInternal-description');\n      } else {\n        directories.classList.add('UnitDirectories-showInternal');\n        internalToggle.innerText = 'Hide internal';\n        internalToggle.setAttribute('aria-label', 'Hide Internal Directories');\n        internalToggle.setAttribute('aria-live', 'polite');\n        internalToggle.setAttribute('aria-describedby', 'hideInternal-description');\n      }\n    });\n  }\n  if (document.querySelector('html[data-local=\"true\"]')) {\n    internalToggle?.click();\n  }\n}\n\nconst treeEl = document.querySelector('.js-tree');\nif (treeEl) {\n  const treeCtrl = new TreeNavController(treeEl);\n  const select = makeSelectNav(treeCtrl);\n  const mobileNav = document.querySelector('.js-mainNavMobile');\n  if (mobileNav && mobileNav.firstElementChild) {\n    mobileNav?.replaceChild(select, mobileNav.firstElementChild);\n  }\n  if (select.firstElementChild) {\n    new SelectNavController(select.firstElementChild);\n  }\n}\n\n/**\n * Event handlers for expanding and collapsing the readme section.\n */\nconst readme = document.querySelector('.js-readme');\nconst readmeContent = document.querySelector('.js-readmeContent');\nconst readmeOutline = document.querySelector('.js-readmeOutline');\nconst readmeExpand = document.querySelectorAll('.js-readmeExpand');\nconst readmeCollapse = document.querySelector('.js-readmeCollapse');\nconst mobileNavSelect = document.querySelector('.DocNavMobile-select');\nif (readme && readmeContent && readmeOutline && readmeExpand.length && readmeCollapse) {\n  if (readme.clientHeight > 320) {\n    readme?.classList.remove('UnitReadme--expanded');\n    readme?.classList.add('UnitReadme--toggle');\n  }\n  if (window.location.hash.includes('readme')) {\n    expandReadme();\n  }\n  mobileNavSelect?.addEventListener('change', e => {\n    if ((e.target as HTMLSelectElement).value.startsWith('readme-')) {\n      expandReadme();\n    }\n  });\n  readmeExpand.forEach(el =>\n    el.addEventListener('click', e => {\n      e.preventDefault();\n      expandReadme();\n      readme.scrollIntoView();\n    })\n  );\n  readmeCollapse.addEventListener('click', e => {\n    e.preventDefault();\n    readme.classList.remove('UnitReadme--expanded');\n    if (readmeExpand[1]) {\n      readmeExpand[1].scrollIntoView({ block: 'center' });\n    }\n  });\n  readmeContent.addEventListener('keyup', () => {\n    expandReadme();\n  });\n  readmeContent.addEventListener('click', () => {\n    expandReadme();\n  });\n  readmeOutline.addEventListener('click', () => {\n    expandReadme();\n  });\n  document.addEventListener('keydown', e => {\n    if ((e.ctrlKey || e.metaKey) && e.key === 'f') {\n      expandReadme();\n    }\n  });\n}\n\n/**\n * expandReadme expands the readme and adds the section-readme hash to the\n * URL so it stays expanded when navigating back from an external link.\n */\nfunction expandReadme() {\n  history.replaceState(null, '', `${location.pathname}${location.search}#section-readme`);\n  readme?.classList.add('UnitReadme--expanded');\n}\n\n/**\n * Expand details items that are focused. This will expand\n * deprecated symbols when they are navigated to from the index\n * or a direct link.\n */\nfunction openDeprecatedSymbol() {\n  if (!location.hash) return;\n  const heading = document.getElementById(location.hash.slice(1));\n  const grandParent = heading?.parentElement?.parentElement as HTMLDetailsElement | null;\n  if (grandParent?.nodeName === 'DETAILS') {\n    grandParent.open = true;\n  }\n}\nopenDeprecatedSymbol();\nwindow.addEventListener('hashchange', () => openDeprecatedSymbol());\n\n/**\n * Listen for changes in the build context dropdown.\n */\ndocument.querySelectorAll('.js-buildContextSelect').forEach(el => {\n  el.addEventListener('change', e => {\n    window.location.search = `?GOOS=${(e.target as HTMLSelectElement).value}`;\n  });\n});\n\n/**\n * fork\uFF1Aunexported\uFF08\u79C1\u6709\uFF09\u7B26\u53F7 toggle\u3002\n *\n * \u80CC\u666F\uFF1Apkgsite -show-unexported flag \u8BA9 godoc \u628A\u79C1\u6709 type/func/method\n * \u90FD\u6E32\u5230 page\u3002\u4F46\u8BFB\u8005\u5927\u591A\u6570\u65F6\u5019\u53EA\u5173\u5FC3 public API\uFF0C\u79C1\u6709\u7684\u592A\u591A\u53CD\u800C\u62D6\u6162\n * \u9605\u8BFB\u3002\u8FD9\u5C42\u5728 client \u7AEF\u6309 id \u9996\u5B57\u6BCD\u5927\u5C0F\u5199\u81EA\u52A8 hide \u79C1\u6709 declaration +\n * index \u94FE\u63A5\uFF0C\u518D\u6CE8\u5165\u4E00\u4E2A toggle button \u4E00\u952E\u5207\u663E\u793A\u3002\u72B6\u6001\u7528 localStorage\n * \u8BB0\u4E0B\uFF0C\u8DE8\u9875\u4FDD\u7559\u3002\n */\n(() => {\n  if (!document.querySelector('h4[data-kind]')) return; // \u975E godoc \u8BE6\u60C5\u9875\n\n  // method id \u5F62\u5982 \"Type.method\"\uFF0C\u53D6\u6700\u540E\u6BB5\u5224\u79C1\u6709\uFF1B\u5176\u4ED6\u76F4\u63A5\u5224 id \u672C\u8EAB\u3002\n  // \u6392\u9664 pkg-overview / section-readme / pkg-index \u8FD9\u7C7B\u9875\u9762 anchor\u2014\u2014\u5B83\u4EEC\u662F\n  // \u5C0F\u5199\u8D77\u5934\u4F46\u4E0D\u662F Go \u7B26\u53F7\uFF0C\u626B\u5230\u4F1A\u8BEF\u6807\u9690\u85CF\u3002\n  const isUnexported = (id: string): boolean => {\n    if (id.startsWith('pkg-') || id.startsWith('section-') || id.startsWith('hdr-')) {\n      return false;\n    }\n    const last = id.split('.').pop() ?? id;\n    return /^[a-z]/.test(last);\n  };\n\n  // \u6807 declaration wrapper\uFF1Afunc \u5305\u5728 .Documentation-function\uFF1Btype / method\n  // \u5305\u5728 .Documentation-type / .Documentation-typeFunc / .Documentation-typeMethod\u3002\n  document.querySelectorAll('h4[data-kind][id]').forEach(h => {\n    if (!isUnexported(h.id)) return;\n    const wrapper = h.closest(\n      '.Documentation-function, .Documentation-type, .Documentation-typeFunc, .Documentation-typeMethod'\n    );\n    wrapper?.classList.add('Documentation-unexported');\n  });\n\n  // index \u5217\u8868\u9879 + \u5DE6\u4FA7\u8FB9\u680F\uFF08go-Tree outline\uFF09\u6309\u94FE\u63A5\u9996\u5B57\u6BCD\u5224\u3002\n  // \u4FA7\u8FB9\u680F\u94FE\u63A5 selector \u76F4\u63A5\u5339\u914D\u6240\u6709 .go-Tree a[href^=\"#\"]\u2014\u2014pkg-overview /\n  // section-readme \u7B49\u5BFC\u822A anchor \u5DF2\u88AB isUnexported \u5934\u90E8\u6392\u9664\uFF0C\u4E0D\u4F1A\u8BEF\u6807\u3002\n  document\n    .querySelectorAll(\n      '.Documentation-indexFunction a[href^=\"#\"], ' +\n        '.Documentation-indexType a[href^=\"#\"], ' +\n        '.Documentation-indexTypeFunctions a[href^=\"#\"], ' +\n        '.Documentation-indexTypeMethods a[href^=\"#\"], ' +\n        '.go-Tree a[href^=\"#\"]'\n    )\n    .forEach(a => {\n      if (isUnexported(a.getAttribute('href')!.slice(1))) {\n        a.closest('li')?.classList.add('Documentation-unexported');\n      }\n    });\n\n  // \u6CE8\u5165 CSS\uFF08\u4E0D\u52A8 main.css build pipeline\uFF0C\u907F\u514D\u589E\u91CF\u6539 esbuild \u8F93\u51FA\uFF09\n  const style = document.createElement('style');\n  style.textContent =\n    'body:not(.show-unexported) .Documentation-unexported{display:none}';\n  document.head.appendChild(style);\n\n  // \u6CE8\u5165 toggle button\u2014\u2014\u653E Index \u6807\u9898\u65C1\u8FB9\u6700\u663E\u773C\uFF0C\u8DDF \"Show internal\" \u540C\u4F4D\n  const indexHeader = document.querySelector('#pkg-index');\n  if (!indexHeader) return;\n  const btn = document.createElement('button');\n  btn.type = 'button';\n  btn.className = 'go-Button go-Button--inline';\n  btn.style.marginLeft = '0.75rem';\n  btn.style.fontSize = '0.875rem';\n  btn.style.verticalAlign = 'middle';\n\n  const STORE_KEY = 'gogodocs:showUnexported';\n  const apply = (show: boolean) => {\n    document.body.classList.toggle('show-unexported', show);\n    btn.textContent = show ? 'Hide unexported' : 'Show unexported';\n    try {\n      localStorage.setItem(STORE_KEY, show ? '1' : '0');\n    } catch {\n      /* localStorage \u4E0D\u53EF\u7528\uFF08\u9690\u79C1\u6A21\u5F0F / \u6587\u4EF6\u534F\u8BAE\uFF09\u65F6\u5FFD\u7565\uFF0C\u53EA\u4E22\u5931\u8DE8\u9875\u8BB0\u5FC6 */\n    }\n  };\n\n  apply(localStorage.getItem(STORE_KEY) === '1');\n  btn.addEventListener('click', () =>\n    apply(!document.body.classList.contains('show-unexported'))\n  );\n  indexHeader.appendChild(btn);\n})();\n"],
    +  "mappings": "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",
       "names": ["getBasePath", "_a", "abs", "p", "PlayExampleClassName", "PlaygroundExampleController", "exampleEl", "_a", "_b", "_c", "_d", "e", "el", "numLineBreaks", "output", "err", "codeWithModFile", "moduleVars", "PLAYGROUND_BASE_URL", "abs", "res", "shareId", "href", "body", "Body", "Error", "Events", "Errors", "resolve", "initPlaygrounds", "exampleHashRegex", "exampleHashEl", "exampleHrefs", "findExampleHash", "playContainer", "ex", "exampleHref", "SelectNavController", "el", "e", "target", "href", "makeSelectNav", "tree", "label", "select", "outline", "groupMap", "group", "t", "o", "_a", "hash", "value", "TreeNavController", "el", "treeitem", "targets", "observer", "entries", "entry", "id", "isIntersecting", "active", "t", "_a", "fn", "href", "target", "delay", "debounce", "currentItem", "nextItem", "ti", "prevItem", "l1", "groupTreeitem", "char", "start", "index", "findItems", "group", "curr", "TreeItem", "idx", "parent", "focusEl", "startIndex", "treeObj", "_b", "_c", "_d", "_e", "groupId", "event", "captured", "func", "wait", "timeout", "args", "later", "ExpandableRowsTableController", "table", "toggleAll", "t", "a", "_a", "e", "target", "isExpanded", "rowIds", "id", "el", "initPlaygrounds", "directories", "table", "ExpandableRowsTableController", "internalToggle", "treeEl", "treeCtrl", "TreeNavController", "select", "makeSelectNav", "mobileNav", "SelectNavController", "readme", "readmeContent", "readmeOutline", "readmeExpand", "readmeCollapse", "mobileNavSelect", "expandReadme", "e", "el", "openDeprecatedSymbol", "_a", "heading", "grandParent", "isUnexported", "id", "last", "h", "wrapper", "a", "style", "indexHeader", "btn", "STORE_KEY", "apply", "show"]
     }
    diff --git a/static/frontend/unit/main/main.ts b/static/frontend/unit/main/main.ts
    index 64ab596b3..1c612d45e 100644
    --- a/static/frontend/unit/main/main.ts
    +++ b/static/frontend/unit/main/main.ts
    @@ -156,7 +156,12 @@ document.querySelectorAll('.js-buildContextSelect').forEach(el => {
       if (!document.querySelector('h4[data-kind]')) return; // 非 godoc 详情页
     
       // method id 形如 "Type.method",取最后段判私有;其他直接判 id 本身。
    +  // 排除 pkg-overview / section-readme / pkg-index 这类页面 anchor——它们是
    +  // 小写起头但不是 Go 符号,扫到会误标隐藏。
       const isUnexported = (id: string): boolean => {
    +    if (id.startsWith('pkg-') || id.startsWith('section-') || id.startsWith('hdr-')) {
    +      return false;
    +    }
         const last = id.split('.').pop() ?? id;
         return /^[a-z]/.test(last);
       };
    @@ -171,13 +176,16 @@ document.querySelectorAll('.js-buildContextSelect').forEach(el => {
         wrapper?.classList.add('Documentation-unexported');
       });
     
    -  // index 列表项也按链接首字母判
    +  // index 列表项 + 左侧边栏(go-Tree outline)按链接首字母判。
    +  // 侧边栏链接 selector 直接匹配所有 .go-Tree a[href^="#"]——pkg-overview /
    +  // section-readme 等导航 anchor 已被 isUnexported 头部排除,不会误标。
       document
         .querySelectorAll(
           '.Documentation-indexFunction a[href^="#"], ' +
             '.Documentation-indexType a[href^="#"], ' +
             '.Documentation-indexTypeFunctions a[href^="#"], ' +
    -        '.Documentation-indexTypeMethods a[href^="#"]'
    +        '.Documentation-indexTypeMethods a[href^="#"], ' +
    +        '.go-Tree a[href^="#"]'
         )
         .forEach(a => {
           if (isUnexported(a.getAttribute('href')!.slice(1))) {
    
    From 70d1af98b78d8ccde503c36f474091396aa4794c Mon Sep 17 00:00:00 2001
    From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com>
    Date: Thu, 7 May 2026 21:19:23 +0800
    Subject: [PATCH 11/26] =?UTF-8?q?fix(test):=20patch=20LoadTemplates=20?=
     =?UTF-8?q?=E7=AD=BE=E5=90=8D=E6=BC=8F=E7=BD=91?=
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    
    P1 改 dochtml.LoadTemplates 签名加 basePath 时只补了 dochtml 包内
    test(dochtml_test.go / symbol_test.go),漏了:
    - internal/godoc/render_test.go 30 / 94
    - internal/fetch/fetch_test.go 43
    
    加空 basePath 第二个参数。
    
    注:跑 fork 全 test 套件还有一个失败:TestFetchModule/master_version_of_
    stdlib_module/stdlibzip 期望 commit hash 89fb59e2e920 实际 2b6a2e56778f。
    切回 pristine upstream/master 复现同样 fail——stdlib zip 内容随 Go 版本
    变化但 fixture 写死,是上游历史问题,跟 fork patch 无关。
    ---
     internal/fetch/fetch_test.go  | 2 +-
     internal/godoc/render_test.go | 4 ++--
     2 files changed, 3 insertions(+), 3 deletions(-)
    
    diff --git a/internal/fetch/fetch_test.go b/internal/fetch/fetch_test.go
    index 5bc060188..3f02aa8dc 100644
    --- a/internal/fetch/fetch_test.go
    +++ b/internal/fetch/fetch_test.go
    @@ -40,7 +40,7 @@ var (
     type fetchFunc func(t *testing.T, withLicenseDetector bool, ctx context.Context, mod *proxytest.Module, fetchVersion string) (*FetchResult, *licenses.Detector)
     
     func TestMain(m *testing.M) {
    -	dochtml.LoadTemplates(templateFS)
    +	dochtml.LoadTemplates(templateFS, "")
     	testModules = proxytest.LoadTestModules("../proxy/testdata")
     	licenses.OmitExceptions = true
     	os.Exit(m.Run())
    diff --git a/internal/godoc/render_test.go b/internal/godoc/render_test.go
    index 75deb3387..33743e276 100644
    --- a/internal/godoc/render_test.go
    +++ b/internal/godoc/render_test.go
    @@ -27,7 +27,7 @@ var (
     )
     
     func TestDocInfo(t *testing.T) {
    -	dochtml.LoadTemplates(templateFS)
    +	dochtml.LoadTemplates(templateFS, "")
     	ctx := context.Background()
     	si := source.NewGitHubInfo("a.com/M", "", "abcde")
     	mi := &ModuleInfo{
    @@ -91,7 +91,7 @@ func TestDocInfo(t *testing.T) {
     }
     
     func TestRenderParts_SinceVersion(t *testing.T) {
    -	dochtml.LoadTemplates(templateFS)
    +	dochtml.LoadTemplates(templateFS, "")
     	ctx := context.Background()
     	si := source.NewGitHubInfo("a.com/M", "", "abcde")
     	mi := &ModuleInfo{
    
    From d4f7372bfd0c55241f9873ab52330dcdc6f9e49d Mon Sep 17 00:00:00 2001
    From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com>
    Date: Thu, 7 May 2026 21:21:02 +0800
    Subject: [PATCH 12/26] =?UTF-8?q?fix(test):=20=E8=A1=A5=E5=85=A8=20LoadTem?=
     =?UTF-8?q?plates=20/=20ParsePageTemplates=20=E7=AD=BE=E5=90=8D=E6=BC=8F?=
     =?UTF-8?q?=E7=BD=91=EF=BC=88worker=20/=20=E9=9B=86=E6=88=90=E6=B5=8B?=
     =?UTF-8?q?=E8=AF=95=EF=BC=89?=
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    
    P1 改这两个函数签名加 basePath 时还有 5 处漏:
    - internal/worker/server.go (prod worker 二进制;fork 主走 cmd/pkgsite
      但仍要 build 通过)
    - internal/worker/server_test.go
    - internal/fetchdatasource/fetchdatasource_test.go
    - internal/tests/templates/templates_test.go (含 ParsePageTemplates)
    - internal/testing/integration/integration_test.go
    
    加空 basePath 第二参数。go vet ./... + go test ./... 全过(除 1 个
    TestFetchModule/master_version_of_stdlib_module/stdlibzip——这是上游
    fixture 漂移,pristine upstream/master 同样 fail,跟 fork patch 无关)。
    
    golangci-lint 24 issues(errcheck / staticcheck)全是上游历史问题不动。
    ---
     internal/fetchdatasource/fetchdatasource_test.go | 2 +-
     internal/testing/integration/integration_test.go | 2 +-
     internal/tests/templates/templates_test.go       | 4 ++--
     internal/worker/server.go                        | 2 +-
     internal/worker/server_test.go                   | 2 +-
     5 files changed, 6 insertions(+), 6 deletions(-)
    
    diff --git a/internal/fetchdatasource/fetchdatasource_test.go b/internal/fetchdatasource/fetchdatasource_test.go
    index 223c38bd9..8cfb2b488 100644
    --- a/internal/fetchdatasource/fetchdatasource_test.go
    +++ b/internal/fetchdatasource/fetchdatasource_test.go
    @@ -35,7 +35,7 @@ var (
     )
     
     func TestMain(m *testing.M) {
    -	dochtml.LoadTemplates(template.TrustedFSFromTrustedSource(template.TrustedSourceFromConstant("../../static")))
    +	dochtml.LoadTemplates(template.TrustedFSFromTrustedSource(template.TrustedSourceFromConstant("../../static")), "")
     	defaultTestModules = proxytest.LoadTestModules("../proxy/testdata")
     	licenses.OmitExceptions = true
     	os.Exit(m.Run())
    diff --git a/internal/testing/integration/integration_test.go b/internal/testing/integration/integration_test.go
    index 6ac8a5ffe..d7c8b1b15 100644
    --- a/internal/testing/integration/integration_test.go
    +++ b/internal/testing/integration/integration_test.go
    @@ -32,7 +32,7 @@ import (
     )
     
     func TestMain(m *testing.M) {
    -	dochtml.LoadTemplates(template.TrustedFSFromTrustedSource(template.TrustedSourceFromConstant("../../../static")))
    +	dochtml.LoadTemplates(template.TrustedFSFromTrustedSource(template.TrustedSourceFromConstant("../../../static")), "")
     	testModules = proxytest.LoadTestModules("../../proxy/testdata")
     	postgres.RunDBTests("discovery_integration_test", m, &testDB)
     }
    diff --git a/internal/tests/templates/templates_test.go b/internal/tests/templates/templates_test.go
    index 30e43a762..fc2bdde1e 100644
    --- a/internal/tests/templates/templates_test.go
    +++ b/internal/tests/templates/templates_test.go
    @@ -20,7 +20,7 @@ import (
     func TestCheckFrontendTemplates(t *testing.T) {
     	// Perform additional checks on parsed templates.
     	staticFS := template.TrustedFSFromEmbed(static.FS)
    -	templates, err := templates.ParsePageTemplates(staticFS)
    +	templates, err := templates.ParsePageTemplates(staticFS, "")
     	if err != nil {
     		t.Fatal(err)
     	}
    @@ -82,7 +82,7 @@ func TestCheckFrontendTemplates(t *testing.T) {
     var templateFS = template.TrustedFSFromTrustedSource(template.TrustedSourceFromConstant("../../../static"))
     
     func TestCheckDocHTMLTemplates(t *testing.T) {
    -	dochtml.LoadTemplates(templateFS)
    +	dochtml.LoadTemplates(templateFS, "")
     	for _, tm := range dochtml.Templates() {
     		if err := templatecheck.CheckSafe(tm, dochtml.TemplateData{}); err != nil {
     			t.Fatal(err)
    diff --git a/internal/worker/server.go b/internal/worker/server.go
    index eaac081cb..468741312 100644
    --- a/internal/worker/server.go
    +++ b/internal/worker/server.go
    @@ -93,7 +93,7 @@ func NewServer(cfg *config.Config, scfg ServerConfig) (_ *Server, err error) {
     	}
     
     	tfs := template.TrustedFSFromTrustedSource(scfg.StaticPath)
    -	dochtml.LoadTemplates(tfs)
    +	dochtml.LoadTemplates(tfs, "")
     	var c *cache.Cache
     	if scfg.RedisCacheClient != nil {
     		c = cache.New(scfg.RedisCacheClient)
    diff --git a/internal/worker/server_test.go b/internal/worker/server_test.go
    index 427dce9e6..5072929e8 100644
    --- a/internal/worker/server_test.go
    +++ b/internal/worker/server_test.go
    @@ -37,7 +37,7 @@ var (
     
     func TestMain(m *testing.M) {
     	httpClient = &http.Client{Transport: fakeTransport{}}
    -	dochtml.LoadTemplates(template.TrustedFSFromTrustedSource(template.TrustedSourceFromConstant("../../static")))
    +	dochtml.LoadTemplates(template.TrustedFSFromTrustedSource(template.TrustedSourceFromConstant("../../static")), "")
     	testModules = proxytest.LoadTestModules("../proxy/testdata")
     	postgres.RunDBTests("discovery_worker_test", m, &testDB)
     }
    
    From 5c2293551e340329d6d06bdf057327c924148018 Mon Sep 17 00:00:00 2001
    From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com>
    Date: Thu, 7 May 2026 22:12:19 +0800
    Subject: [PATCH 13/26] =?UTF-8?q?chore:=20rename=20gogodocs=20=E2=86=92=20?=
     =?UTF-8?q?pkgsitex=20=E5=85=A8=E5=A5=97=E5=BC=95=E7=94=A8=E6=9B=BF?=
     =?UTF-8?q?=E6=8D=A2?=
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    
    GitHub repo 已 rename NickWilde18/gogodocs → NickWilde18/pkgsitex;
    本仓库内部 17 个文件里所有 \"gogodocs\" 字面量替换为 \"pkgsitex\",覆盖:
    
    - cmd/pkgsite/main.go:-base-path flag 默认值文案 + comment
    - cmd/internal/pkgsite/server.go:ServerConfig.BasePath 注释
    - internal/frontend/server.go / versions/versions.go / templates/templates.go
      / fetchserver/fetch.go:godoc 注释里举例 URL 全换
    - internal/godoc/render.go / dochtml/dochtml.go / dochtml/template.go:
      PackageURL 拼接逻辑注释 + IncludeUnexported 包级 var 注释
    - internal/godoc/dochtml/internal/render/markdown_ext.go:注释举例
    - static/frontend/unit/main/main.ts:localStorage key
      gogodocs:showUnexported → pkgsitex:showUnexported(影响:之前
      toggle 状态用户需要重新点一次切换)
    - static/frontend/about/index.ts、static/shared/base-path/base-path.ts:
      注释里举例 base path
    - Dockerfile:org.opencontainers.image.source label
    
    esbuild 已重 build 静态 bundle 同步反映 main.ts / about.ts 改动。
    
    不动 git commit history(之前的 commit message 历史保留 \"gogodocs\"
    字面量)。fork 仓库 PR #1 标题 / description 待 update。
    
    Smoke test:本地起 \`-base-path=/pkgsitex\`,主页 + chat.cuhksz module
    HTTP 200,渲染 HTML 残留 \"gogodocs\" 引用 0 个。
    ---
     Dockerfile                               | 2 +-
     cmd/internal/pkgsite/server.go           | 2 +-
     cmd/pkgsite/main.go                      | 6 +++---
     internal/frontend/fetchserver/fetch.go   | 2 +-
     internal/frontend/server.go              | 6 +++---
     internal/frontend/templates/templates.go | 6 +++---
     internal/frontend/versions/versions.go   | 2 +-
     internal/godoc/dochtml/dochtml.go        | 2 +-
     internal/godoc/dochtml/template.go       | 4 ++--
     internal/godoc/render.go                 | 2 +-
     static/frontend/about/index.js.map       | 2 +-
     static/frontend/about/index.ts           | 2 +-
     static/frontend/frontend.js.map          | 2 +-
     static/frontend/unit/main/main.js        | 2 +-
     static/frontend/unit/main/main.js.map    | 2 +-
     static/frontend/unit/main/main.ts        | 2 +-
     static/shared/base-path/base-path.ts     | 8 ++++----
     17 files changed, 27 insertions(+), 27 deletions(-)
    
    diff --git a/Dockerfile b/Dockerfile
    index 8a9282a67..27caf3e7f 100644
    --- a/Dockerfile
    +++ b/Dockerfile
    @@ -24,7 +24,7 @@ COPY . .
     RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /pkgsite ./cmd/pkgsite
     
     FROM golang:1.24
    -LABEL org.opencontainers.image.source="https://github.com/NickWilde18/gogodocs"
    +LABEL org.opencontainers.image.source="https://github.com/NickWilde18/pkgsitex"
     LABEL org.opencontainers.image.description="Self-hosted godoc browser (pkgsite fork) with base-path support"
     
     COPY --from=builder /pkgsite /usr/local/bin/pkgsite
    diff --git a/cmd/internal/pkgsite/server.go b/cmd/internal/pkgsite/server.go
    index 81e610a76..191e56bf7 100644
    --- a/cmd/internal/pkgsite/server.go
    +++ b/cmd/internal/pkgsite/server.go
    @@ -44,7 +44,7 @@ type ServerConfig struct {
     	GoDocMode             bool
     	RecordCodeWikiMetrics frontend.RecordClickFunc
     
    -	// BasePath:URL 前缀(如 "/gogodocs")让站点挂在子路径下。空 = 挂根。
    +	// BasePath:URL 前缀(如 "/pkgsitex")让站点挂在子路径下。空 = 挂根。
     	// 详见 cmd/pkgsite/main.go 的 -base-path flag。
     	BasePath string
     
    diff --git a/cmd/pkgsite/main.go b/cmd/pkgsite/main.go
    index d24544312..b924b7e40 100644
    --- a/cmd/pkgsite/main.go
    +++ b/cmd/pkgsite/main.go
    @@ -77,10 +77,10 @@ var (
     	goRepoPath = flag.String("gorepo", "", "path to Go repo on local filesystem")
     	useProxy   = flag.Bool("proxy", false, "fetch from GOPROXY if not found locally")
     	openFlag   = flag.Bool("open", false, "open a browser window to the server's address")
    -	// basePath:把整个站点挂在 URL 子路径下(如 -base-path=/gogodocs,
    -	// 站点入口 http://host/gogodocs/)。空字符串 = 默认挂根路径,跟上游一致。
    +	// basePath:把整个站点挂在 URL 子路径下(如 -base-path=/pkgsitex,
    +	// 站点入口 http://host/pkgsitex/)。空字符串 = 默认挂根路径,跟上游一致。
     	// fork 加入这个 flag 是为了让 pkgsite 能跟主网关共用域名(反代而非 subdomain)。
    -	basePath = flag.String("base-path", "", "URL prefix to mount the site under (e.g. /gogodocs). Must start with / and not end with /.")
    +	basePath = flag.String("base-path", "", "URL prefix to mount the site under (e.g. /pkgsitex). Must start with / and not end with /.")
     	// showUnexported:godoc 显示 unexported 符号(doc.AllDecls 模式)。fork
     	// 内网部署常见诉求——自家代码完整展示比 public-only 视图更有用。
     	showUnexported = flag.Bool("show-unexported", false, "Render documentation including unexported declarations (doc.AllDecls mode).")
    diff --git a/internal/frontend/fetchserver/fetch.go b/internal/frontend/fetchserver/fetch.go
    index 46acacbcd..baff89d41 100644
    --- a/internal/frontend/fetchserver/fetch.go
    +++ b/internal/frontend/fetchserver/fetch.go
    @@ -89,7 +89,7 @@ var (
     type FetchServer struct {
     	Queue                queue.Queue
     	TaskIDChangeInterval time.Duration
    -	// BasePath:站点 URL 子路径前缀(如 "/gogodocs")。空 = 挂根。
    +	// BasePath:站点 URL 子路径前缀(如 "/pkgsitex")。空 = 挂根。
     	// 用于 [ServePathNotFoundPage] 内部 http.Redirect 拼绝对 URL,否则
     	// /search?q=... 之类的重定向会跳到 reverse proxy 之外。
     	BasePath string
    diff --git a/internal/frontend/server.go b/internal/frontend/server.go
    index 59fe187ab..8504ea72f 100644
    --- a/internal/frontend/server.go
    +++ b/internal/frontend/server.go
    @@ -63,7 +63,7 @@ type Server struct {
     	instanceID            string
     	HTTPClient            *http.Client
     	recordCodeWikiMetrics RecordClickFunc
    -	// basePath:URL 前缀(如 "/gogodocs"),空字符串 = 默认挂根。
    +	// basePath:URL 前缀(如 "/pkgsitex"),空字符串 = 默认挂根。
     	// 由调用方通过 ServerConfig.BasePath 设置,并贯穿 mux pattern / StripPrefix /
     	// template "abs" 助手 / 内部 redirect / dochtml 链接拼接,使整站能整体迁移到子路径。
     	// 关于不变式:basePath 要么空、要么形如 "/foo"——尾部不带斜杠(拼 mux 时双斜杠会失配)。
    @@ -106,7 +106,7 @@ type ServerConfig struct {
     	VulndbClient          *vuln.Client
     	HTTPClient            *http.Client
     	RecordCodeWikiMetrics RecordClickFunc
    -	// BasePath:URL 子路径前缀(如 "/gogodocs"),空 = 挂根。详见 [Server.basePath]。
    +	// BasePath:URL 子路径前缀(如 "/pkgsitex"),空 = 挂根。详见 [Server.basePath]。
     	BasePath string
     }
     
    @@ -193,7 +193,7 @@ type Cacher interface {
     // 让 file server 能剥到正确的剩余 path。
     func (s *Server) Install(handle func(string, http.Handler), cacher Cacher, authValues []string) {
     	// shadow 入参 handle:所有 handle("GET /static/", ...) 自动变成
    -	// realHandle("GET /gogodocs/static/", ...),保持原代码零改动。
    +	// realHandle("GET /pkgsitex/static/", ...),保持原代码零改动。
     	// installDebugHandlers(handle) 透传时也用包装版本——_debug/pprof 等
     	// 调试端点同样落在 base-path 下。
     	realHandle := handle
    diff --git a/internal/frontend/templates/templates.go b/internal/frontend/templates/templates.go
    index a6c190e43..bb1994f9d 100644
    --- a/internal/frontend/templates/templates.go
    +++ b/internal/frontend/templates/templates.go
    @@ -44,8 +44,8 @@ func stripScheme(url string) string {
     // funcsWithBasePath 在内置 templateFuncs 之上叠两个 base-path 助手:
     //
     //   - `{{abs "/static/foo.svg"}}` → 静态绝对路径前置 BasePath,
    -//     站点挂根时输出 `/static/foo.svg`,挂 -base-path=/gogodocs 时输出
    -//     `/gogodocs/static/foo.svg`。必须以 / 开头;否则原样(让作者改时一目了然)。
    +//     站点挂根时输出 `/static/foo.svg`,挂 -base-path=/pkgsitex 时输出
    +//     `/pkgsitex/static/foo.svg`。必须以 / 开头;否则原样(让作者改时一目了然)。
     //     返回 [safehtml.TrustedResourceURL]——safehtml/template 不会校验,允许
     //     模板里跟 `?version={{.AppVersionLabel}}` 等动态 query 拼接(普通
     //     string return 会被 safehtml 拒绝,认为 ?version= 不是合法 URL prefix)。
    @@ -104,7 +104,7 @@ func funcsWithBasePath(basePath string) template.FuncMap {
     // ParsePageTemplates parses html templates contained in the given filesystem in
     // order to generate a map of Name->*template.Template.
     //
    -// basePath 形如 "/gogodocs" 或空字符串。空 = 站点挂根(pkg.go.dev 行为)。
    +// basePath 形如 "/pkgsitex" 或空字符串。空 = 站点挂根(pkg.go.dev 行为)。
     // 模板里通过 `{{abs "/static/foo.svg"}}` 输出带 prefix 的绝对 URL。
     //
     // Separate templates are used so that certain contextual functions (e.g.
    diff --git a/internal/frontend/versions/versions.go b/internal/frontend/versions/versions.go
    index 43056e859..80a6077ca 100644
    --- a/internal/frontend/versions/versions.go
    +++ b/internal/frontend/versions/versions.go
    @@ -430,7 +430,7 @@ func absoluteTime(date time.Time) string {
     	return date.In(time.UTC).Format("Jan _2, 2006")
     }
     
    -// BasePath 是 fork 加的包级 URL 前缀(如 "/gogodocs"),由 cmd/pkgsite/main.go
    +// BasePath 是 fork 加的包级 URL 前缀(如 "/pkgsitex"),由 cmd/pkgsite/main.go
     // 在 -base-path flag 处设置。空 = 默认挂根,跟上游零差异。
     //
     // [ConstructUnitURL] 是 pkgsite 内部所有 unit / package / module 详情页链接
    diff --git a/internal/godoc/dochtml/dochtml.go b/internal/godoc/dochtml/dochtml.go
    index 8407e6f4e..d3586ddbb 100644
    --- a/internal/godoc/dochtml/dochtml.go
    +++ b/internal/godoc/dochtml/dochtml.go
    @@ -286,7 +286,7 @@ func renderInfo(ctx context.Context, fset *token.FileSet, p *doc.Package, opt Re
     			if opt.BuildContext.GOOS != "" && opt.BuildContext.GOOS != "all" {
     				search = "?GOOS=" + opt.BuildContext.GOOS
     			}
    -			// basePath 在站点挂子路径时非空(如 "/gogodocs")——godoc 渲染的
    +			// basePath 在站点挂子路径时非空(如 "/pkgsitex")——godoc 渲染的
     			// cross-reference 链接(如 std lib ``)拼上前缀
     			// 才能在反代环境正确路由。
     			return basePath + "/" + versionedPath + search
    diff --git a/internal/godoc/dochtml/template.go b/internal/godoc/dochtml/template.go
    index 95e869e6d..0eb5bf7af 100644
    --- a/internal/godoc/dochtml/template.go
    +++ b/internal/godoc/dochtml/template.go
    @@ -22,7 +22,7 @@ var (
     	// then it becomes more viable to factor out inline CSS style.
     	bodyTemplate, outlineTemplate, sidenavTemplate *template.Template
     
    -	// basePath:单进程包级 URL 前缀(如 "/gogodocs"),由 [LoadTemplates] 写入;
    +	// basePath:单进程包级 URL 前缀(如 "/pkgsitex"),由 [LoadTemplates] 写入;
     	// 渲染 godoc cross-reference 时([dochtml.go] 的 PackageURL)拼到链接前。
     	// dochtml 包是单实例使用,存包级 var 可接受——pkgsite 单进程只有一个 BasePath。
     	basePath string
    @@ -38,7 +38,7 @@ func Templates() []*template.Template {
     
     // LoadTemplates reads and parses the templates used to generate documentation.
     //
    -// basePathPrefix 形如 "/gogodocs" 或空字符串。空 = 站点挂根(pkg.go.dev 行为)。
    +// basePathPrefix 形如 "/pkgsitex" 或空字符串。空 = 站点挂根(pkg.go.dev 行为)。
     // 仅在第一次调用生效(loadOnce 之后包级 templates 已 freeze),但 basePath
     // 每次调用都会覆盖——单进程 pkgsite 全程只配一个 BasePath,重复写无副作用。
     func LoadTemplates(fsys template.TrustedFS, basePathPrefix string) {
    diff --git a/internal/godoc/render.go b/internal/godoc/render.go
    index c509ab3aa..5c31344ce 100644
    --- a/internal/godoc/render.go
    +++ b/internal/godoc/render.go
    @@ -58,7 +58,7 @@ var IncludeUnexported bool
     //
     // 用途:cmd/pkgsite local mode 下,[Renderer] 生成 view source / file link 时
     // source.Info 走的是 `/files/{path}` 模板,不带 fork 站点子路径。挂
    -// -base-path=/gogodocs 时这些链接得变成 `/gogodocs/files/{path}` 才能正确路由
    +// -base-path=/pkgsitex 时这些链接得变成 `/pkgsitex/files/{path}` 才能正确路由
     // 到 file mux。在 [renderOptions] 的 fileLinkFunc / sourceLinkFunc 里识别 local
     // 模式(URL 以 "/files/" 起头)并 prefix。
     var BasePath string
    diff --git a/static/frontend/about/index.js.map b/static/frontend/about/index.js.map
    index 3304fc4ec..005aed200 100644
    --- a/static/frontend/about/index.js.map
    +++ b/static/frontend/about/index.js.map
    @@ -1,7 +1,7 @@
     {
       "version": 3,
       "sources": ["../../shared/base-path/base-path.ts", "index.ts"],
    -  "sourcesContent": ["/**\n * @license\n * Copyright 2024 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * \u7AD9\u70B9 URL \u5B50\u8DEF\u5F84\u524D\u7F00\uFF0C\u4ECE  \u8BFB\u51FA\u3002\n *\n * fork \u7528 -base-path=/gogodocs \u542F\u52A8\u65F6\uFF0Cserver \u7AEF\u6A21\u677F\u628A attribute \u5199\u6210\n * \"/gogodocs\"\uFF1B\u4E0A\u6E38\u516C\u7F51 pkg.go.dev \u9ED8\u8BA4\u6302\u6839\uFF0Cattribute \u4E3A\u7A7A\u5B57\u7B26\u4E32\u3002\n *\n * \u5728  \u4E4B\u5916\u7684\u811A\u672C\u521D\u59CB\u5316\u9636\u6BB5\u8C03\u7528\u90FD\u5B89\u5168\u2014\u2014documentElement \u4E00\u5B9A\u5B58\u5728\u3002\n */\nexport function getBasePath(): string {\n  return document.documentElement.dataset.basePath ?? '';\n}\n\n/**\n * \u7ED9\u4EE5 / \u5F00\u5934\u7684\u7EDD\u5BF9\u8DEF\u5F84\u52A0 BasePath \u524D\u7F00\u3002\n *\n * \u7528\u4E8E fetch URL / innerHTML src / location \u6BD4\u8F83\u7B49\u4EFB\u4F55\u9700\u8981\u62FC\u7AD9\u70B9\u7EDD\u5BF9 path\n * \u7684\u5730\u65B9\u3002\u975E\u7EDD\u5BF9\u8DEF\u5F84\uFF08\u4E0D\u4EE5 / \u5F00\u5934\uFF09\u539F\u6837\u8FD4\u56DE\u2014\u2014caller \u81EA\u5DF1\u4FDD\u8BC1\u8BED\u4E49\u3002\n *\n * \u4F8B\uFF1A\n *   abs('/play/share')       // \u2192 '/gogodocs/play/share' \u6216 '/play/share'\n *   abs('/static/foo.svg')   // \u2192 '/gogodocs/static/foo.svg' \u6216 '/static/foo.svg'\n *   abs('relative/x')        // \u2192 'relative/x'\uFF08\u4E0D\u52A8\uFF09\n */\nexport function abs(p: string): string {\n  if (!p.startsWith('/')) return p;\n  return getBasePath() + p;\n}\n", "/**\n * @license\n * Copyright 2022 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { abs } from '../../shared/base-path/base-path';\n\n/**\n * Left Navigation.\n */\nexport const initJumpLinks = async function () {\n  // pathname \u5728\u6302 -base-path=/gogodocs \u65F6\u662F \"/gogodocs/about\"\u2014\u2014\u6BD4\u8F83\u5217\u8868\u4E5F\u5F97\u5E26\u524D\u7F00\n  const pagesWithJumpLinks = [abs('/about')];\n  if (!pagesWithJumpLinks.includes(window.location.pathname)) {\n    // stop the file from doing anything else if the page doesn't have jumplinks\n    return;\n  }\n\n  // these might be generated or not so don't grab references to the elements until actually need them.\n  const titles = 'h2, h3, h4';\n  const nav = '.LeftNav a';\n  // these are always in the dom so we can get them now and throw errors if they're not.\n  const leftNav = document.querySelector('.LeftNav');\n  const siteContent = document.querySelector('.go-Content');\n  let isObserverDisabled = false;\n\n  /**\n   * El function\n   * @example el('h1', {className: 'title'}, 'Welcome to the site');\n   * @example el('ul', {className: 'list'}, el('li', {}, 'Item one'), el('li', {}, 'Item two'), el('li', {}, 'Item three'));\n   * @example el('img', {src: '/url.svg'});\n   */\n  function el(\n    type = '',\n    props: { [key: string]: string } = {},\n    ...children: (HTMLElement | HTMLElement[] | string | undefined)[]\n  ) {\n    // Error, no type declared.\n    if (!type) {\n      throw new Error('Provide `type` to create document element.');\n    }\n\n    // Create element with optional attribute props\n    const docEl = Object.assign(document.createElement(type), props);\n\n    // Children: array containing strings or elements\n    children.forEach(child => {\n      if (typeof child === 'string') {\n        docEl.appendChild(document.createTextNode(child));\n      } else if (Array.isArray(child)) {\n        child.forEach(c => docEl.appendChild(c));\n      } else if (child instanceof HTMLElement) {\n        docEl.appendChild(child);\n      }\n    });\n\n    return docEl;\n  }\n  /**  Build Nav if data hydrate is present. */\n  function buildNav() {\n    return new Promise((resolve, reject) => {\n      let navItems: { id: string; label: string; subnav?: { id: string; label: string }[] }[] = [];\n      let elements: HTMLElement[] = [];\n\n      if (!siteContent || !leftNav) {\n        return reject('.SiteContent not found.');\n      }\n      if (leftNav instanceof HTMLElement && !leftNav?.dataset?.hydrate) {\n        return resolve(true);\n      }\n\n      for (const title of siteContent.querySelectorAll(titles)) {\n        if (title instanceof HTMLElement && !title?.dataset?.ignore) {\n          switch (title.tagName) {\n            case 'H2':\n              navItems = [\n                ...navItems,\n                {\n                  id: title.id,\n                  label: title?.dataset?.title ? title.dataset.title : title.textContent ?? '',\n                },\n              ];\n              break;\n\n            case 'H3':\n            case 'H4':\n              if (!navItems[navItems.length - 1]?.subnav) {\n                navItems[navItems.length - 1].subnav = [\n                  {\n                    id: title.id,\n                    label: title?.dataset?.title ? title.dataset.title : title.textContent ?? '',\n                  },\n                ];\n              } else if (navItems[navItems.length - 1].subnav) {\n                navItems[navItems.length - 1].subnav?.push({\n                  id: title.id,\n                  label: title?.dataset?.title ? title.dataset.title : title.textContent ?? '',\n                });\n              }\n              break;\n          }\n        }\n      }\n\n      for (const navItem of navItems) {\n        const link = el('a', { href: '#' + navItem.id }, el('span', {}, navItem.label));\n        elements = [...elements, link];\n        if (navItem?.subnav) {\n          let subLinks: HTMLElement[] = [];\n          for (const subnavItem of navItem.subnav) {\n            const subItem = el(\n              'li',\n              {},\n              el(\n                'a',\n                { href: '#' + subnavItem.id },\n                el('img', { src: abs('/static/frontend/about/dot.svg'), width: '5', height: '5' }),\n                el('span', {}, subnavItem.label)\n              )\n            );\n            subLinks = [...subLinks, subItem];\n          }\n          const list = el('ul', { className: 'LeftSubnav' }, subLinks);\n          elements = [...elements, list];\n        }\n      }\n\n      elements.forEach(element => leftNav.appendChild(element));\n\n      return resolve(true);\n    });\n  }\n  /**\n   * Set the correct active element.\n   */\n  function setNav() {\n    return new Promise(resolve => {\n      if (!document.querySelectorAll(nav)) return resolve(true);\n      for (const a of document.querySelectorAll(nav)) {\n        if (a instanceof HTMLAnchorElement && a.href === location.href) {\n          setElementActive(a);\n          break;\n        }\n      }\n      resolve(true);\n    });\n  }\n  /** resetNav: removes all .active from nav elements */\n  function resetNav() {\n    return new Promise(resolve => {\n      if (!document.querySelectorAll(nav)) return resolve(true);\n      for (const a of document.querySelectorAll(nav)) {\n        a.classList.remove('active');\n      }\n      resolve(true);\n    });\n  }\n  /** setElementActive: controls resetting nav and highlighting the appropriate nav items */\n  function setElementActive(element: HTMLAnchorElement) {\n    if (element instanceof HTMLAnchorElement) {\n      resetNav().then(() => {\n        element.classList.add('active');\n        const parent = element?.parentNode?.parentNode;\n        if (parent instanceof HTMLElement && parent?.classList?.contains('LeftSubnav')) {\n          parent.previousElementSibling?.classList.add('active');\n        }\n      });\n    }\n  }\n  /** setLinkManually: disables observer and selects the clicked nav item. */\n  function setLinkManually() {\n    delayObserver();\n    const link = document.querySelector('[href=\"' + location.hash + '\"]');\n    if (link instanceof HTMLAnchorElement) {\n      setElementActive(link);\n    }\n  }\n  /** delayObserver: Quick on off switch for intersection observer. */\n  function delayObserver() {\n    isObserverDisabled = true;\n    setTimeout(() => {\n      isObserverDisabled = false;\n    }, 200);\n  }\n  /** observeSections: kicks off observation of titles as well as manual clicks with hashchange */\n  function observeSections() {\n    window.addEventListener('hashchange', setLinkManually);\n\n    if (siteContent?.querySelectorAll(titles)) {\n      const callback: IntersectionObserverCallback = entries => {\n        if (!isObserverDisabled && Array.isArray(entries) && entries.length > 0) {\n          for (const entry of entries) {\n            if (entry.isIntersecting && entry.target instanceof HTMLElement) {\n              const { id } = entry.target;\n              const link = document.querySelector('[href=\"#' + id + '\"]');\n              if (link instanceof HTMLAnchorElement) {\n                setElementActive(link);\n              }\n              break;\n            }\n          }\n        }\n      };\n      // rootMargin is important when multiple sections are in the observable area **on page load**.\n      // they will still be highlighted on scroll because of the root margin.\n      const ob = new IntersectionObserver(callback, {\n        threshold: 0,\n        rootMargin: '0px 0px -50% 0px',\n      });\n      for (const title of siteContent.querySelectorAll(titles)) {\n        if (title instanceof HTMLElement && !title?.dataset?.ignore) {\n          ob.observe(title);\n        }\n      }\n    }\n  }\n\n  try {\n    await buildNav();\n    await setNav();\n    if (location.hash) {\n      delayObserver();\n    }\n    observeSections();\n  } catch (e) {\n    if (e instanceof Error) {\n      console.error(e.message);\n    } else {\n      console.error(e);\n    }\n  }\n};\n"],
    +  "sourcesContent": ["/**\n * @license\n * Copyright 2024 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * \u7AD9\u70B9 URL \u5B50\u8DEF\u5F84\u524D\u7F00\uFF0C\u4ECE  \u8BFB\u51FA\u3002\n *\n * fork \u7528 -base-path=/pkgsitex \u542F\u52A8\u65F6\uFF0Cserver \u7AEF\u6A21\u677F\u628A attribute \u5199\u6210\n * \"/pkgsitex\"\uFF1B\u4E0A\u6E38\u516C\u7F51 pkg.go.dev \u9ED8\u8BA4\u6302\u6839\uFF0Cattribute \u4E3A\u7A7A\u5B57\u7B26\u4E32\u3002\n *\n * \u5728  \u4E4B\u5916\u7684\u811A\u672C\u521D\u59CB\u5316\u9636\u6BB5\u8C03\u7528\u90FD\u5B89\u5168\u2014\u2014documentElement \u4E00\u5B9A\u5B58\u5728\u3002\n */\nexport function getBasePath(): string {\n  return document.documentElement.dataset.basePath ?? '';\n}\n\n/**\n * \u7ED9\u4EE5 / \u5F00\u5934\u7684\u7EDD\u5BF9\u8DEF\u5F84\u52A0 BasePath \u524D\u7F00\u3002\n *\n * \u7528\u4E8E fetch URL / innerHTML src / location \u6BD4\u8F83\u7B49\u4EFB\u4F55\u9700\u8981\u62FC\u7AD9\u70B9\u7EDD\u5BF9 path\n * \u7684\u5730\u65B9\u3002\u975E\u7EDD\u5BF9\u8DEF\u5F84\uFF08\u4E0D\u4EE5 / \u5F00\u5934\uFF09\u539F\u6837\u8FD4\u56DE\u2014\u2014caller \u81EA\u5DF1\u4FDD\u8BC1\u8BED\u4E49\u3002\n *\n * \u4F8B\uFF1A\n *   abs('/play/share')       // \u2192 '/pkgsitex/play/share' \u6216 '/play/share'\n *   abs('/static/foo.svg')   // \u2192 '/pkgsitex/static/foo.svg' \u6216 '/static/foo.svg'\n *   abs('relative/x')        // \u2192 'relative/x'\uFF08\u4E0D\u52A8\uFF09\n */\nexport function abs(p: string): string {\n  if (!p.startsWith('/')) return p;\n  return getBasePath() + p;\n}\n", "/**\n * @license\n * Copyright 2022 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { abs } from '../../shared/base-path/base-path';\n\n/**\n * Left Navigation.\n */\nexport const initJumpLinks = async function () {\n  // pathname \u5728\u6302 -base-path=/pkgsitex \u65F6\u662F \"/pkgsitex/about\"\u2014\u2014\u6BD4\u8F83\u5217\u8868\u4E5F\u5F97\u5E26\u524D\u7F00\n  const pagesWithJumpLinks = [abs('/about')];\n  if (!pagesWithJumpLinks.includes(window.location.pathname)) {\n    // stop the file from doing anything else if the page doesn't have jumplinks\n    return;\n  }\n\n  // these might be generated or not so don't grab references to the elements until actually need them.\n  const titles = 'h2, h3, h4';\n  const nav = '.LeftNav a';\n  // these are always in the dom so we can get them now and throw errors if they're not.\n  const leftNav = document.querySelector('.LeftNav');\n  const siteContent = document.querySelector('.go-Content');\n  let isObserverDisabled = false;\n\n  /**\n   * El function\n   * @example el('h1', {className: 'title'}, 'Welcome to the site');\n   * @example el('ul', {className: 'list'}, el('li', {}, 'Item one'), el('li', {}, 'Item two'), el('li', {}, 'Item three'));\n   * @example el('img', {src: '/url.svg'});\n   */\n  function el(\n    type = '',\n    props: { [key: string]: string } = {},\n    ...children: (HTMLElement | HTMLElement[] | string | undefined)[]\n  ) {\n    // Error, no type declared.\n    if (!type) {\n      throw new Error('Provide `type` to create document element.');\n    }\n\n    // Create element with optional attribute props\n    const docEl = Object.assign(document.createElement(type), props);\n\n    // Children: array containing strings or elements\n    children.forEach(child => {\n      if (typeof child === 'string') {\n        docEl.appendChild(document.createTextNode(child));\n      } else if (Array.isArray(child)) {\n        child.forEach(c => docEl.appendChild(c));\n      } else if (child instanceof HTMLElement) {\n        docEl.appendChild(child);\n      }\n    });\n\n    return docEl;\n  }\n  /**  Build Nav if data hydrate is present. */\n  function buildNav() {\n    return new Promise((resolve, reject) => {\n      let navItems: { id: string; label: string; subnav?: { id: string; label: string }[] }[] = [];\n      let elements: HTMLElement[] = [];\n\n      if (!siteContent || !leftNav) {\n        return reject('.SiteContent not found.');\n      }\n      if (leftNav instanceof HTMLElement && !leftNav?.dataset?.hydrate) {\n        return resolve(true);\n      }\n\n      for (const title of siteContent.querySelectorAll(titles)) {\n        if (title instanceof HTMLElement && !title?.dataset?.ignore) {\n          switch (title.tagName) {\n            case 'H2':\n              navItems = [\n                ...navItems,\n                {\n                  id: title.id,\n                  label: title?.dataset?.title ? title.dataset.title : title.textContent ?? '',\n                },\n              ];\n              break;\n\n            case 'H3':\n            case 'H4':\n              if (!navItems[navItems.length - 1]?.subnav) {\n                navItems[navItems.length - 1].subnav = [\n                  {\n                    id: title.id,\n                    label: title?.dataset?.title ? title.dataset.title : title.textContent ?? '',\n                  },\n                ];\n              } else if (navItems[navItems.length - 1].subnav) {\n                navItems[navItems.length - 1].subnav?.push({\n                  id: title.id,\n                  label: title?.dataset?.title ? title.dataset.title : title.textContent ?? '',\n                });\n              }\n              break;\n          }\n        }\n      }\n\n      for (const navItem of navItems) {\n        const link = el('a', { href: '#' + navItem.id }, el('span', {}, navItem.label));\n        elements = [...elements, link];\n        if (navItem?.subnav) {\n          let subLinks: HTMLElement[] = [];\n          for (const subnavItem of navItem.subnav) {\n            const subItem = el(\n              'li',\n              {},\n              el(\n                'a',\n                { href: '#' + subnavItem.id },\n                el('img', { src: abs('/static/frontend/about/dot.svg'), width: '5', height: '5' }),\n                el('span', {}, subnavItem.label)\n              )\n            );\n            subLinks = [...subLinks, subItem];\n          }\n          const list = el('ul', { className: 'LeftSubnav' }, subLinks);\n          elements = [...elements, list];\n        }\n      }\n\n      elements.forEach(element => leftNav.appendChild(element));\n\n      return resolve(true);\n    });\n  }\n  /**\n   * Set the correct active element.\n   */\n  function setNav() {\n    return new Promise(resolve => {\n      if (!document.querySelectorAll(nav)) return resolve(true);\n      for (const a of document.querySelectorAll(nav)) {\n        if (a instanceof HTMLAnchorElement && a.href === location.href) {\n          setElementActive(a);\n          break;\n        }\n      }\n      resolve(true);\n    });\n  }\n  /** resetNav: removes all .active from nav elements */\n  function resetNav() {\n    return new Promise(resolve => {\n      if (!document.querySelectorAll(nav)) return resolve(true);\n      for (const a of document.querySelectorAll(nav)) {\n        a.classList.remove('active');\n      }\n      resolve(true);\n    });\n  }\n  /** setElementActive: controls resetting nav and highlighting the appropriate nav items */\n  function setElementActive(element: HTMLAnchorElement) {\n    if (element instanceof HTMLAnchorElement) {\n      resetNav().then(() => {\n        element.classList.add('active');\n        const parent = element?.parentNode?.parentNode;\n        if (parent instanceof HTMLElement && parent?.classList?.contains('LeftSubnav')) {\n          parent.previousElementSibling?.classList.add('active');\n        }\n      });\n    }\n  }\n  /** setLinkManually: disables observer and selects the clicked nav item. */\n  function setLinkManually() {\n    delayObserver();\n    const link = document.querySelector('[href=\"' + location.hash + '\"]');\n    if (link instanceof HTMLAnchorElement) {\n      setElementActive(link);\n    }\n  }\n  /** delayObserver: Quick on off switch for intersection observer. */\n  function delayObserver() {\n    isObserverDisabled = true;\n    setTimeout(() => {\n      isObserverDisabled = false;\n    }, 200);\n  }\n  /** observeSections: kicks off observation of titles as well as manual clicks with hashchange */\n  function observeSections() {\n    window.addEventListener('hashchange', setLinkManually);\n\n    if (siteContent?.querySelectorAll(titles)) {\n      const callback: IntersectionObserverCallback = entries => {\n        if (!isObserverDisabled && Array.isArray(entries) && entries.length > 0) {\n          for (const entry of entries) {\n            if (entry.isIntersecting && entry.target instanceof HTMLElement) {\n              const { id } = entry.target;\n              const link = document.querySelector('[href=\"#' + id + '\"]');\n              if (link instanceof HTMLAnchorElement) {\n                setElementActive(link);\n              }\n              break;\n            }\n          }\n        }\n      };\n      // rootMargin is important when multiple sections are in the observable area **on page load**.\n      // they will still be highlighted on scroll because of the root margin.\n      const ob = new IntersectionObserver(callback, {\n        threshold: 0,\n        rootMargin: '0px 0px -50% 0px',\n      });\n      for (const title of siteContent.querySelectorAll(titles)) {\n        if (title instanceof HTMLElement && !title?.dataset?.ignore) {\n          ob.observe(title);\n        }\n      }\n    }\n  }\n\n  try {\n    await buildNav();\n    await setNav();\n    if (location.hash) {\n      delayObserver();\n    }\n    observeSections();\n  } catch (e) {\n    if (e instanceof Error) {\n      console.error(e.message);\n    } else {\n      console.error(e);\n    }\n  }\n};\n"],
       "mappings": "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",
       "names": ["getBasePath", "_a", "abs", "p", "initJumpLinks", "abs", "titles", "nav", "leftNav", "siteContent", "isObserverDisabled", "el", "type", "props", "children", "docEl", "child", "c", "buildNav", "resolve", "reject", "_a", "_b", "_c", "_d", "_e", "_f", "_g", "_h", "_i", "_j", "navItems", "elements", "title", "navItem", "link", "subLinks", "subnavItem", "subItem", "list", "element", "setNav", "a", "setElementActive", "resetNav", "parent", "setLinkManually", "delayObserver", "observeSections", "callback", "entries", "entry", "id", "ob", "e"]
     }
    diff --git a/static/frontend/about/index.ts b/static/frontend/about/index.ts
    index 6f2ea53a3..0e08d1605 100644
    --- a/static/frontend/about/index.ts
    +++ b/static/frontend/about/index.ts
    @@ -11,7 +11,7 @@ import { abs } from '../../shared/base-path/base-path';
      * Left Navigation.
      */
     export const initJumpLinks = async function () {
    -  // pathname 在挂 -base-path=/gogodocs 时是 "/gogodocs/about"——比较列表也得带前缀
    +  // pathname 在挂 -base-path=/pkgsitex 时是 "/pkgsitex/about"——比较列表也得带前缀
       const pagesWithJumpLinks = [abs('/about')];
       if (!pagesWithJumpLinks.includes(window.location.pathname)) {
         // stop the file from doing anything else if the page doesn't have jumplinks
    diff --git a/static/frontend/frontend.js.map b/static/frontend/frontend.js.map
    index dca67f047..0b262a0fd 100644
    --- a/static/frontend/frontend.js.map
    +++ b/static/frontend/frontend.js.map
    @@ -1,7 +1,7 @@
     {
       "version": 3,
       "sources": ["../shared/header/header.ts", "../shared/base-path/base-path.ts", "../shared/carousel/carousel.ts", "../shared/clipboard/clipboard.ts", "../shared/tooltip/tooltip.ts", "../shared/outline/select.ts", "../shared/modal/modal.ts", "../shared/analytics/analytics.ts", "../shared/keyboard/keyboard.ts", "../shared/jump/jump.ts", "about/index.ts", "frontend.ts"],
    -  "sourcesContent": ["/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nexport function registerHeaderListeners(): void {\n  const header = document.querySelector('.js-header') as HTMLElement;\n\n  // Desktop menu hover state\n  const menuItemHovers = document.querySelectorAll('.js-desktop-menu-hover');\n  menuItemHovers.forEach(menuItemHover => {\n    // when user clicks on the dropdown menu item on desktop or mobile,\n    // force the menu to stay open until the user clicks off of it.\n    menuItemHover.addEventListener('mouseenter', e => {\n      const target = e.target as HTMLElement;\n      const forced = document.querySelector('.forced-open') as HTMLElement;\n      if (forced && forced !== menuItemHover) {\n        forced.blur();\n        forced.classList.remove('forced-open');\n      }\n      // prevents menus that have been tabbed into from staying open\n      // when you hover over another menu\n      target.classList.remove('forced-closed');\n      target.classList.add('forced-open');\n    });\n\n    const toggleForcedOpen = (e: Event) => {\n      const target = e.target as HTMLElement;\n      const isForced = target?.classList.contains('forced-open');\n      const currentTarget = e.currentTarget as HTMLElement;\n      if (isForced) {\n        currentTarget.removeEventListener('blur', () =>\n          currentTarget.classList.remove('forced-open')\n        );\n        currentTarget.classList.remove('forced-open');\n        currentTarget.classList.add('forced-closed');\n        currentTarget.blur();\n        currentTarget?.parentNode?.addEventListener('mouseout', () => {\n          currentTarget.classList.remove('forced-closed');\n        });\n      } else {\n        currentTarget.classList.remove('forced-closed');\n        currentTarget.classList.add('forced-open');\n        currentTarget.focus();\n        currentTarget.addEventListener('blur', () => currentTarget.classList.remove('forced-open'));\n        currentTarget?.parentNode?.removeEventListener('mouseout', () => {\n          currentTarget.classList.remove('forced-closed');\n        });\n      }\n      currentTarget.focus();\n    };\n    menuItemHover.addEventListener('click', toggleForcedOpen);\n    menuItemHover.addEventListener('focus', e => {\n      const target = e.target as HTMLElement;\n      target.classList.add('forced-closed');\n      target.classList.remove('forced-open');\n    });\n\n    // ensure desktop submenus are closed when esc is pressed\n    const closeSubmenuOnEsc = (e: Event) => {\n      const event = e as KeyboardEvent;\n      const target = e.target as HTMLElement;\n      if (event.key === 'Escape') {\n        const forcedOpenItem = document.querySelector('.forced-open') as HTMLElement;\n        if (forcedOpenItem) {\n          forcedOpenItem.classList.remove('forced-open');\n          forcedOpenItem.blur();\n          forcedOpenItem.classList.add('forced-closed');\n          target?.focus();\n        }\n      }\n    };\n    document.addEventListener('keydown', closeSubmenuOnEsc);\n  });\n\n  // Mobile menu subnav menus\n  const headerbuttons = document.querySelectorAll('.js-headerMenuButton');\n  headerbuttons.forEach(button => {\n    button.addEventListener('click', e => {\n      e.preventDefault();\n      const isActive = header?.classList.contains('is-active');\n      if (isActive) {\n        handleNavigationDrawerInactive(header);\n      } else {\n        handleNavigationDrawerActive(header);\n      }\n      button.setAttribute('aria-expanded', isActive ? 'true' : 'false');\n    });\n  });\n\n  const scrim = document.querySelector('.js-scrim');\n  scrim?.addEventListener('click', e => {\n    e.preventDefault();\n\n    // find any active submenus and close them\n    const activeSubnavs = document.querySelectorAll('.go-NavigationDrawer-submenuItem.is-active');\n    activeSubnavs.forEach(subnav => handleNavigationDrawerInactive(subnav as HTMLElement));\n\n    handleNavigationDrawerInactive(header);\n\n    headerbuttons.forEach(button => {\n      button.setAttribute(\n        'aria-expanded',\n        header?.classList.contains('is-active') ? 'true' : 'false'\n      );\n    });\n  });\n\n  const getNavigationDrawerMenuItems = (navigationDrawer: HTMLElement): HTMLElement[] => {\n    if (!navigationDrawer) {\n      return [];\n    }\n\n    const menuItems = Array.from(\n      navigationDrawer.querySelectorAll(\n        ':scope > .go-NavigationDrawer-nav > .go-NavigationDrawer-list > .go-NavigationDrawer-listItem > a, :scope > .go-NavigationDrawer-nav > .go-NavigationDrawer-list > .go-NavigationDrawer-listItem > .go-Header-socialIcons > a'\n      ) || []\n    );\n\n    const anchorEl = navigationDrawer.querySelector('.go-NavigationDrawer-header > a');\n    if (anchorEl) {\n      menuItems.unshift(anchorEl);\n    }\n    return menuItems as HTMLElement[];\n  };\n\n  const getNavigationDrawerIsSubnav = (navigationDrawer: HTMLElement) => {\n    if (!navigationDrawer) {\n      return;\n    }\n    return navigationDrawer.classList.contains('go-NavigationDrawer-submenuItem');\n  };\n\n  const handleNavigationDrawerInactive = (navigationDrawer: HTMLElement) => {\n    if (!navigationDrawer) {\n      return;\n    }\n    const menuItems = getNavigationDrawerMenuItems(navigationDrawer);\n    navigationDrawer.classList.remove('is-active');\n    const parentMenuItem = navigationDrawer\n      .closest('.go-NavigationDrawer-listItem')\n      ?.querySelector(':scope > a') as HTMLElement;\n    parentMenuItem?.focus();\n    menuItems?.forEach(item => item?.setAttribute('tabindex', '-1'));\n    if (menuItems && menuItems[0]) {\n      menuItems[0].removeEventListener('keydown', handleMenuItemTabLeftFactory(navigationDrawer));\n      menuItems[menuItems.length - 1].removeEventListener(\n        'keydown',\n        handleMenuItemTabRightFactory(navigationDrawer)\n      );\n    }\n\n    if (navigationDrawer === header) {\n      headerbuttons && (headerbuttons[0] as HTMLElement)?.focus();\n    }\n  };\n\n  const handleNavigationDrawerActive = (navigationDrawer: HTMLElement) => {\n    const menuItems = getNavigationDrawerMenuItems(navigationDrawer);\n\n    navigationDrawer.classList.add('is-active');\n    menuItems.forEach(item => item.setAttribute('tabindex', '0'));\n    menuItems[0].focus();\n\n    menuItems[0].addEventListener('keydown', handleMenuItemTabLeftFactory(navigationDrawer));\n    menuItems[menuItems.length - 1].addEventListener(\n      'keydown',\n      handleMenuItemTabRightFactory(navigationDrawer)\n    );\n  };\n\n  const handleMenuItemTabLeftFactory = (navigationDrawer: HTMLElement) => {\n    return (e: KeyboardEvent) => {\n      if (e.key === 'Tab' && e.shiftKey) {\n        e.preventDefault();\n        handleNavigationDrawerInactive(navigationDrawer);\n      }\n    };\n  };\n\n  const handleMenuItemTabRightFactory = (navigationDrawer: HTMLElement) => {\n    return (e: KeyboardEvent) => {\n      if (e.key === 'Tab' && !e.shiftKey) {\n        e.preventDefault();\n        handleNavigationDrawerInactive(navigationDrawer);\n      }\n    };\n  };\n\n  const prepMobileNavigationDrawer = (navigationDrawer: HTMLElement) => {\n    const isSubnav = getNavigationDrawerIsSubnav(navigationDrawer);\n    const menuItems = getNavigationDrawerMenuItems(navigationDrawer);\n    navigationDrawer.addEventListener('keyup', e => {\n      if (e.key === 'Escape') {\n        handleNavigationDrawerInactive(navigationDrawer);\n      }\n    });\n\n    menuItems.forEach(item => {\n      const parentLi = item.closest('li');\n      if (parentLi && parentLi.classList.contains('js-mobile-subnav-trigger')) {\n        const submenu = parentLi.querySelector('.go-NavigationDrawer-submenuItem') as HTMLElement;\n        item.addEventListener('click', () => {\n          handleNavigationDrawerActive(submenu);\n        });\n      }\n    });\n    if (isSubnav) {\n      handleNavigationDrawerInactive(navigationDrawer);\n      navigationDrawer\n        ?.querySelector('.go-NavigationDrawer-header')\n        ?.addEventListener('click', e => {\n          e.preventDefault();\n          handleNavigationDrawerInactive(navigationDrawer);\n        });\n    }\n  };\n\n  document\n    .querySelectorAll('.go-NavigationDrawer')\n    .forEach(drawer => prepMobileNavigationDrawer(drawer as HTMLElement));\n\n  handleNavigationDrawerInactive(header);\n}\n\nexport function registerSearchFormListeners(): void {\n  const searchForm = document.querySelector('.js-searchForm');\n  const expandSearch = document.querySelector('.js-expandSearch');\n  const input = searchForm?.querySelector('input');\n  const headerLogo = document.querySelector('.js-headerLogo');\n  const menuButton = document.querySelector('.js-headerMenuButton');\n  expandSearch?.addEventListener('click', () => {\n    searchForm?.classList.add('go-SearchForm--expanded');\n    headerLogo?.classList.add('go-Header-logo--hidden');\n    menuButton?.classList.add('go-Header-navOpen--hidden');\n    input?.focus();\n  });\n  document?.addEventListener('click', e => {\n    if (!searchForm?.contains(e.target as Node)) {\n      searchForm?.classList.remove('go-SearchForm--expanded');\n      headerLogo?.classList.remove('go-Header-logo--hidden');\n      menuButton?.classList.remove('go-Header-navOpen--hidden');\n    }\n  });\n}\n", "/**\n * @license\n * Copyright 2024 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * \u7AD9\u70B9 URL \u5B50\u8DEF\u5F84\u524D\u7F00\uFF0C\u4ECE  \u8BFB\u51FA\u3002\n *\n * fork \u7528 -base-path=/gogodocs \u542F\u52A8\u65F6\uFF0Cserver \u7AEF\u6A21\u677F\u628A attribute \u5199\u6210\n * \"/gogodocs\"\uFF1B\u4E0A\u6E38\u516C\u7F51 pkg.go.dev \u9ED8\u8BA4\u6302\u6839\uFF0Cattribute \u4E3A\u7A7A\u5B57\u7B26\u4E32\u3002\n *\n * \u5728  \u4E4B\u5916\u7684\u811A\u672C\u521D\u59CB\u5316\u9636\u6BB5\u8C03\u7528\u90FD\u5B89\u5168\u2014\u2014documentElement \u4E00\u5B9A\u5B58\u5728\u3002\n */\nexport function getBasePath(): string {\n  return document.documentElement.dataset.basePath ?? '';\n}\n\n/**\n * \u7ED9\u4EE5 / \u5F00\u5934\u7684\u7EDD\u5BF9\u8DEF\u5F84\u52A0 BasePath \u524D\u7F00\u3002\n *\n * \u7528\u4E8E fetch URL / innerHTML src / location \u6BD4\u8F83\u7B49\u4EFB\u4F55\u9700\u8981\u62FC\u7AD9\u70B9\u7EDD\u5BF9 path\n * \u7684\u5730\u65B9\u3002\u975E\u7EDD\u5BF9\u8DEF\u5F84\uFF08\u4E0D\u4EE5 / \u5F00\u5934\uFF09\u539F\u6837\u8FD4\u56DE\u2014\u2014caller \u81EA\u5DF1\u4FDD\u8BC1\u8BED\u4E49\u3002\n *\n * \u4F8B\uFF1A\n *   abs('/play/share')       // \u2192 '/gogodocs/play/share' \u6216 '/play/share'\n *   abs('/static/foo.svg')   // \u2192 '/gogodocs/static/foo.svg' \u6216 '/static/foo.svg'\n *   abs('relative/x')        // \u2192 'relative/x'\uFF08\u4E0D\u52A8\uFF09\n */\nexport function abs(p: string): string {\n  if (!p.startsWith('/')) return p;\n  return getBasePath() + p;\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { getBasePath } from '../base-path/base-path';\n\n/**\n * Carousel Controller adds event listeners, accessibility enhancements, and\n * control elements to a carousel component.\n */\nexport class CarouselController {\n  /**\n   * slides is a collection of slides in the carousel.\n   */\n  private slides: HTMLLIElement[];\n  /**\n   * dots is a collection of dot navigation controls, added to the carousel\n   * by this controller.\n   */\n  private dots: HTMLElement[];\n  /**\n   * liveRegion is a visually hidden element that notifies assitive devices\n   * of visual changes to the carousel. They are added to the carousel by\n   * this controller.\n   */\n  private liveRegion: HTMLElement;\n  /**\n   * activeIndex is the 0-index of the currently active slide.\n   */\n  private activeIndex: number;\n\n  constructor(private el: HTMLElement) {\n    this.slides = Array.from(el.querySelectorAll('.go-Carousel-slide'));\n    this.dots = [];\n    this.liveRegion = document.createElement('div');\n    this.activeIndex = Number(el.getAttribute('data-slide-index') ?? 0);\n\n    this.initSlides();\n    this.initArrows();\n    this.initDots();\n    this.initLiveRegion();\n  }\n\n  private initSlides() {\n    for (const [i, v] of this.slides.entries()) {\n      if (i === this.activeIndex) continue;\n      v.setAttribute('aria-hidden', 'true');\n    }\n  }\n\n  private initArrows() {\n    const arrows = document.createElement('ul');\n    arrows.classList.add('go-Carousel-arrows');\n    // base path \u901A\u8FC7 [getBasePath]() \u6CE8\u5165\uFF1B\u6302\u6839\u65F6\u8FD4\u7A7A\u5B57\u7B26\u4E32\uFF0C\u5BF9\u4E0A\u6E38\u96F6\u5DEE\u5F02\u3002\n    const bp = getBasePath();\n    arrows.innerHTML = `\n      
  • \n \n
  • \n
  • \n \n
  • \n `;\n arrows\n .querySelector('.go-Carousel-prevSlide')\n ?.addEventListener('click', () => this.setActive(this.activeIndex - 1));\n arrows\n .querySelector('.go-Carousel-nextSlide')\n ?.addEventListener('click', () => this.setActive(this.activeIndex + 1));\n this.el.append(arrows);\n }\n\n private initDots() {\n const dots = document.createElement('ul');\n dots.classList.add('go-Carousel-dots');\n for (let i = 0; i < this.slides.length; i++) {\n const li = document.createElement('li');\n const button = document.createElement('button');\n button.classList.add('go-Carousel-dot');\n if (i === this.activeIndex) {\n button.classList.add('go-Carousel-dot--active');\n }\n button.innerHTML = `Slide ${i + 1}`;\n button.addEventListener('click', () => this.setActive(i));\n li.append(button);\n dots.append(li);\n this.dots.push(button);\n }\n this.el.append(dots);\n }\n\n private initLiveRegion() {\n this.liveRegion.setAttribute('aria-live', 'polite');\n this.liveRegion.setAttribute('aria-atomic', 'true');\n this.liveRegion.setAttribute('class', 'go-Carousel-obscured');\n this.liveRegion.textContent = `Slide ${this.activeIndex + 1} of ${this.slides.length}`;\n this.el.appendChild(this.liveRegion);\n }\n\n private setActive = (index: number) => {\n this.activeIndex = (index + this.slides.length) % this.slides.length;\n this.el.setAttribute('data-slide-index', String(this.activeIndex));\n for (const d of this.dots) {\n d.classList.remove('go-Carousel-dot--active');\n }\n this.dots[this.activeIndex].classList.add('go-Carousel-dot--active');\n for (const s of this.slides) {\n s.setAttribute('aria-hidden', 'true');\n }\n this.slides[this.activeIndex].removeAttribute('aria-hidden');\n this.liveRegion.textContent = 'Slide ' + (this.activeIndex + 1) + ' of ' + this.slides.length;\n };\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * This class decorates an element to copy arbitrary data attached via a data-\n * attribute to the clipboard.\n */\nexport class ClipboardController {\n /**\n * The data to be copied to the clipboard.\n */\n private data: string;\n\n /**\n * @param el The element that will trigger copying text to the clipboard. The text is\n * expected to be within its data-to-copy attribute.\n */\n constructor(private el: HTMLButtonElement) {\n this.data = el.dataset['toCopy'] ?? el.innerText;\n // if data-to-copy is empty and the button is part of an input group\n // capture the value of the input.\n if (!this.data && el.parentElement?.classList.contains('go-InputGroup')) {\n this.data = (this.data || el.parentElement?.querySelector('input')?.value) ?? '';\n }\n el.addEventListener('click', e => this.handleCopyClick(e));\n }\n\n /**\n * Handles when the primary element is clicked.\n */\n handleCopyClick(e: MouseEvent): void {\n e.preventDefault();\n const TOOLTIP_SHOW_DURATION_MS = 1000;\n\n // This API is not available on iOS.\n if (!navigator.clipboard) {\n this.showTooltipText('Unable to copy', TOOLTIP_SHOW_DURATION_MS);\n return;\n }\n navigator.clipboard\n .writeText(this.data)\n .then(() => {\n this.showTooltipText('Copied!', TOOLTIP_SHOW_DURATION_MS);\n })\n .catch(() => {\n this.showTooltipText('Unable to copy', TOOLTIP_SHOW_DURATION_MS);\n });\n }\n\n /**\n * Shows the given text in a tooltip for a specified amount of time, in milliseconds.\n */\n showTooltipText(text: string, durationMs: number): void {\n this.el.setAttribute('data-tooltip', text);\n setTimeout(() => this.el.setAttribute('data-tooltip', ''), durationMs);\n }\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * ToolTipController handles closing tooltips on external clicks.\n */\nexport class ToolTipController {\n constructor(private el: HTMLDetailsElement) {\n document.addEventListener('click', e => {\n const insideTooltip = this.el.contains(e.target as Element);\n if (!insideTooltip) {\n this.el.removeAttribute('open');\n }\n });\n\n // Add event listener for \"Escape\" keydown to close tooltip\n this.el.addEventListener('keydown', e => {\n if (e.key === 'Escape') {\n this.el.open = false;\n }\n });\n }\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { TreeNavController } from './tree.js';\n\nexport class SelectNavController {\n constructor(private el: Element) {\n this.el.addEventListener('change', e => {\n const target = e.target as HTMLSelectElement;\n let href = target.value;\n if (!target.value.startsWith('/')) {\n href = '/' + href;\n }\n window.location.href = href;\n });\n }\n}\n\nexport function makeSelectNav(tree: TreeNavController): HTMLLabelElement {\n const label = document.createElement('label');\n label.classList.add('go-Label');\n label.setAttribute('aria-label', 'Menu');\n const select = document.createElement('select');\n select.classList.add('go-Select', 'js-selectNav');\n label.appendChild(select);\n const outline = document.createElement('optgroup');\n outline.label = 'Outline';\n select.appendChild(outline);\n const groupMap: Record = {};\n let group: HTMLOptGroupElement;\n for (const t of tree.treeitems) {\n if (Number(t.depth) > 4) continue;\n if (t.groupTreeitem) {\n group = groupMap[t.groupTreeitem.label];\n if (!group) {\n group = groupMap[t.groupTreeitem.label] = document.createElement('optgroup');\n group.label = t.groupTreeitem.label;\n select.appendChild(group);\n }\n } else {\n group = outline;\n }\n const o = document.createElement('option');\n o.label = t.label;\n o.textContent = t.label;\n o.value = (t.el as HTMLAnchorElement).href.replace(window.location.origin, '').replace('/', '');\n group.appendChild(o);\n }\n tree.addObserver(t => {\n const hash = (t.el as HTMLAnchorElement).hash;\n const value = select.querySelector(`[value$=\"${hash}\"]`)?.value;\n if (value) {\n select.value = value;\n }\n }, 50);\n return label;\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\ninterface Window {\n dialogPolyfill?: {\n registerDialog: (el: HTMLDialogElement) => void;\n };\n}\n\ndeclare const window: Window;\n\n/**\n * ModalController registers a dialog element with the polyfill if\n * necessary for the current browser, add adds event listeners to\n * close and open modals.\n */\nexport class ModalController {\n constructor(private el: HTMLDialogElement) {\n if (window.dialogPolyfill) {\n window.dialogPolyfill.registerDialog(el);\n }\n this.init();\n }\n\n init() {\n const button = document.querySelector(`[aria-controls=\"${this.el.id}\"]`);\n if (button) {\n button.addEventListener('click', () => {\n if (this.el.showModal) {\n this.el.showModal();\n } else {\n this.el.setAttribute('opened', 'true');\n }\n this.el.querySelector('input')?.focus();\n });\n }\n for (const btn of this.el.querySelectorAll('[data-modal-close]')) {\n btn.addEventListener('click', () => {\n if (this.el.close) {\n this.el.close();\n } else {\n this.el.removeAttribute('opened');\n }\n });\n }\n }\n}\n", "interface TagManagerEvent {\n /**\n * event is the name of the event, used to filter events in\n * Google Analytics.\n */\n event: string;\n\n /**\n * event_category is a name that you supply as a way to group objects\n * that to analyze. Typically, you will use the same category name\n * multiple times over related UI elements (buttons, links, etc).\n */\n event_category?: string;\n\n /**\n * event_action is used to name the type of event or interaction you\n * want to measure for a particular web object. For example, with a\n * single \"form\" category, you can analyze a number of specific events\n * with this parameter, such as: form entered, form submitted.\n */\n event_action?: string;\n\n /**\n * event_label provide additional information for events that you want\n * to analyze, such as the text label of a link.\n */\n event_label?: string;\n\n /**\n * gtm.start is used to initialize Google Tag Manager.\n */\n 'gtm.start'?: number;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\ndeclare global {\n interface Window {\n dataLayer?: (TagManagerEvent | VoidFunction)[];\n ga?: unknown;\n }\n}\n\n/**\n * track sends events to Google Tag Manager.\n */\nexport function track(\n event: string | TagManagerEvent,\n category?: string,\n action?: string,\n label?: string\n): void {\n window.dataLayer ??= [];\n if (typeof event === 'string') {\n window.dataLayer.push({\n event,\n event_category: category,\n event_action: action,\n event_label: label,\n });\n } else {\n window.dataLayer.push(event);\n }\n}\n\n/**\n * func adds functions to run sequentionally after\n * Google Tag Manager is ready.\n */\nexport function func(fn: () => void): void {\n window.dataLayer ??= [];\n window.dataLayer.push(fn);\n}\n", "/*!\n * @license\n * Copyright 2019-2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { track } from '../analytics/analytics';\n\n/**\n * Options are keyhandler callback options.\n */\ninterface Options {\n /**\n * target is the element the key event should filter on. The\n * default target is the document.\n */\n target?: Element;\n\n /**\n * withMeta specifies if the event callback should fire when\n * the key is pressed with a meta key (ctrl, alt, etc). By\n * default meta keypresses are ignored.\n */\n withMeta?: boolean;\n}\n\n/**\n * KeyHandler is the config for a keyboard event callback.\n */\ninterface KeyHandler extends Options {\n description: string;\n callback: (e: KeyboardEvent) => void;\n}\n\n/**\n * KeyboardController controls event callbacks for sitewide\n * keyboard events. Multiple callbacks can be registered for\n * a single key and by default the controller ignores events\n * for text input targets.\n */\nclass KeyboardController {\n handlers: Record>;\n\n constructor() {\n this.handlers = {};\n document.addEventListener('keydown', e => this.handleKeyPress(e));\n }\n\n /**\n * on registers keyboard event callbacks.\n * @param key the key to register.\n * @param description name of the event.\n * @param callback event callback.\n * @param options set target and withMeta options to override the default behaviors.\n */\n on(key: string, description: string, callback: (e: KeyboardEvent) => void, options?: Options) {\n this.handlers[key] ??= new Set();\n this.handlers[key].add({ description, callback, ...options });\n return this;\n }\n\n private handleKeyPress(e: KeyboardEvent) {\n for (const handler of this.handlers[e.key.toLowerCase()] ?? new Set()) {\n if (handler.target && handler.target !== e.target) {\n return;\n }\n const t = e.target as HTMLElement | null;\n if (\n !handler.target &&\n (t?.tagName === 'INPUT' || t?.tagName === 'SELECT' || t?.tagName === 'TEXTAREA')\n ) {\n return;\n }\n if (t?.isContentEditable) {\n return;\n }\n if (\n (handler.withMeta && !(e.ctrlKey || e.metaKey)) ||\n (!handler.withMeta && (e.ctrlKey || e.metaKey))\n ) {\n return;\n }\n track('keypress', 'hotkeys', `${e.key} pressed`, handler.description);\n handler.callback(e);\n }\n }\n}\n\nexport const keyboard = new KeyboardController();\n", "/*!\n * @license\n * Copyright 2019-2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n// This file implements the behavior of the \"jump to symbol\" dialog for Go\n// package documentation, as well as the simple dialog that displays keyboard\n// shortcuts.\n\n// The DOM for the dialogs is at the bottom of static/frontend/unit/main/_modals.tmpl.\n// The CSS is in static/frontend/unit/main/_modals.css.\n\n// The dialog is activated by pressing the 'f' key. It presents a list\n// (#JumpDialog-list) of all Go symbols displayed in the documentation.\n// Entering text in the dialog's text box (#JumpDialog-filter) restricts the\n// list to symbols containing the text. Clicking on an symbol jumps to\n// its documentation.\n\n// This code is based on\n// https://go.googlesource.com/gddo/+/refs/heads/master/gddo-server/assets/site.js.\n// It was modified to remove the dependence on jquery and bootstrap.\n\nimport { keyboard } from '../keyboard/keyboard';\n\nexport function initModals(): void {\n const jumpDialog = document.querySelector('.JumpDialog');\n const jumpBody = jumpDialog?.querySelector('.JumpDialog-body');\n const jumpList = jumpDialog?.querySelector('.JumpDialog-list');\n const jumpFilter = jumpDialog?.querySelector('.JumpDialog-input');\n const doc = document.querySelector('.js-documentation');\n\n interface JumpListItem {\n link: HTMLAnchorElement;\n name: string;\n kind: string;\n lower: string;\n }\n\n let jumpListItems: JumpListItem[] | undefined; // All the symbols in the doc; computed only once.\n\n // collectJumpListItems returns a list of items, one for each symbol in the\n // documentation on the current page.\n //\n // It uses the data-kind attribute generated in the documentation HTML to find\n // the symbols and their id attributes.\n //\n // If there are no data-kind attributes, then we have older doc; fall back to\n // a less precise method.\n function collectJumpListItems() {\n const items = [];\n if (!doc) return;\n for (const el of doc.querySelectorAll('[data-kind]')) {\n items.push(newJumpListItem(el));\n }\n\n // Clicking on any of the links closes the dialog.\n for (const item of items) {\n item.link.addEventListener('click', function () {\n jumpDialog?.close();\n });\n }\n // Sort case-insensitively by symbol name.\n items.sort(function (a, b) {\n return a.lower.localeCompare(b.lower);\n });\n return items;\n }\n\n // newJumpListItem creates a new item for the DOM element el.\n // An item is an object with:\n // - name: the element's id (which is the symbol name)\n // - kind: the element's kind (function, variable, etc.),\n // - link: a link ('a' tag) to the element\n // - lower: the name in lower case, just for sorting\n function newJumpListItem(el: Element): JumpListItem {\n const a = document.createElement('a');\n const name = el.getAttribute('id');\n a.setAttribute('href', '#' + name);\n a.setAttribute('tabindex', '-1');\n a.setAttribute('data-gtmc', 'jump to link');\n const kind = el.getAttribute('data-kind');\n return {\n link: a,\n name: name ?? '',\n kind: kind ?? '',\n lower: name?.toLowerCase() ?? '', // for sorting\n };\n }\n\n let lastFilterValue: string; // The last contents of the filter text box.\n let activeJumpItem = -1; // The index of the currently active item in the list.\n\n // updateJumpList sets the elements of the dialog list to\n // everything whose name contains filter.\n function updateJumpList(filter: string) {\n lastFilterValue = filter;\n if (!jumpListItems) {\n jumpListItems = collectJumpListItems();\n }\n setActiveJumpItem(-1);\n\n // Remove all children from list.\n while (jumpList?.firstChild) {\n jumpList.firstChild.remove();\n }\n\n if (filter) {\n // A filter is set. We treat the filter as a substring that can appear in\n // an item name (case insensitive), and find the following matches - in\n // order of priority:\n //\n // 1. Exact matches (the filter matches the item's name exactly)\n // 2. Prefix matches (the item's name starts with filter)\n // 3. Infix matches (the filter is a substring of the item's name)\n const filterLowerCase = filter.toLowerCase();\n\n const exactMatches = [];\n const prefixMatches = [];\n const infixMatches = [];\n\n // makeLinkHtml creates the link name HTML for a list item. item is the DOM\n // item. item.name.substr(boldStart, boldEnd) will be bolded.\n const makeLinkHtml = (item: JumpListItem, boldStart: number, boldEnd: number) => {\n return (\n item.name.substring(0, boldStart) +\n '' +\n item.name.substring(boldStart, boldEnd) +\n '' +\n item.name.substring(boldEnd)\n );\n };\n\n for (const item of jumpListItems ?? []) {\n const nameLowerCase = item.name.toLowerCase();\n\n if (nameLowerCase === filterLowerCase) {\n item.link.innerHTML = makeLinkHtml(item, 0, item.name.length);\n exactMatches.push(item);\n } else if (nameLowerCase.startsWith(filterLowerCase)) {\n item.link.innerHTML = makeLinkHtml(item, 0, filter.length);\n prefixMatches.push(item);\n } else {\n const index = nameLowerCase.indexOf(filterLowerCase);\n if (index > -1) {\n item.link.innerHTML = makeLinkHtml(item, index, index + filter.length);\n infixMatches.push(item);\n }\n }\n }\n\n for (const item of exactMatches.concat(prefixMatches).concat(infixMatches)) {\n jumpList?.appendChild(item.link);\n }\n } else {\n if (!jumpListItems || jumpListItems.length === 0) {\n const msg = document.createElement('i');\n msg.innerHTML = 'There are no symbols on this page.';\n jumpList?.appendChild(msg);\n }\n // No filter set; display all items in their existing order.\n for (const item of jumpListItems ?? []) {\n item.link.innerHTML = item.name + ' ' + item.kind + '';\n jumpList?.appendChild(item.link);\n }\n }\n\n if (jumpBody) {\n jumpBody.scrollTop = 0;\n }\n if (jumpListItems?.length && jumpList && jumpList.children.length > 0) {\n setActiveJumpItem(0);\n }\n }\n\n // Set the active jump item to n.\n function setActiveJumpItem(n: number) {\n const cs = jumpList?.children as HTMLCollectionOf | null | undefined;\n if (!cs || !jumpBody) {\n return;\n }\n if (activeJumpItem >= 0) {\n cs[activeJumpItem].classList.remove('JumpDialog-active');\n }\n if (n >= cs.length) {\n n = cs.length - 1;\n }\n if (n >= 0) {\n cs[n].classList.add('JumpDialog-active');\n\n // Scroll so the active item is visible.\n // For some reason cs[n].scrollIntoView() doesn't behave as I'd expect:\n // it moves the entire dialog box in the viewport.\n\n // Get the top and bottom of the active item relative to jumpBody.\n const activeTop = cs[n].offsetTop - cs[0].offsetTop;\n const activeBottom = activeTop + cs[n].clientHeight;\n if (activeTop < jumpBody.scrollTop) {\n // Off the top; scroll up.\n jumpBody.scrollTop = activeTop;\n } else if (activeBottom > jumpBody.scrollTop + jumpBody.clientHeight) {\n // Off the bottom; scroll down.\n jumpBody.scrollTop = activeBottom - jumpBody.clientHeight;\n }\n }\n activeJumpItem = n;\n }\n\n // Increment the activeJumpItem by delta.\n function incActiveJumpItem(delta: number) {\n if (activeJumpItem < 0) {\n return;\n }\n let n = activeJumpItem + delta;\n if (n < 0) {\n n = 0;\n }\n setActiveJumpItem(n);\n }\n\n // Pressing a key in the filter updates the list (if the filter actually changed).\n jumpFilter?.addEventListener('keyup', function () {\n if (jumpFilter.value.toUpperCase() != lastFilterValue.toUpperCase()) {\n updateJumpList(jumpFilter.value);\n }\n });\n\n // Pressing enter in the filter selects the first element in the list.\n jumpFilter?.addEventListener('keydown', function (event) {\n const upArrow = 38;\n const downArrow = 40;\n const enterKey = 13;\n switch (event.which) {\n case upArrow:\n incActiveJumpItem(-1);\n event.preventDefault();\n break;\n case downArrow:\n incActiveJumpItem(1);\n event.preventDefault();\n break;\n case enterKey:\n if (activeJumpItem >= 0) {\n if (jumpList) {\n (jumpList.children[activeJumpItem] as HTMLElement).click();\n event.preventDefault();\n }\n }\n break;\n }\n });\n\n const shortcutsDialog = document.querySelector('.ShortcutsDialog');\n\n // - Pressing 'f' or 'F' opens the jump-to-symbol dialog.\n // - Pressing '?' opens up the shortcut dialog.\n // Ignore a keypress if a dialog is already open, or if it is pressed on a\n // component that wants to consume it.\n keyboard\n .on('f', 'open jump to modal', e => {\n if (jumpDialog?.open || shortcutsDialog?.open) {\n return;\n }\n e.preventDefault();\n if (jumpFilter) {\n jumpFilter.value = '';\n }\n jumpDialog?.showModal?.();\n jumpFilter?.focus();\n updateJumpList('');\n })\n .on('?', 'open shortcuts modal', () => {\n if (jumpDialog?.open || shortcutsDialog?.open) {\n return;\n }\n shortcutsDialog?.showModal?.();\n });\n\n const jumpOutlineInput = document.querySelector('.js-jumpToInput');\n if (jumpOutlineInput) {\n jumpOutlineInput.addEventListener('click', () => {\n if (jumpFilter) {\n jumpFilter.value = '';\n }\n updateJumpList('');\n if (jumpDialog?.open || shortcutsDialog?.open) {\n return;\n }\n jumpDialog?.showModal?.();\n jumpFilter?.focus();\n });\n }\n\n document.querySelector('.js-openShortcuts')?.addEventListener('click', () => {\n shortcutsDialog?.showModal?.();\n });\n}\n", "/**\n * @license\n * Copyright 2022 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { abs } from '../../shared/base-path/base-path';\n\n/**\n * Left Navigation.\n */\nexport const initJumpLinks = async function () {\n // pathname \u5728\u6302 -base-path=/gogodocs \u65F6\u662F \"/gogodocs/about\"\u2014\u2014\u6BD4\u8F83\u5217\u8868\u4E5F\u5F97\u5E26\u524D\u7F00\n const pagesWithJumpLinks = [abs('/about')];\n if (!pagesWithJumpLinks.includes(window.location.pathname)) {\n // stop the file from doing anything else if the page doesn't have jumplinks\n return;\n }\n\n // these might be generated or not so don't grab references to the elements until actually need them.\n const titles = 'h2, h3, h4';\n const nav = '.LeftNav a';\n // these are always in the dom so we can get them now and throw errors if they're not.\n const leftNav = document.querySelector('.LeftNav');\n const siteContent = document.querySelector('.go-Content');\n let isObserverDisabled = false;\n\n /**\n * El function\n * @example el('h1', {className: 'title'}, 'Welcome to the site');\n * @example el('ul', {className: 'list'}, el('li', {}, 'Item one'), el('li', {}, 'Item two'), el('li', {}, 'Item three'));\n * @example el('img', {src: '/url.svg'});\n */\n function el(\n type = '',\n props: { [key: string]: string } = {},\n ...children: (HTMLElement | HTMLElement[] | string | undefined)[]\n ) {\n // Error, no type declared.\n if (!type) {\n throw new Error('Provide `type` to create document element.');\n }\n\n // Create element with optional attribute props\n const docEl = Object.assign(document.createElement(type), props);\n\n // Children: array containing strings or elements\n children.forEach(child => {\n if (typeof child === 'string') {\n docEl.appendChild(document.createTextNode(child));\n } else if (Array.isArray(child)) {\n child.forEach(c => docEl.appendChild(c));\n } else if (child instanceof HTMLElement) {\n docEl.appendChild(child);\n }\n });\n\n return docEl;\n }\n /** Build Nav if data hydrate is present. */\n function buildNav() {\n return new Promise((resolve, reject) => {\n let navItems: { id: string; label: string; subnav?: { id: string; label: string }[] }[] = [];\n let elements: HTMLElement[] = [];\n\n if (!siteContent || !leftNav) {\n return reject('.SiteContent not found.');\n }\n if (leftNav instanceof HTMLElement && !leftNav?.dataset?.hydrate) {\n return resolve(true);\n }\n\n for (const title of siteContent.querySelectorAll(titles)) {\n if (title instanceof HTMLElement && !title?.dataset?.ignore) {\n switch (title.tagName) {\n case 'H2':\n navItems = [\n ...navItems,\n {\n id: title.id,\n label: title?.dataset?.title ? title.dataset.title : title.textContent ?? '',\n },\n ];\n break;\n\n case 'H3':\n case 'H4':\n if (!navItems[navItems.length - 1]?.subnav) {\n navItems[navItems.length - 1].subnav = [\n {\n id: title.id,\n label: title?.dataset?.title ? title.dataset.title : title.textContent ?? '',\n },\n ];\n } else if (navItems[navItems.length - 1].subnav) {\n navItems[navItems.length - 1].subnav?.push({\n id: title.id,\n label: title?.dataset?.title ? title.dataset.title : title.textContent ?? '',\n });\n }\n break;\n }\n }\n }\n\n for (const navItem of navItems) {\n const link = el('a', { href: '#' + navItem.id }, el('span', {}, navItem.label));\n elements = [...elements, link];\n if (navItem?.subnav) {\n let subLinks: HTMLElement[] = [];\n for (const subnavItem of navItem.subnav) {\n const subItem = el(\n 'li',\n {},\n el(\n 'a',\n { href: '#' + subnavItem.id },\n el('img', { src: abs('/static/frontend/about/dot.svg'), width: '5', height: '5' }),\n el('span', {}, subnavItem.label)\n )\n );\n subLinks = [...subLinks, subItem];\n }\n const list = el('ul', { className: 'LeftSubnav' }, subLinks);\n elements = [...elements, list];\n }\n }\n\n elements.forEach(element => leftNav.appendChild(element));\n\n return resolve(true);\n });\n }\n /**\n * Set the correct active element.\n */\n function setNav() {\n return new Promise(resolve => {\n if (!document.querySelectorAll(nav)) return resolve(true);\n for (const a of document.querySelectorAll(nav)) {\n if (a instanceof HTMLAnchorElement && a.href === location.href) {\n setElementActive(a);\n break;\n }\n }\n resolve(true);\n });\n }\n /** resetNav: removes all .active from nav elements */\n function resetNav() {\n return new Promise(resolve => {\n if (!document.querySelectorAll(nav)) return resolve(true);\n for (const a of document.querySelectorAll(nav)) {\n a.classList.remove('active');\n }\n resolve(true);\n });\n }\n /** setElementActive: controls resetting nav and highlighting the appropriate nav items */\n function setElementActive(element: HTMLAnchorElement) {\n if (element instanceof HTMLAnchorElement) {\n resetNav().then(() => {\n element.classList.add('active');\n const parent = element?.parentNode?.parentNode;\n if (parent instanceof HTMLElement && parent?.classList?.contains('LeftSubnav')) {\n parent.previousElementSibling?.classList.add('active');\n }\n });\n }\n }\n /** setLinkManually: disables observer and selects the clicked nav item. */\n function setLinkManually() {\n delayObserver();\n const link = document.querySelector('[href=\"' + location.hash + '\"]');\n if (link instanceof HTMLAnchorElement) {\n setElementActive(link);\n }\n }\n /** delayObserver: Quick on off switch for intersection observer. */\n function delayObserver() {\n isObserverDisabled = true;\n setTimeout(() => {\n isObserverDisabled = false;\n }, 200);\n }\n /** observeSections: kicks off observation of titles as well as manual clicks with hashchange */\n function observeSections() {\n window.addEventListener('hashchange', setLinkManually);\n\n if (siteContent?.querySelectorAll(titles)) {\n const callback: IntersectionObserverCallback = entries => {\n if (!isObserverDisabled && Array.isArray(entries) && entries.length > 0) {\n for (const entry of entries) {\n if (entry.isIntersecting && entry.target instanceof HTMLElement) {\n const { id } = entry.target;\n const link = document.querySelector('[href=\"#' + id + '\"]');\n if (link instanceof HTMLAnchorElement) {\n setElementActive(link);\n }\n break;\n }\n }\n }\n };\n // rootMargin is important when multiple sections are in the observable area **on page load**.\n // they will still be highlighted on scroll because of the root margin.\n const ob = new IntersectionObserver(callback, {\n threshold: 0,\n rootMargin: '0px 0px -50% 0px',\n });\n for (const title of siteContent.querySelectorAll(titles)) {\n if (title instanceof HTMLElement && !title?.dataset?.ignore) {\n ob.observe(title);\n }\n }\n }\n }\n\n try {\n await buildNav();\n await setNav();\n if (location.hash) {\n delayObserver();\n }\n observeSections();\n } catch (e) {\n if (e instanceof Error) {\n console.error(e.message);\n } else {\n console.error(e);\n }\n }\n};\n", "/**\n * @license\n * Copyright 2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { registerHeaderListeners, registerSearchFormListeners } from 'static/shared/header/header';\nimport { CarouselController } from 'static/shared/carousel/carousel';\nimport { ClipboardController } from 'static/shared/clipboard/clipboard';\nimport { ToolTipController } from 'static/shared/tooltip/tooltip';\nimport { SelectNavController } from 'static/shared/outline/select';\nimport { ModalController } from 'static/shared/modal/modal';\nimport { initModals } from 'static/shared/jump/jump';\n\nimport { keyboard } from 'static/shared/keyboard/keyboard';\nimport * as analytics from 'static/shared/analytics/analytics';\nimport { initJumpLinks } from './about/index';\n\nwindow.addEventListener('load', () => {\n for (const el of document.querySelectorAll('.js-clipboard')) {\n new ClipboardController(el);\n }\n\n for (const el of document.querySelectorAll('.js-modal')) {\n new ModalController(el);\n }\n\n for (const t of document.querySelectorAll('.js-tooltip')) {\n new ToolTipController(t);\n }\n\n for (const el of document.querySelectorAll('.js-selectNav')) {\n new SelectNavController(el);\n }\n\n for (const el of document.querySelectorAll('.js-carousel')) {\n new CarouselController(el);\n }\n\n for (const el of document.querySelectorAll('.js-toggleTheme')) {\n el.addEventListener('click', () => {\n toggleTheme();\n });\n }\n\n if (document.querySelector('.js-gtmID')?.dataset.gtmid && window.dataLayer) {\n analytics.func(function () {\n removeUTMSource();\n });\n } else {\n removeUTMSource();\n }\n\n registerHeaderListeners();\n registerSearchFormListeners();\n initModals();\n initJumpLinks();\n registerCookieNotice();\n});\n\n// Pressing '/' focuses the search box\nkeyboard.on('/', 'focus search', e => {\n const searchInput = Array.from(\n document.querySelectorAll('.js-searchFocus')\n ).pop();\n // Favoring the Firefox quick find feature over search input\n // focus. See: https://github.com/golang/go/issues/41093.\n if (searchInput && !window.navigator.userAgent.includes('Firefox')) {\n e.preventDefault();\n searchInput.focus();\n }\n});\n\n// Pressing 'y' changes the browser URL to the canonical URL\n// without triggering a reload.\nkeyboard.on('y', 'set canonical url', () => {\n let canonicalURLPath = document.querySelector('.js-canonicalURLPath')?.dataset[\n 'canonicalUrlPath'\n ];\n if (canonicalURLPath && canonicalURLPath !== '') {\n const fragment = window.location.hash;\n if (fragment) {\n canonicalURLPath += fragment;\n }\n window.history.replaceState(null, '', canonicalURLPath);\n }\n});\n\n/**\n * setupGoogleTagManager initializes Google Tag Manager.\n */\n(function setupGoogleTagManager() {\n analytics.track({\n 'gtm.start': new Date().getTime(),\n event: 'gtm.js',\n });\n})();\n\n/**\n * removeUTMSource removes the utm_source GET parameter if present.\n * This is done using JavaScript, so that the utm_source is still\n * captured by Google Analytics.\n */\nfunction removeUTMSource() {\n const urlParams = new URLSearchParams(window.location.search);\n const utmSource = urlParams.get('utm_source');\n if (utmSource !== 'gopls' && utmSource !== 'godoc' && utmSource !== 'pkggodev') {\n return;\n }\n\n /** Strip the utm_source query parameter and replace the URL. **/\n const newURL = new URL(window.location.href);\n urlParams.delete('utm_source');\n newURL.search = urlParams.toString();\n window.history.replaceState(null, '', newURL.toString());\n}\n\n/**\n * toggleTheme switches the preferred color scheme between auto, light, and dark.\n */\nfunction toggleTheme() {\n let nextTheme = 'dark';\n const theme = document.documentElement.getAttribute('data-theme');\n if (theme === 'dark') {\n nextTheme = 'light';\n } else if (theme === 'light') {\n nextTheme = 'auto';\n }\n let domain = '';\n if (location.hostname === 'go.dev' || location.hostname.endsWith('.go.dev')) {\n domain = 'domain=.go.dev;';\n }\n document.documentElement.setAttribute('data-theme', nextTheme);\n document.cookie = `prefers-color-scheme=${nextTheme};${domain}path=/;max-age=31536000;`;\n}\n\n/**\n * registerCookieNotice makes the cookie notice visible and adds listeners to dismiss it\n * if it has not yet been acknowledge by the user.\n */\nfunction registerCookieNotice() {\n const themeCookie = document.cookie.match(/cookie-consent=true/);\n if (!themeCookie) {\n const notice = document.querySelector('.js-cookieNotice');\n const button = notice?.querySelector('button');\n notice?.classList.add('Cookie-notice--visible');\n button?.addEventListener('click', () => {\n let domain = '';\n if (location.hostname === 'go.dev' || location.hostname.endsWith('.go.dev')) {\n // Apply the cookie to *.go.dev.\n domain = 'domain=.go.dev;';\n }\n document.cookie = `cookie-consent=true;${domain}path=/;max-age=31536000`;\n notice?.remove();\n });\n }\n}\n"], + "sourcesContent": ["/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nexport function registerHeaderListeners(): void {\n const header = document.querySelector('.js-header') as HTMLElement;\n\n // Desktop menu hover state\n const menuItemHovers = document.querySelectorAll('.js-desktop-menu-hover');\n menuItemHovers.forEach(menuItemHover => {\n // when user clicks on the dropdown menu item on desktop or mobile,\n // force the menu to stay open until the user clicks off of it.\n menuItemHover.addEventListener('mouseenter', e => {\n const target = e.target as HTMLElement;\n const forced = document.querySelector('.forced-open') as HTMLElement;\n if (forced && forced !== menuItemHover) {\n forced.blur();\n forced.classList.remove('forced-open');\n }\n // prevents menus that have been tabbed into from staying open\n // when you hover over another menu\n target.classList.remove('forced-closed');\n target.classList.add('forced-open');\n });\n\n const toggleForcedOpen = (e: Event) => {\n const target = e.target as HTMLElement;\n const isForced = target?.classList.contains('forced-open');\n const currentTarget = e.currentTarget as HTMLElement;\n if (isForced) {\n currentTarget.removeEventListener('blur', () =>\n currentTarget.classList.remove('forced-open')\n );\n currentTarget.classList.remove('forced-open');\n currentTarget.classList.add('forced-closed');\n currentTarget.blur();\n currentTarget?.parentNode?.addEventListener('mouseout', () => {\n currentTarget.classList.remove('forced-closed');\n });\n } else {\n currentTarget.classList.remove('forced-closed');\n currentTarget.classList.add('forced-open');\n currentTarget.focus();\n currentTarget.addEventListener('blur', () => currentTarget.classList.remove('forced-open'));\n currentTarget?.parentNode?.removeEventListener('mouseout', () => {\n currentTarget.classList.remove('forced-closed');\n });\n }\n currentTarget.focus();\n };\n menuItemHover.addEventListener('click', toggleForcedOpen);\n menuItemHover.addEventListener('focus', e => {\n const target = e.target as HTMLElement;\n target.classList.add('forced-closed');\n target.classList.remove('forced-open');\n });\n\n // ensure desktop submenus are closed when esc is pressed\n const closeSubmenuOnEsc = (e: Event) => {\n const event = e as KeyboardEvent;\n const target = e.target as HTMLElement;\n if (event.key === 'Escape') {\n const forcedOpenItem = document.querySelector('.forced-open') as HTMLElement;\n if (forcedOpenItem) {\n forcedOpenItem.classList.remove('forced-open');\n forcedOpenItem.blur();\n forcedOpenItem.classList.add('forced-closed');\n target?.focus();\n }\n }\n };\n document.addEventListener('keydown', closeSubmenuOnEsc);\n });\n\n // Mobile menu subnav menus\n const headerbuttons = document.querySelectorAll('.js-headerMenuButton');\n headerbuttons.forEach(button => {\n button.addEventListener('click', e => {\n e.preventDefault();\n const isActive = header?.classList.contains('is-active');\n if (isActive) {\n handleNavigationDrawerInactive(header);\n } else {\n handleNavigationDrawerActive(header);\n }\n button.setAttribute('aria-expanded', isActive ? 'true' : 'false');\n });\n });\n\n const scrim = document.querySelector('.js-scrim');\n scrim?.addEventListener('click', e => {\n e.preventDefault();\n\n // find any active submenus and close them\n const activeSubnavs = document.querySelectorAll('.go-NavigationDrawer-submenuItem.is-active');\n activeSubnavs.forEach(subnav => handleNavigationDrawerInactive(subnav as HTMLElement));\n\n handleNavigationDrawerInactive(header);\n\n headerbuttons.forEach(button => {\n button.setAttribute(\n 'aria-expanded',\n header?.classList.contains('is-active') ? 'true' : 'false'\n );\n });\n });\n\n const getNavigationDrawerMenuItems = (navigationDrawer: HTMLElement): HTMLElement[] => {\n if (!navigationDrawer) {\n return [];\n }\n\n const menuItems = Array.from(\n navigationDrawer.querySelectorAll(\n ':scope > .go-NavigationDrawer-nav > .go-NavigationDrawer-list > .go-NavigationDrawer-listItem > a, :scope > .go-NavigationDrawer-nav > .go-NavigationDrawer-list > .go-NavigationDrawer-listItem > .go-Header-socialIcons > a'\n ) || []\n );\n\n const anchorEl = navigationDrawer.querySelector('.go-NavigationDrawer-header > a');\n if (anchorEl) {\n menuItems.unshift(anchorEl);\n }\n return menuItems as HTMLElement[];\n };\n\n const getNavigationDrawerIsSubnav = (navigationDrawer: HTMLElement) => {\n if (!navigationDrawer) {\n return;\n }\n return navigationDrawer.classList.contains('go-NavigationDrawer-submenuItem');\n };\n\n const handleNavigationDrawerInactive = (navigationDrawer: HTMLElement) => {\n if (!navigationDrawer) {\n return;\n }\n const menuItems = getNavigationDrawerMenuItems(navigationDrawer);\n navigationDrawer.classList.remove('is-active');\n const parentMenuItem = navigationDrawer\n .closest('.go-NavigationDrawer-listItem')\n ?.querySelector(':scope > a') as HTMLElement;\n parentMenuItem?.focus();\n menuItems?.forEach(item => item?.setAttribute('tabindex', '-1'));\n if (menuItems && menuItems[0]) {\n menuItems[0].removeEventListener('keydown', handleMenuItemTabLeftFactory(navigationDrawer));\n menuItems[menuItems.length - 1].removeEventListener(\n 'keydown',\n handleMenuItemTabRightFactory(navigationDrawer)\n );\n }\n\n if (navigationDrawer === header) {\n headerbuttons && (headerbuttons[0] as HTMLElement)?.focus();\n }\n };\n\n const handleNavigationDrawerActive = (navigationDrawer: HTMLElement) => {\n const menuItems = getNavigationDrawerMenuItems(navigationDrawer);\n\n navigationDrawer.classList.add('is-active');\n menuItems.forEach(item => item.setAttribute('tabindex', '0'));\n menuItems[0].focus();\n\n menuItems[0].addEventListener('keydown', handleMenuItemTabLeftFactory(navigationDrawer));\n menuItems[menuItems.length - 1].addEventListener(\n 'keydown',\n handleMenuItemTabRightFactory(navigationDrawer)\n );\n };\n\n const handleMenuItemTabLeftFactory = (navigationDrawer: HTMLElement) => {\n return (e: KeyboardEvent) => {\n if (e.key === 'Tab' && e.shiftKey) {\n e.preventDefault();\n handleNavigationDrawerInactive(navigationDrawer);\n }\n };\n };\n\n const handleMenuItemTabRightFactory = (navigationDrawer: HTMLElement) => {\n return (e: KeyboardEvent) => {\n if (e.key === 'Tab' && !e.shiftKey) {\n e.preventDefault();\n handleNavigationDrawerInactive(navigationDrawer);\n }\n };\n };\n\n const prepMobileNavigationDrawer = (navigationDrawer: HTMLElement) => {\n const isSubnav = getNavigationDrawerIsSubnav(navigationDrawer);\n const menuItems = getNavigationDrawerMenuItems(navigationDrawer);\n navigationDrawer.addEventListener('keyup', e => {\n if (e.key === 'Escape') {\n handleNavigationDrawerInactive(navigationDrawer);\n }\n });\n\n menuItems.forEach(item => {\n const parentLi = item.closest('li');\n if (parentLi && parentLi.classList.contains('js-mobile-subnav-trigger')) {\n const submenu = parentLi.querySelector('.go-NavigationDrawer-submenuItem') as HTMLElement;\n item.addEventListener('click', () => {\n handleNavigationDrawerActive(submenu);\n });\n }\n });\n if (isSubnav) {\n handleNavigationDrawerInactive(navigationDrawer);\n navigationDrawer\n ?.querySelector('.go-NavigationDrawer-header')\n ?.addEventListener('click', e => {\n e.preventDefault();\n handleNavigationDrawerInactive(navigationDrawer);\n });\n }\n };\n\n document\n .querySelectorAll('.go-NavigationDrawer')\n .forEach(drawer => prepMobileNavigationDrawer(drawer as HTMLElement));\n\n handleNavigationDrawerInactive(header);\n}\n\nexport function registerSearchFormListeners(): void {\n const searchForm = document.querySelector('.js-searchForm');\n const expandSearch = document.querySelector('.js-expandSearch');\n const input = searchForm?.querySelector('input');\n const headerLogo = document.querySelector('.js-headerLogo');\n const menuButton = document.querySelector('.js-headerMenuButton');\n expandSearch?.addEventListener('click', () => {\n searchForm?.classList.add('go-SearchForm--expanded');\n headerLogo?.classList.add('go-Header-logo--hidden');\n menuButton?.classList.add('go-Header-navOpen--hidden');\n input?.focus();\n });\n document?.addEventListener('click', e => {\n if (!searchForm?.contains(e.target as Node)) {\n searchForm?.classList.remove('go-SearchForm--expanded');\n headerLogo?.classList.remove('go-Header-logo--hidden');\n menuButton?.classList.remove('go-Header-navOpen--hidden');\n }\n });\n}\n", "/**\n * @license\n * Copyright 2024 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * \u7AD9\u70B9 URL \u5B50\u8DEF\u5F84\u524D\u7F00\uFF0C\u4ECE \u8BFB\u51FA\u3002\n *\n * fork \u7528 -base-path=/pkgsitex \u542F\u52A8\u65F6\uFF0Cserver \u7AEF\u6A21\u677F\u628A attribute \u5199\u6210\n * \"/pkgsitex\"\uFF1B\u4E0A\u6E38\u516C\u7F51 pkg.go.dev \u9ED8\u8BA4\u6302\u6839\uFF0Cattribute \u4E3A\u7A7A\u5B57\u7B26\u4E32\u3002\n *\n * \u5728 \u4E4B\u5916\u7684\u811A\u672C\u521D\u59CB\u5316\u9636\u6BB5\u8C03\u7528\u90FD\u5B89\u5168\u2014\u2014documentElement \u4E00\u5B9A\u5B58\u5728\u3002\n */\nexport function getBasePath(): string {\n return document.documentElement.dataset.basePath ?? '';\n}\n\n/**\n * \u7ED9\u4EE5 / \u5F00\u5934\u7684\u7EDD\u5BF9\u8DEF\u5F84\u52A0 BasePath \u524D\u7F00\u3002\n *\n * \u7528\u4E8E fetch URL / innerHTML src / location \u6BD4\u8F83\u7B49\u4EFB\u4F55\u9700\u8981\u62FC\u7AD9\u70B9\u7EDD\u5BF9 path\n * \u7684\u5730\u65B9\u3002\u975E\u7EDD\u5BF9\u8DEF\u5F84\uFF08\u4E0D\u4EE5 / \u5F00\u5934\uFF09\u539F\u6837\u8FD4\u56DE\u2014\u2014caller \u81EA\u5DF1\u4FDD\u8BC1\u8BED\u4E49\u3002\n *\n * \u4F8B\uFF1A\n * abs('/play/share') // \u2192 '/pkgsitex/play/share' \u6216 '/play/share'\n * abs('/static/foo.svg') // \u2192 '/pkgsitex/static/foo.svg' \u6216 '/static/foo.svg'\n * abs('relative/x') // \u2192 'relative/x'\uFF08\u4E0D\u52A8\uFF09\n */\nexport function abs(p: string): string {\n if (!p.startsWith('/')) return p;\n return getBasePath() + p;\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { getBasePath } from '../base-path/base-path';\n\n/**\n * Carousel Controller adds event listeners, accessibility enhancements, and\n * control elements to a carousel component.\n */\nexport class CarouselController {\n /**\n * slides is a collection of slides in the carousel.\n */\n private slides: HTMLLIElement[];\n /**\n * dots is a collection of dot navigation controls, added to the carousel\n * by this controller.\n */\n private dots: HTMLElement[];\n /**\n * liveRegion is a visually hidden element that notifies assitive devices\n * of visual changes to the carousel. They are added to the carousel by\n * this controller.\n */\n private liveRegion: HTMLElement;\n /**\n * activeIndex is the 0-index of the currently active slide.\n */\n private activeIndex: number;\n\n constructor(private el: HTMLElement) {\n this.slides = Array.from(el.querySelectorAll('.go-Carousel-slide'));\n this.dots = [];\n this.liveRegion = document.createElement('div');\n this.activeIndex = Number(el.getAttribute('data-slide-index') ?? 0);\n\n this.initSlides();\n this.initArrows();\n this.initDots();\n this.initLiveRegion();\n }\n\n private initSlides() {\n for (const [i, v] of this.slides.entries()) {\n if (i === this.activeIndex) continue;\n v.setAttribute('aria-hidden', 'true');\n }\n }\n\n private initArrows() {\n const arrows = document.createElement('ul');\n arrows.classList.add('go-Carousel-arrows');\n // base path \u901A\u8FC7 [getBasePath]() \u6CE8\u5165\uFF1B\u6302\u6839\u65F6\u8FD4\u7A7A\u5B57\u7B26\u4E32\uFF0C\u5BF9\u4E0A\u6E38\u96F6\u5DEE\u5F02\u3002\n const bp = getBasePath();\n arrows.innerHTML = `\n
  • \n \n
  • \n
  • \n \n
  • \n `;\n arrows\n .querySelector('.go-Carousel-prevSlide')\n ?.addEventListener('click', () => this.setActive(this.activeIndex - 1));\n arrows\n .querySelector('.go-Carousel-nextSlide')\n ?.addEventListener('click', () => this.setActive(this.activeIndex + 1));\n this.el.append(arrows);\n }\n\n private initDots() {\n const dots = document.createElement('ul');\n dots.classList.add('go-Carousel-dots');\n for (let i = 0; i < this.slides.length; i++) {\n const li = document.createElement('li');\n const button = document.createElement('button');\n button.classList.add('go-Carousel-dot');\n if (i === this.activeIndex) {\n button.classList.add('go-Carousel-dot--active');\n }\n button.innerHTML = `Slide ${i + 1}`;\n button.addEventListener('click', () => this.setActive(i));\n li.append(button);\n dots.append(li);\n this.dots.push(button);\n }\n this.el.append(dots);\n }\n\n private initLiveRegion() {\n this.liveRegion.setAttribute('aria-live', 'polite');\n this.liveRegion.setAttribute('aria-atomic', 'true');\n this.liveRegion.setAttribute('class', 'go-Carousel-obscured');\n this.liveRegion.textContent = `Slide ${this.activeIndex + 1} of ${this.slides.length}`;\n this.el.appendChild(this.liveRegion);\n }\n\n private setActive = (index: number) => {\n this.activeIndex = (index + this.slides.length) % this.slides.length;\n this.el.setAttribute('data-slide-index', String(this.activeIndex));\n for (const d of this.dots) {\n d.classList.remove('go-Carousel-dot--active');\n }\n this.dots[this.activeIndex].classList.add('go-Carousel-dot--active');\n for (const s of this.slides) {\n s.setAttribute('aria-hidden', 'true');\n }\n this.slides[this.activeIndex].removeAttribute('aria-hidden');\n this.liveRegion.textContent = 'Slide ' + (this.activeIndex + 1) + ' of ' + this.slides.length;\n };\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * This class decorates an element to copy arbitrary data attached via a data-\n * attribute to the clipboard.\n */\nexport class ClipboardController {\n /**\n * The data to be copied to the clipboard.\n */\n private data: string;\n\n /**\n * @param el The element that will trigger copying text to the clipboard. The text is\n * expected to be within its data-to-copy attribute.\n */\n constructor(private el: HTMLButtonElement) {\n this.data = el.dataset['toCopy'] ?? el.innerText;\n // if data-to-copy is empty and the button is part of an input group\n // capture the value of the input.\n if (!this.data && el.parentElement?.classList.contains('go-InputGroup')) {\n this.data = (this.data || el.parentElement?.querySelector('input')?.value) ?? '';\n }\n el.addEventListener('click', e => this.handleCopyClick(e));\n }\n\n /**\n * Handles when the primary element is clicked.\n */\n handleCopyClick(e: MouseEvent): void {\n e.preventDefault();\n const TOOLTIP_SHOW_DURATION_MS = 1000;\n\n // This API is not available on iOS.\n if (!navigator.clipboard) {\n this.showTooltipText('Unable to copy', TOOLTIP_SHOW_DURATION_MS);\n return;\n }\n navigator.clipboard\n .writeText(this.data)\n .then(() => {\n this.showTooltipText('Copied!', TOOLTIP_SHOW_DURATION_MS);\n })\n .catch(() => {\n this.showTooltipText('Unable to copy', TOOLTIP_SHOW_DURATION_MS);\n });\n }\n\n /**\n * Shows the given text in a tooltip for a specified amount of time, in milliseconds.\n */\n showTooltipText(text: string, durationMs: number): void {\n this.el.setAttribute('data-tooltip', text);\n setTimeout(() => this.el.setAttribute('data-tooltip', ''), durationMs);\n }\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * ToolTipController handles closing tooltips on external clicks.\n */\nexport class ToolTipController {\n constructor(private el: HTMLDetailsElement) {\n document.addEventListener('click', e => {\n const insideTooltip = this.el.contains(e.target as Element);\n if (!insideTooltip) {\n this.el.removeAttribute('open');\n }\n });\n\n // Add event listener for \"Escape\" keydown to close tooltip\n this.el.addEventListener('keydown', e => {\n if (e.key === 'Escape') {\n this.el.open = false;\n }\n });\n }\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { TreeNavController } from './tree.js';\n\nexport class SelectNavController {\n constructor(private el: Element) {\n this.el.addEventListener('change', e => {\n const target = e.target as HTMLSelectElement;\n let href = target.value;\n if (!target.value.startsWith('/')) {\n href = '/' + href;\n }\n window.location.href = href;\n });\n }\n}\n\nexport function makeSelectNav(tree: TreeNavController): HTMLLabelElement {\n const label = document.createElement('label');\n label.classList.add('go-Label');\n label.setAttribute('aria-label', 'Menu');\n const select = document.createElement('select');\n select.classList.add('go-Select', 'js-selectNav');\n label.appendChild(select);\n const outline = document.createElement('optgroup');\n outline.label = 'Outline';\n select.appendChild(outline);\n const groupMap: Record = {};\n let group: HTMLOptGroupElement;\n for (const t of tree.treeitems) {\n if (Number(t.depth) > 4) continue;\n if (t.groupTreeitem) {\n group = groupMap[t.groupTreeitem.label];\n if (!group) {\n group = groupMap[t.groupTreeitem.label] = document.createElement('optgroup');\n group.label = t.groupTreeitem.label;\n select.appendChild(group);\n }\n } else {\n group = outline;\n }\n const o = document.createElement('option');\n o.label = t.label;\n o.textContent = t.label;\n o.value = (t.el as HTMLAnchorElement).href.replace(window.location.origin, '').replace('/', '');\n group.appendChild(o);\n }\n tree.addObserver(t => {\n const hash = (t.el as HTMLAnchorElement).hash;\n const value = select.querySelector(`[value$=\"${hash}\"]`)?.value;\n if (value) {\n select.value = value;\n }\n }, 50);\n return label;\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\ninterface Window {\n dialogPolyfill?: {\n registerDialog: (el: HTMLDialogElement) => void;\n };\n}\n\ndeclare const window: Window;\n\n/**\n * ModalController registers a dialog element with the polyfill if\n * necessary for the current browser, add adds event listeners to\n * close and open modals.\n */\nexport class ModalController {\n constructor(private el: HTMLDialogElement) {\n if (window.dialogPolyfill) {\n window.dialogPolyfill.registerDialog(el);\n }\n this.init();\n }\n\n init() {\n const button = document.querySelector(`[aria-controls=\"${this.el.id}\"]`);\n if (button) {\n button.addEventListener('click', () => {\n if (this.el.showModal) {\n this.el.showModal();\n } else {\n this.el.setAttribute('opened', 'true');\n }\n this.el.querySelector('input')?.focus();\n });\n }\n for (const btn of this.el.querySelectorAll('[data-modal-close]')) {\n btn.addEventListener('click', () => {\n if (this.el.close) {\n this.el.close();\n } else {\n this.el.removeAttribute('opened');\n }\n });\n }\n }\n}\n", "interface TagManagerEvent {\n /**\n * event is the name of the event, used to filter events in\n * Google Analytics.\n */\n event: string;\n\n /**\n * event_category is a name that you supply as a way to group objects\n * that to analyze. Typically, you will use the same category name\n * multiple times over related UI elements (buttons, links, etc).\n */\n event_category?: string;\n\n /**\n * event_action is used to name the type of event or interaction you\n * want to measure for a particular web object. For example, with a\n * single \"form\" category, you can analyze a number of specific events\n * with this parameter, such as: form entered, form submitted.\n */\n event_action?: string;\n\n /**\n * event_label provide additional information for events that you want\n * to analyze, such as the text label of a link.\n */\n event_label?: string;\n\n /**\n * gtm.start is used to initialize Google Tag Manager.\n */\n 'gtm.start'?: number;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\ndeclare global {\n interface Window {\n dataLayer?: (TagManagerEvent | VoidFunction)[];\n ga?: unknown;\n }\n}\n\n/**\n * track sends events to Google Tag Manager.\n */\nexport function track(\n event: string | TagManagerEvent,\n category?: string,\n action?: string,\n label?: string\n): void {\n window.dataLayer ??= [];\n if (typeof event === 'string') {\n window.dataLayer.push({\n event,\n event_category: category,\n event_action: action,\n event_label: label,\n });\n } else {\n window.dataLayer.push(event);\n }\n}\n\n/**\n * func adds functions to run sequentionally after\n * Google Tag Manager is ready.\n */\nexport function func(fn: () => void): void {\n window.dataLayer ??= [];\n window.dataLayer.push(fn);\n}\n", "/*!\n * @license\n * Copyright 2019-2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { track } from '../analytics/analytics';\n\n/**\n * Options are keyhandler callback options.\n */\ninterface Options {\n /**\n * target is the element the key event should filter on. The\n * default target is the document.\n */\n target?: Element;\n\n /**\n * withMeta specifies if the event callback should fire when\n * the key is pressed with a meta key (ctrl, alt, etc). By\n * default meta keypresses are ignored.\n */\n withMeta?: boolean;\n}\n\n/**\n * KeyHandler is the config for a keyboard event callback.\n */\ninterface KeyHandler extends Options {\n description: string;\n callback: (e: KeyboardEvent) => void;\n}\n\n/**\n * KeyboardController controls event callbacks for sitewide\n * keyboard events. Multiple callbacks can be registered for\n * a single key and by default the controller ignores events\n * for text input targets.\n */\nclass KeyboardController {\n handlers: Record>;\n\n constructor() {\n this.handlers = {};\n document.addEventListener('keydown', e => this.handleKeyPress(e));\n }\n\n /**\n * on registers keyboard event callbacks.\n * @param key the key to register.\n * @param description name of the event.\n * @param callback event callback.\n * @param options set target and withMeta options to override the default behaviors.\n */\n on(key: string, description: string, callback: (e: KeyboardEvent) => void, options?: Options) {\n this.handlers[key] ??= new Set();\n this.handlers[key].add({ description, callback, ...options });\n return this;\n }\n\n private handleKeyPress(e: KeyboardEvent) {\n for (const handler of this.handlers[e.key.toLowerCase()] ?? new Set()) {\n if (handler.target && handler.target !== e.target) {\n return;\n }\n const t = e.target as HTMLElement | null;\n if (\n !handler.target &&\n (t?.tagName === 'INPUT' || t?.tagName === 'SELECT' || t?.tagName === 'TEXTAREA')\n ) {\n return;\n }\n if (t?.isContentEditable) {\n return;\n }\n if (\n (handler.withMeta && !(e.ctrlKey || e.metaKey)) ||\n (!handler.withMeta && (e.ctrlKey || e.metaKey))\n ) {\n return;\n }\n track('keypress', 'hotkeys', `${e.key} pressed`, handler.description);\n handler.callback(e);\n }\n }\n}\n\nexport const keyboard = new KeyboardController();\n", "/*!\n * @license\n * Copyright 2019-2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n// This file implements the behavior of the \"jump to symbol\" dialog for Go\n// package documentation, as well as the simple dialog that displays keyboard\n// shortcuts.\n\n// The DOM for the dialogs is at the bottom of static/frontend/unit/main/_modals.tmpl.\n// The CSS is in static/frontend/unit/main/_modals.css.\n\n// The dialog is activated by pressing the 'f' key. It presents a list\n// (#JumpDialog-list) of all Go symbols displayed in the documentation.\n// Entering text in the dialog's text box (#JumpDialog-filter) restricts the\n// list to symbols containing the text. Clicking on an symbol jumps to\n// its documentation.\n\n// This code is based on\n// https://go.googlesource.com/gddo/+/refs/heads/master/gddo-server/assets/site.js.\n// It was modified to remove the dependence on jquery and bootstrap.\n\nimport { keyboard } from '../keyboard/keyboard';\n\nexport function initModals(): void {\n const jumpDialog = document.querySelector('.JumpDialog');\n const jumpBody = jumpDialog?.querySelector('.JumpDialog-body');\n const jumpList = jumpDialog?.querySelector('.JumpDialog-list');\n const jumpFilter = jumpDialog?.querySelector('.JumpDialog-input');\n const doc = document.querySelector('.js-documentation');\n\n interface JumpListItem {\n link: HTMLAnchorElement;\n name: string;\n kind: string;\n lower: string;\n }\n\n let jumpListItems: JumpListItem[] | undefined; // All the symbols in the doc; computed only once.\n\n // collectJumpListItems returns a list of items, one for each symbol in the\n // documentation on the current page.\n //\n // It uses the data-kind attribute generated in the documentation HTML to find\n // the symbols and their id attributes.\n //\n // If there are no data-kind attributes, then we have older doc; fall back to\n // a less precise method.\n function collectJumpListItems() {\n const items = [];\n if (!doc) return;\n for (const el of doc.querySelectorAll('[data-kind]')) {\n items.push(newJumpListItem(el));\n }\n\n // Clicking on any of the links closes the dialog.\n for (const item of items) {\n item.link.addEventListener('click', function () {\n jumpDialog?.close();\n });\n }\n // Sort case-insensitively by symbol name.\n items.sort(function (a, b) {\n return a.lower.localeCompare(b.lower);\n });\n return items;\n }\n\n // newJumpListItem creates a new item for the DOM element el.\n // An item is an object with:\n // - name: the element's id (which is the symbol name)\n // - kind: the element's kind (function, variable, etc.),\n // - link: a link ('a' tag) to the element\n // - lower: the name in lower case, just for sorting\n function newJumpListItem(el: Element): JumpListItem {\n const a = document.createElement('a');\n const name = el.getAttribute('id');\n a.setAttribute('href', '#' + name);\n a.setAttribute('tabindex', '-1');\n a.setAttribute('data-gtmc', 'jump to link');\n const kind = el.getAttribute('data-kind');\n return {\n link: a,\n name: name ?? '',\n kind: kind ?? '',\n lower: name?.toLowerCase() ?? '', // for sorting\n };\n }\n\n let lastFilterValue: string; // The last contents of the filter text box.\n let activeJumpItem = -1; // The index of the currently active item in the list.\n\n // updateJumpList sets the elements of the dialog list to\n // everything whose name contains filter.\n function updateJumpList(filter: string) {\n lastFilterValue = filter;\n if (!jumpListItems) {\n jumpListItems = collectJumpListItems();\n }\n setActiveJumpItem(-1);\n\n // Remove all children from list.\n while (jumpList?.firstChild) {\n jumpList.firstChild.remove();\n }\n\n if (filter) {\n // A filter is set. We treat the filter as a substring that can appear in\n // an item name (case insensitive), and find the following matches - in\n // order of priority:\n //\n // 1. Exact matches (the filter matches the item's name exactly)\n // 2. Prefix matches (the item's name starts with filter)\n // 3. Infix matches (the filter is a substring of the item's name)\n const filterLowerCase = filter.toLowerCase();\n\n const exactMatches = [];\n const prefixMatches = [];\n const infixMatches = [];\n\n // makeLinkHtml creates the link name HTML for a list item. item is the DOM\n // item. item.name.substr(boldStart, boldEnd) will be bolded.\n const makeLinkHtml = (item: JumpListItem, boldStart: number, boldEnd: number) => {\n return (\n item.name.substring(0, boldStart) +\n '' +\n item.name.substring(boldStart, boldEnd) +\n '' +\n item.name.substring(boldEnd)\n );\n };\n\n for (const item of jumpListItems ?? []) {\n const nameLowerCase = item.name.toLowerCase();\n\n if (nameLowerCase === filterLowerCase) {\n item.link.innerHTML = makeLinkHtml(item, 0, item.name.length);\n exactMatches.push(item);\n } else if (nameLowerCase.startsWith(filterLowerCase)) {\n item.link.innerHTML = makeLinkHtml(item, 0, filter.length);\n prefixMatches.push(item);\n } else {\n const index = nameLowerCase.indexOf(filterLowerCase);\n if (index > -1) {\n item.link.innerHTML = makeLinkHtml(item, index, index + filter.length);\n infixMatches.push(item);\n }\n }\n }\n\n for (const item of exactMatches.concat(prefixMatches).concat(infixMatches)) {\n jumpList?.appendChild(item.link);\n }\n } else {\n if (!jumpListItems || jumpListItems.length === 0) {\n const msg = document.createElement('i');\n msg.innerHTML = 'There are no symbols on this page.';\n jumpList?.appendChild(msg);\n }\n // No filter set; display all items in their existing order.\n for (const item of jumpListItems ?? []) {\n item.link.innerHTML = item.name + ' ' + item.kind + '';\n jumpList?.appendChild(item.link);\n }\n }\n\n if (jumpBody) {\n jumpBody.scrollTop = 0;\n }\n if (jumpListItems?.length && jumpList && jumpList.children.length > 0) {\n setActiveJumpItem(0);\n }\n }\n\n // Set the active jump item to n.\n function setActiveJumpItem(n: number) {\n const cs = jumpList?.children as HTMLCollectionOf | null | undefined;\n if (!cs || !jumpBody) {\n return;\n }\n if (activeJumpItem >= 0) {\n cs[activeJumpItem].classList.remove('JumpDialog-active');\n }\n if (n >= cs.length) {\n n = cs.length - 1;\n }\n if (n >= 0) {\n cs[n].classList.add('JumpDialog-active');\n\n // Scroll so the active item is visible.\n // For some reason cs[n].scrollIntoView() doesn't behave as I'd expect:\n // it moves the entire dialog box in the viewport.\n\n // Get the top and bottom of the active item relative to jumpBody.\n const activeTop = cs[n].offsetTop - cs[0].offsetTop;\n const activeBottom = activeTop + cs[n].clientHeight;\n if (activeTop < jumpBody.scrollTop) {\n // Off the top; scroll up.\n jumpBody.scrollTop = activeTop;\n } else if (activeBottom > jumpBody.scrollTop + jumpBody.clientHeight) {\n // Off the bottom; scroll down.\n jumpBody.scrollTop = activeBottom - jumpBody.clientHeight;\n }\n }\n activeJumpItem = n;\n }\n\n // Increment the activeJumpItem by delta.\n function incActiveJumpItem(delta: number) {\n if (activeJumpItem < 0) {\n return;\n }\n let n = activeJumpItem + delta;\n if (n < 0) {\n n = 0;\n }\n setActiveJumpItem(n);\n }\n\n // Pressing a key in the filter updates the list (if the filter actually changed).\n jumpFilter?.addEventListener('keyup', function () {\n if (jumpFilter.value.toUpperCase() != lastFilterValue.toUpperCase()) {\n updateJumpList(jumpFilter.value);\n }\n });\n\n // Pressing enter in the filter selects the first element in the list.\n jumpFilter?.addEventListener('keydown', function (event) {\n const upArrow = 38;\n const downArrow = 40;\n const enterKey = 13;\n switch (event.which) {\n case upArrow:\n incActiveJumpItem(-1);\n event.preventDefault();\n break;\n case downArrow:\n incActiveJumpItem(1);\n event.preventDefault();\n break;\n case enterKey:\n if (activeJumpItem >= 0) {\n if (jumpList) {\n (jumpList.children[activeJumpItem] as HTMLElement).click();\n event.preventDefault();\n }\n }\n break;\n }\n });\n\n const shortcutsDialog = document.querySelector('.ShortcutsDialog');\n\n // - Pressing 'f' or 'F' opens the jump-to-symbol dialog.\n // - Pressing '?' opens up the shortcut dialog.\n // Ignore a keypress if a dialog is already open, or if it is pressed on a\n // component that wants to consume it.\n keyboard\n .on('f', 'open jump to modal', e => {\n if (jumpDialog?.open || shortcutsDialog?.open) {\n return;\n }\n e.preventDefault();\n if (jumpFilter) {\n jumpFilter.value = '';\n }\n jumpDialog?.showModal?.();\n jumpFilter?.focus();\n updateJumpList('');\n })\n .on('?', 'open shortcuts modal', () => {\n if (jumpDialog?.open || shortcutsDialog?.open) {\n return;\n }\n shortcutsDialog?.showModal?.();\n });\n\n const jumpOutlineInput = document.querySelector('.js-jumpToInput');\n if (jumpOutlineInput) {\n jumpOutlineInput.addEventListener('click', () => {\n if (jumpFilter) {\n jumpFilter.value = '';\n }\n updateJumpList('');\n if (jumpDialog?.open || shortcutsDialog?.open) {\n return;\n }\n jumpDialog?.showModal?.();\n jumpFilter?.focus();\n });\n }\n\n document.querySelector('.js-openShortcuts')?.addEventListener('click', () => {\n shortcutsDialog?.showModal?.();\n });\n}\n", "/**\n * @license\n * Copyright 2022 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { abs } from '../../shared/base-path/base-path';\n\n/**\n * Left Navigation.\n */\nexport const initJumpLinks = async function () {\n // pathname \u5728\u6302 -base-path=/pkgsitex \u65F6\u662F \"/pkgsitex/about\"\u2014\u2014\u6BD4\u8F83\u5217\u8868\u4E5F\u5F97\u5E26\u524D\u7F00\n const pagesWithJumpLinks = [abs('/about')];\n if (!pagesWithJumpLinks.includes(window.location.pathname)) {\n // stop the file from doing anything else if the page doesn't have jumplinks\n return;\n }\n\n // these might be generated or not so don't grab references to the elements until actually need them.\n const titles = 'h2, h3, h4';\n const nav = '.LeftNav a';\n // these are always in the dom so we can get them now and throw errors if they're not.\n const leftNav = document.querySelector('.LeftNav');\n const siteContent = document.querySelector('.go-Content');\n let isObserverDisabled = false;\n\n /**\n * El function\n * @example el('h1', {className: 'title'}, 'Welcome to the site');\n * @example el('ul', {className: 'list'}, el('li', {}, 'Item one'), el('li', {}, 'Item two'), el('li', {}, 'Item three'));\n * @example el('img', {src: '/url.svg'});\n */\n function el(\n type = '',\n props: { [key: string]: string } = {},\n ...children: (HTMLElement | HTMLElement[] | string | undefined)[]\n ) {\n // Error, no type declared.\n if (!type) {\n throw new Error('Provide `type` to create document element.');\n }\n\n // Create element with optional attribute props\n const docEl = Object.assign(document.createElement(type), props);\n\n // Children: array containing strings or elements\n children.forEach(child => {\n if (typeof child === 'string') {\n docEl.appendChild(document.createTextNode(child));\n } else if (Array.isArray(child)) {\n child.forEach(c => docEl.appendChild(c));\n } else if (child instanceof HTMLElement) {\n docEl.appendChild(child);\n }\n });\n\n return docEl;\n }\n /** Build Nav if data hydrate is present. */\n function buildNav() {\n return new Promise((resolve, reject) => {\n let navItems: { id: string; label: string; subnav?: { id: string; label: string }[] }[] = [];\n let elements: HTMLElement[] = [];\n\n if (!siteContent || !leftNav) {\n return reject('.SiteContent not found.');\n }\n if (leftNav instanceof HTMLElement && !leftNav?.dataset?.hydrate) {\n return resolve(true);\n }\n\n for (const title of siteContent.querySelectorAll(titles)) {\n if (title instanceof HTMLElement && !title?.dataset?.ignore) {\n switch (title.tagName) {\n case 'H2':\n navItems = [\n ...navItems,\n {\n id: title.id,\n label: title?.dataset?.title ? title.dataset.title : title.textContent ?? '',\n },\n ];\n break;\n\n case 'H3':\n case 'H4':\n if (!navItems[navItems.length - 1]?.subnav) {\n navItems[navItems.length - 1].subnav = [\n {\n id: title.id,\n label: title?.dataset?.title ? title.dataset.title : title.textContent ?? '',\n },\n ];\n } else if (navItems[navItems.length - 1].subnav) {\n navItems[navItems.length - 1].subnav?.push({\n id: title.id,\n label: title?.dataset?.title ? title.dataset.title : title.textContent ?? '',\n });\n }\n break;\n }\n }\n }\n\n for (const navItem of navItems) {\n const link = el('a', { href: '#' + navItem.id }, el('span', {}, navItem.label));\n elements = [...elements, link];\n if (navItem?.subnav) {\n let subLinks: HTMLElement[] = [];\n for (const subnavItem of navItem.subnav) {\n const subItem = el(\n 'li',\n {},\n el(\n 'a',\n { href: '#' + subnavItem.id },\n el('img', { src: abs('/static/frontend/about/dot.svg'), width: '5', height: '5' }),\n el('span', {}, subnavItem.label)\n )\n );\n subLinks = [...subLinks, subItem];\n }\n const list = el('ul', { className: 'LeftSubnav' }, subLinks);\n elements = [...elements, list];\n }\n }\n\n elements.forEach(element => leftNav.appendChild(element));\n\n return resolve(true);\n });\n }\n /**\n * Set the correct active element.\n */\n function setNav() {\n return new Promise(resolve => {\n if (!document.querySelectorAll(nav)) return resolve(true);\n for (const a of document.querySelectorAll(nav)) {\n if (a instanceof HTMLAnchorElement && a.href === location.href) {\n setElementActive(a);\n break;\n }\n }\n resolve(true);\n });\n }\n /** resetNav: removes all .active from nav elements */\n function resetNav() {\n return new Promise(resolve => {\n if (!document.querySelectorAll(nav)) return resolve(true);\n for (const a of document.querySelectorAll(nav)) {\n a.classList.remove('active');\n }\n resolve(true);\n });\n }\n /** setElementActive: controls resetting nav and highlighting the appropriate nav items */\n function setElementActive(element: HTMLAnchorElement) {\n if (element instanceof HTMLAnchorElement) {\n resetNav().then(() => {\n element.classList.add('active');\n const parent = element?.parentNode?.parentNode;\n if (parent instanceof HTMLElement && parent?.classList?.contains('LeftSubnav')) {\n parent.previousElementSibling?.classList.add('active');\n }\n });\n }\n }\n /** setLinkManually: disables observer and selects the clicked nav item. */\n function setLinkManually() {\n delayObserver();\n const link = document.querySelector('[href=\"' + location.hash + '\"]');\n if (link instanceof HTMLAnchorElement) {\n setElementActive(link);\n }\n }\n /** delayObserver: Quick on off switch for intersection observer. */\n function delayObserver() {\n isObserverDisabled = true;\n setTimeout(() => {\n isObserverDisabled = false;\n }, 200);\n }\n /** observeSections: kicks off observation of titles as well as manual clicks with hashchange */\n function observeSections() {\n window.addEventListener('hashchange', setLinkManually);\n\n if (siteContent?.querySelectorAll(titles)) {\n const callback: IntersectionObserverCallback = entries => {\n if (!isObserverDisabled && Array.isArray(entries) && entries.length > 0) {\n for (const entry of entries) {\n if (entry.isIntersecting && entry.target instanceof HTMLElement) {\n const { id } = entry.target;\n const link = document.querySelector('[href=\"#' + id + '\"]');\n if (link instanceof HTMLAnchorElement) {\n setElementActive(link);\n }\n break;\n }\n }\n }\n };\n // rootMargin is important when multiple sections are in the observable area **on page load**.\n // they will still be highlighted on scroll because of the root margin.\n const ob = new IntersectionObserver(callback, {\n threshold: 0,\n rootMargin: '0px 0px -50% 0px',\n });\n for (const title of siteContent.querySelectorAll(titles)) {\n if (title instanceof HTMLElement && !title?.dataset?.ignore) {\n ob.observe(title);\n }\n }\n }\n }\n\n try {\n await buildNav();\n await setNav();\n if (location.hash) {\n delayObserver();\n }\n observeSections();\n } catch (e) {\n if (e instanceof Error) {\n console.error(e.message);\n } else {\n console.error(e);\n }\n }\n};\n", "/**\n * @license\n * Copyright 2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { registerHeaderListeners, registerSearchFormListeners } from 'static/shared/header/header';\nimport { CarouselController } from 'static/shared/carousel/carousel';\nimport { ClipboardController } from 'static/shared/clipboard/clipboard';\nimport { ToolTipController } from 'static/shared/tooltip/tooltip';\nimport { SelectNavController } from 'static/shared/outline/select';\nimport { ModalController } from 'static/shared/modal/modal';\nimport { initModals } from 'static/shared/jump/jump';\n\nimport { keyboard } from 'static/shared/keyboard/keyboard';\nimport * as analytics from 'static/shared/analytics/analytics';\nimport { initJumpLinks } from './about/index';\n\nwindow.addEventListener('load', () => {\n for (const el of document.querySelectorAll('.js-clipboard')) {\n new ClipboardController(el);\n }\n\n for (const el of document.querySelectorAll('.js-modal')) {\n new ModalController(el);\n }\n\n for (const t of document.querySelectorAll('.js-tooltip')) {\n new ToolTipController(t);\n }\n\n for (const el of document.querySelectorAll('.js-selectNav')) {\n new SelectNavController(el);\n }\n\n for (const el of document.querySelectorAll('.js-carousel')) {\n new CarouselController(el);\n }\n\n for (const el of document.querySelectorAll('.js-toggleTheme')) {\n el.addEventListener('click', () => {\n toggleTheme();\n });\n }\n\n if (document.querySelector('.js-gtmID')?.dataset.gtmid && window.dataLayer) {\n analytics.func(function () {\n removeUTMSource();\n });\n } else {\n removeUTMSource();\n }\n\n registerHeaderListeners();\n registerSearchFormListeners();\n initModals();\n initJumpLinks();\n registerCookieNotice();\n});\n\n// Pressing '/' focuses the search box\nkeyboard.on('/', 'focus search', e => {\n const searchInput = Array.from(\n document.querySelectorAll('.js-searchFocus')\n ).pop();\n // Favoring the Firefox quick find feature over search input\n // focus. See: https://github.com/golang/go/issues/41093.\n if (searchInput && !window.navigator.userAgent.includes('Firefox')) {\n e.preventDefault();\n searchInput.focus();\n }\n});\n\n// Pressing 'y' changes the browser URL to the canonical URL\n// without triggering a reload.\nkeyboard.on('y', 'set canonical url', () => {\n let canonicalURLPath = document.querySelector('.js-canonicalURLPath')?.dataset[\n 'canonicalUrlPath'\n ];\n if (canonicalURLPath && canonicalURLPath !== '') {\n const fragment = window.location.hash;\n if (fragment) {\n canonicalURLPath += fragment;\n }\n window.history.replaceState(null, '', canonicalURLPath);\n }\n});\n\n/**\n * setupGoogleTagManager initializes Google Tag Manager.\n */\n(function setupGoogleTagManager() {\n analytics.track({\n 'gtm.start': new Date().getTime(),\n event: 'gtm.js',\n });\n})();\n\n/**\n * removeUTMSource removes the utm_source GET parameter if present.\n * This is done using JavaScript, so that the utm_source is still\n * captured by Google Analytics.\n */\nfunction removeUTMSource() {\n const urlParams = new URLSearchParams(window.location.search);\n const utmSource = urlParams.get('utm_source');\n if (utmSource !== 'gopls' && utmSource !== 'godoc' && utmSource !== 'pkggodev') {\n return;\n }\n\n /** Strip the utm_source query parameter and replace the URL. **/\n const newURL = new URL(window.location.href);\n urlParams.delete('utm_source');\n newURL.search = urlParams.toString();\n window.history.replaceState(null, '', newURL.toString());\n}\n\n/**\n * toggleTheme switches the preferred color scheme between auto, light, and dark.\n */\nfunction toggleTheme() {\n let nextTheme = 'dark';\n const theme = document.documentElement.getAttribute('data-theme');\n if (theme === 'dark') {\n nextTheme = 'light';\n } else if (theme === 'light') {\n nextTheme = 'auto';\n }\n let domain = '';\n if (location.hostname === 'go.dev' || location.hostname.endsWith('.go.dev')) {\n domain = 'domain=.go.dev;';\n }\n document.documentElement.setAttribute('data-theme', nextTheme);\n document.cookie = `prefers-color-scheme=${nextTheme};${domain}path=/;max-age=31536000;`;\n}\n\n/**\n * registerCookieNotice makes the cookie notice visible and adds listeners to dismiss it\n * if it has not yet been acknowledge by the user.\n */\nfunction registerCookieNotice() {\n const themeCookie = document.cookie.match(/cookie-consent=true/);\n if (!themeCookie) {\n const notice = document.querySelector('.js-cookieNotice');\n const button = notice?.querySelector('button');\n notice?.classList.add('Cookie-notice--visible');\n button?.addEventListener('click', () => {\n let domain = '';\n if (location.hostname === 'go.dev' || location.hostname.endsWith('.go.dev')) {\n // Apply the cookie to *.go.dev.\n domain = 'domain=.go.dev;';\n }\n document.cookie = `cookie-consent=true;${domain}path=/;max-age=31536000`;\n notice?.remove();\n });\n }\n}\n"], "mappings": "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", "names": ["registerHeaderListeners", "header", "menuItemHover", "e", "target", "forced", "toggleForcedOpen", "_a", "_b", "isForced", "currentTarget", "closeSubmenuOnEsc", "event", "forcedOpenItem", "headerbuttons", "button", "isActive", "handleNavigationDrawerInactive", "handleNavigationDrawerActive", "scrim", "subnav", "getNavigationDrawerMenuItems", "navigationDrawer", "menuItems", "anchorEl", "getNavigationDrawerIsSubnav", "parentMenuItem", "item", "handleMenuItemTabLeftFactory", "handleMenuItemTabRightFactory", "prepMobileNavigationDrawer", "isSubnav", "parentLi", "submenu", "drawer", "registerSearchFormListeners", "searchForm", "expandSearch", "input", "headerLogo", "menuButton", "getBasePath", "_a", "abs", "p", "CarouselController", "el", "index", "d", "s", "_a", "i", "v", "_b", "arrows", "bp", "getBasePath", "dots", "li", "button", "ClipboardController", "el", "_a", "_b", "_c", "_d", "_e", "e", "TOOLTIP_SHOW_DURATION_MS", "text", "durationMs", "ToolTipController", "el", "e", "SelectNavController", "el", "e", "target", "href", "ModalController", "el", "button", "_a", "btn", "track", "event", "category", "action", "label", "_a", "func", "fn", "KeyboardController", "key", "description", "callback", "options", "_a", "_b", "handler", "t", "track", "keyboard", "initModals", "_a", "jumpDialog", "jumpBody", "jumpList", "jumpFilter", "doc", "jumpListItems", "collectJumpListItems", "items", "el", "newJumpListItem", "item", "a", "b", "name", "kind", "lastFilterValue", "activeJumpItem", "updateJumpList", "filter", "setActiveJumpItem", "filterLowerCase", "exactMatches", "prefixMatches", "infixMatches", "makeLinkHtml", "boldStart", "boldEnd", "nameLowerCase", "index", "msg", "n", "cs", "activeTop", "activeBottom", "incActiveJumpItem", "delta", "event", "shortcutsDialog", "keyboard", "e", "jumpOutlineInput", "initJumpLinks", "abs", "titles", "nav", "leftNav", "siteContent", "isObserverDisabled", "el", "type", "props", "children", "docEl", "child", "c", "buildNav", "resolve", "reject", "_a", "_b", "_c", "_d", "_e", "_f", "_g", "_h", "_i", "_j", "navItems", "elements", "title", "navItem", "link", "subLinks", "subnavItem", "subItem", "list", "element", "setNav", "a", "setElementActive", "resetNav", "parent", "setLinkManually", "delayObserver", "observeSections", "callback", "entries", "entry", "id", "ob", "e", "_a", "el", "ClipboardController", "ModalController", "t", "ToolTipController", "SelectNavController", "CarouselController", "toggleTheme", "func", "removeUTMSource", "registerHeaderListeners", "registerSearchFormListeners", "initModals", "initJumpLinks", "registerCookieNotice", "keyboard", "e", "searchInput", "canonicalURLPath", "fragment", "track", "urlParams", "utmSource", "newURL", "nextTheme", "theme", "domain", "notice", "button"] } diff --git a/static/frontend/unit/main/main.js b/static/frontend/unit/main/main.js index c1a1cbba7..3ad82aff2 100644 --- a/static/frontend/unit/main/main.js +++ b/static/frontend/unit/main/main.js @@ -3,7 +3,7 @@ function H(){var l;return(l=document.documentElement.dataset.basePath)!=null?l:" module play.ground require ${t.modulepath} ${t.version} -`)),e}handleShareButtonClick(){let e="https://play.golang.org/p/";this.setOutputText("Waiting for remote server\u2026"),fetch(p("/play/share"),{method:"POST",body:this.getCodeWithModFile()}).then(t=>t.text()).then(t=>{let i=e+t;this.setOutputHTML(`
    ${i}`),window.open(i)}).catch(t=>{this.setErrorText(t)})}handleFormatButtonClick(){var t,i;this.setOutputText("Waiting for remote server\u2026");let e=new FormData;e.append("body",(i=(t=this.inputEl)==null?void 0:t.value)!=null?i:""),fetch(p("/play/fmt"),{method:"POST",body:e}).then(s=>s.json()).then(({Body:s,Error:n})=>{this.setOutputText(n||"Done."),s&&(this.setInputText(s),this.resize())}).catch(s=>{this.setErrorText(s)})}handleRunButtonClick(){this.setOutputText("Waiting for remote server\u2026"),fetch(p("/play/compile"),{method:"POST",body:JSON.stringify({body:this.getCodeWithModFile(),version:2})}).then(e=>e.json()).then(async({Events:e,Errors:t})=>{this.setOutputText(t||"");for(let i of e||[])this.appendToOutputText(i.Message),await new Promise(s=>setTimeout(s,i.Delay/1e6))}).catch(e=>{this.setErrorText(e)})}};function C(){let l=location.hash.match(/^#(example-.*)$/);if(l){let i=document.getElementById(l[1]);i&&(i.open=!0)}let e=[...document.querySelectorAll(u.PLAY_HREF)],t=i=>e.find(s=>s.hash===i.getAnchorHash());for(let i of document.querySelectorAll(u.PLAY_CONTAINER)){let s=new x(i),n=t(s);n?n.addEventListener("click",()=>{s.expand()}):console.warn("example href not found")}}var f=class{constructor(e){this.el=e;this.el.addEventListener("change",t=>{let i=t.target,s=i.value;i.value.startsWith("/")||(s="/"+s),window.location.href=s})}};function I(l){let e=document.createElement("label");e.classList.add("go-Label"),e.setAttribute("aria-label","Menu");let t=document.createElement("select");t.classList.add("go-Select","js-selectNav"),e.appendChild(t);let i=document.createElement("optgroup");i.label="Outline",t.appendChild(i);let s={},n;for(let r of l.treeitems){if(Number(r.depth)>4)continue;r.groupTreeitem?(n=s[r.groupTreeitem.label],n||(n=s[r.groupTreeitem.label]=document.createElement("optgroup"),n.label=r.groupTreeitem.label,t.appendChild(n))):n=i;let a=document.createElement("option");a.label=r.label,a.textContent=r.label,a.value=r.el.href.replace(window.location.origin,"").replace("/",""),n.appendChild(a)}return l.addObserver(r=>{var h;let a=r.el.hash,d=(h=t.querySelector(`[value$="${a}"]`))==null?void 0:h.value;d&&(t.value=d)},50),e}var E=class{constructor(e){this.el=e;this.handleResize=()=>{this.el.style.setProperty("--js-tree-height","100vh"),this.el.style.setProperty("--js-tree-height",this.el.clientHeight+"px")};this.treeitems=[],this.firstChars=[],this.firstTreeitem=null,this.lastTreeitem=null,this.observerCallbacks=[],this.init()}init(){this.handleResize(),window.addEventListener("resize",this.handleResize),this.findTreeItems(),this.updateVisibleTreeitems(),this.observeTargets(),this.firstTreeitem&&(this.firstTreeitem.el.tabIndex=0)}observeTargets(){this.addObserver(i=>{this.expandTreeitem(i),this.setSelected(i)});let e=new Map,t=new IntersectionObserver(i=>{for(let s of i)e.set(s.target.id,s.isIntersecting||s.intersectionRatio===1);for(let[s,n]of e)if(n){let r=this.treeitems.find(a=>{var d;return(d=a.el)==null?void 0:d.href.endsWith(`#${s}`)});if(r)for(let a of this.observerCallbacks)a(r);break}},{threshold:1,rootMargin:"-60px 0px 0px 0px"});for(let i of this.treeitems.map(s=>s.el.getAttribute("href")))if(i){let s=i.replace(window.location.origin,"").replace("/","").replace("#",""),n=document.getElementById(s);n&&!s.startsWith("example-")&&t.observe(n)}}addObserver(e,t=200){this.observerCallbacks.push(O(e,t))}setFocusToNextItem(e){let t=null;for(let i=e.index+1;i-1;i--){let s=this.treeitems[i];if(s.isVisible){t=s;break}}t&&this.setFocusToItem(t)}setFocusToParentItem(e){e.groupTreeitem&&this.setFocusToItem(e.groupTreeitem)}setFocusToFirstItem(){this.firstTreeitem&&this.setFocusToItem(this.firstTreeitem)}setFocusToLastItem(){this.lastTreeitem&&this.setFocusToItem(this.lastTreeitem)}setSelected(e){var t;for(let i of this.el.querySelectorAll('[aria-expanded="true"]'))i!==e.el&&((t=i.nextElementSibling)!=null&&t.contains(e.el)||i.setAttribute("aria-expanded","false"));for(let i of this.el.querySelectorAll("[aria-selected]"))i!==e.el&&i.setAttribute("aria-selected","false");e.el.setAttribute("aria-selected","true"),this.updateVisibleTreeitems(),this.setFocusToItem(e,!1)}expandTreeitem(e){let t=e;for(;t;)t.isExpandable&&t.el.setAttribute("aria-expanded","true"),t=t.groupTreeitem;this.updateVisibleTreeitems()}expandAllSiblingItems(e){for(let t of this.treeitems)t.groupTreeitem===e.groupTreeitem&&t.isExpandable&&this.expandTreeitem(t)}collapseTreeitem(e){let t=null;e.isExpanded()?t=e:t=e.groupTreeitem,t&&(t.el.setAttribute("aria-expanded","false"),this.updateVisibleTreeitems(),this.setFocusToItem(t))}setFocusByFirstCharacter(e,t){let i,s;t=t.toLowerCase(),i=e.index+1,i===this.treeitems.length&&(i=0),s=this.getIndexFirstChars(i,t),s===-1&&(s=this.getIndexFirstChars(0,t)),s>-1&&this.setFocusToItem(this.treeitems[s])}findTreeItems(){let e=(t,i)=>{let s=i,n=t.firstElementChild;for(;n;)(n.tagName==="A"||n.tagName==="SPAN")&&(s=new g(n,this,i),this.treeitems.push(s),this.firstChars.push(s.label.substring(0,1).toLowerCase())),n.firstElementChild&&e(n,s),n=n.nextElementSibling};e(this.el,null),this.treeitems.map((t,i)=>t.index=i)}updateVisibleTreeitems(){this.firstTreeitem=this.treeitems[0];for(let e of this.treeitems){let t=e.groupTreeitem;for(e.isVisible=!0;t&&t.el!==this.el;)t.isExpanded()||(e.isVisible=!1),t=t.groupTreeitem;e.isVisible&&(this.lastTreeitem=e)}}setFocusToItem(e,t=!0){e.el.tabIndex=0,t&&e.el.focus();for(let i of this.treeitems)i!==e&&(i.el.tabIndex=-1)}getIndexFirstChars(e,t){for(let i=e;i{let s=()=>{t=null,l(...i)};t&&clearTimeout(t),t=setTimeout(s,e)}}var T=class{constructor(e,t){this.table=e;this.toggleAll=t;this.expandAllItems=()=>{this.toggles.map(e=>e.setAttribute("aria-expanded","true")),this.update()};this.collapseAllItems=()=>{this.toggles.map(e=>e.setAttribute("aria-expanded","false")),this.update()};this.update=()=>{this.updateVisibleItems(),setTimeout(()=>this.updateGlobalToggle())};this.rows=Array.from(e.querySelectorAll("[data-aria-controls]")),this.toggles=Array.from(this.table.querySelectorAll("[aria-expanded]")),this.setAttributes(),this.attachEventListeners(),this.update()}setAttributes(){for(let e of["data-aria-controls","data-aria-labelledby","data-id"])this.table.querySelectorAll(`[${e}]`).forEach(t=>{var i;t.setAttribute(e.replace("data-",""),(i=t.getAttribute(e))!=null?i:""),t.removeAttribute(e)})}attachEventListeners(){var e;this.rows.forEach(t=>{t.addEventListener("click",i=>{this.handleToggleClick(i)})}),(e=this.toggleAll)==null||e.addEventListener("click",()=>{this.expandAllItems()}),document.addEventListener("keydown",t=>{(t.ctrlKey||t.metaKey)&&t.key==="f"&&this.expandAllItems()})}handleToggleClick(e){let t=e.currentTarget;t!=null&&t.hasAttribute("aria-expanded")||(t=this.table.querySelector(`button[aria-controls="${t==null?void 0:t.getAttribute("aria-controls")}"]`));let i=(t==null?void 0:t.getAttribute("aria-expanded"))==="true";t==null||t.setAttribute("aria-expanded",i?"false":"true"),e.stopPropagation(),this.update()}updateVisibleItems(){this.rows.map(e=>{var s;let t=(e==null?void 0:e.getAttribute("aria-expanded"))==="true",i=(s=e==null?void 0:e.getAttribute("aria-controls"))==null?void 0:s.trimEnd().split(" ");i==null||i.map(n=>{let r=document.getElementById(`${n}`);t?(r==null||r.classList.add("visible"),r==null||r.classList.remove("hidden")):(r==null||r.classList.add("hidden"),r==null||r.classList.remove("visible"))})})}updateGlobalToggle(){if(!this.toggleAll)return;this.rows.some(t=>t.hasAttribute("aria-expanded"))&&(this.toggleAll.style.display="block"),this.toggles.some(t=>t.getAttribute("aria-expanded")==="false")?(this.toggleAll.innerText="Expand all",this.toggleAll.onclick=this.expandAllItems,this.toggleAll.setAttribute("aria-label","Expand all directories"),this.toggleAll.setAttribute("aria-live","polite")):(this.toggleAll.innerText="Collapse all",this.toggleAll.onclick=this.collapseAllItems,this.toggleAll.setAttribute("aria-label","Collapse all directories"),this.toggleAll.setAttribute("aria-live","polite"))}};C();var m=document.querySelector(".js-expandableTable");if(m){let l=new T(m,document.querySelector(".js-expandAllDirectories"));window.location.search.includes("expand-directories")&&l.expandAllItems();let e=document.querySelector(".js-showInternalDirectories");e&&(document.querySelector(".UnitDirectories-internal")&&(e.style.display="block",e.setAttribute("aria-label","Show Internal Directories"),e.setAttribute("aria-describedby","showInternal-description")),e.addEventListener("click",()=>{m.classList.contains("UnitDirectories-showInternal")?(m.classList.remove("UnitDirectories-showInternal"),e.innerText="Show internal",e.setAttribute("aria-label","Show Internal Directories"),e.setAttribute("aria-live","polite"),e.setAttribute("aria-describedby","showInternal-description")):(m.classList.add("UnitDirectories-showInternal"),e.innerText="Hide internal",e.setAttribute("aria-label","Hide Internal Directories"),e.setAttribute("aria-live","polite"),e.setAttribute("aria-describedby","hideInternal-description"))})),document.querySelector('html[data-local="true"]')&&(e==null||e.click())}var S=document.querySelector(".js-tree");if(S){let l=new E(S),e=I(l),t=document.querySelector(".js-mainNavMobile");t&&t.firstElementChild&&(t==null||t.replaceChild(e,t.firstElementChild)),e.firstElementChild&&new f(e.firstElementChild)}var o=document.querySelector(".js-readme"),v=document.querySelector(".js-readmeContent"),w=document.querySelector(".js-readmeOutline"),b=document.querySelectorAll(".js-readmeExpand"),k=document.querySelector(".js-readmeCollapse"),y=document.querySelector(".DocNavMobile-select");o&&v&&w&&b.length&&k&&(o.clientHeight>320&&(o==null||o.classList.remove("UnitReadme--expanded"),o==null||o.classList.add("UnitReadme--toggle")),window.location.hash.includes("readme")&&c(),y==null||y.addEventListener("change",l=>{l.target.value.startsWith("readme-")&&c()}),b.forEach(l=>l.addEventListener("click",e=>{e.preventDefault(),c(),o.scrollIntoView()})),k.addEventListener("click",l=>{l.preventDefault(),o.classList.remove("UnitReadme--expanded"),b[1]&&b[1].scrollIntoView({block:"center"})}),v.addEventListener("keyup",()=>{c()}),v.addEventListener("click",()=>{c()}),w.addEventListener("click",()=>{c()}),document.addEventListener("keydown",l=>{(l.ctrlKey||l.metaKey)&&l.key==="f"&&c()}));function c(){history.replaceState(null,"",`${location.pathname}${location.search}#section-readme`),o==null||o.classList.add("UnitReadme--expanded")}function M(){var t;if(!location.hash)return;let l=document.getElementById(location.hash.slice(1)),e=(t=l==null?void 0:l.parentElement)==null?void 0:t.parentElement;(e==null?void 0:e.nodeName)==="DETAILS"&&(e.open=!0)}M();window.addEventListener("hashchange",()=>M());document.querySelectorAll(".js-buildContextSelect").forEach(l=>{l.addEventListener("change",e=>{window.location.search=`?GOOS=${e.target.value}`})});(()=>{if(!document.querySelector("h4[data-kind]"))return;let l=r=>{var d;if(r.startsWith("pkg-")||r.startsWith("section-")||r.startsWith("hdr-"))return!1;let a=(d=r.split(".").pop())!=null?d:r;return/^[a-z]/.test(a)};document.querySelectorAll("h4[data-kind][id]").forEach(r=>{if(!l(r.id))return;let a=r.closest(".Documentation-function, .Documentation-type, .Documentation-typeFunc, .Documentation-typeMethod");a==null||a.classList.add("Documentation-unexported")}),document.querySelectorAll('.Documentation-indexFunction a[href^="#"], .Documentation-indexType a[href^="#"], .Documentation-indexTypeFunctions a[href^="#"], .Documentation-indexTypeMethods a[href^="#"], .go-Tree a[href^="#"]').forEach(r=>{var a;l(r.getAttribute("href").slice(1))&&((a=r.closest("li"))==null||a.classList.add("Documentation-unexported"))});let e=document.createElement("style");e.textContent="body:not(.show-unexported) .Documentation-unexported{display:none}",document.head.appendChild(e);let t=document.querySelector("#pkg-index");if(!t)return;let i=document.createElement("button");i.type="button",i.className="go-Button go-Button--inline",i.style.marginLeft="0.75rem",i.style.fontSize="0.875rem",i.style.verticalAlign="middle";let s="gogodocs:showUnexported",n=r=>{document.body.classList.toggle("show-unexported",r),i.textContent=r?"Hide unexported":"Show unexported";try{localStorage.setItem(s,r?"1":"0")}catch{}};n(localStorage.getItem(s)==="1"),i.addEventListener("click",()=>n(!document.body.classList.contains("show-unexported"))),t.appendChild(i)})(); +`)),e}handleShareButtonClick(){let e="https://play.golang.org/p/";this.setOutputText("Waiting for remote server\u2026"),fetch(p("/play/share"),{method:"POST",body:this.getCodeWithModFile()}).then(t=>t.text()).then(t=>{let i=e+t;this.setOutputHTML(`${i}`),window.open(i)}).catch(t=>{this.setErrorText(t)})}handleFormatButtonClick(){var t,i;this.setOutputText("Waiting for remote server\u2026");let e=new FormData;e.append("body",(i=(t=this.inputEl)==null?void 0:t.value)!=null?i:""),fetch(p("/play/fmt"),{method:"POST",body:e}).then(s=>s.json()).then(({Body:s,Error:n})=>{this.setOutputText(n||"Done."),s&&(this.setInputText(s),this.resize())}).catch(s=>{this.setErrorText(s)})}handleRunButtonClick(){this.setOutputText("Waiting for remote server\u2026"),fetch(p("/play/compile"),{method:"POST",body:JSON.stringify({body:this.getCodeWithModFile(),version:2})}).then(e=>e.json()).then(async({Events:e,Errors:t})=>{this.setOutputText(t||"");for(let i of e||[])this.appendToOutputText(i.Message),await new Promise(s=>setTimeout(s,i.Delay/1e6))}).catch(e=>{this.setErrorText(e)})}};function C(){let l=location.hash.match(/^#(example-.*)$/);if(l){let i=document.getElementById(l[1]);i&&(i.open=!0)}let e=[...document.querySelectorAll(u.PLAY_HREF)],t=i=>e.find(s=>s.hash===i.getAnchorHash());for(let i of document.querySelectorAll(u.PLAY_CONTAINER)){let s=new x(i),n=t(s);n?n.addEventListener("click",()=>{s.expand()}):console.warn("example href not found")}}var f=class{constructor(e){this.el=e;this.el.addEventListener("change",t=>{let i=t.target,s=i.value;i.value.startsWith("/")||(s="/"+s),window.location.href=s})}};function I(l){let e=document.createElement("label");e.classList.add("go-Label"),e.setAttribute("aria-label","Menu");let t=document.createElement("select");t.classList.add("go-Select","js-selectNav"),e.appendChild(t);let i=document.createElement("optgroup");i.label="Outline",t.appendChild(i);let s={},n;for(let r of l.treeitems){if(Number(r.depth)>4)continue;r.groupTreeitem?(n=s[r.groupTreeitem.label],n||(n=s[r.groupTreeitem.label]=document.createElement("optgroup"),n.label=r.groupTreeitem.label,t.appendChild(n))):n=i;let a=document.createElement("option");a.label=r.label,a.textContent=r.label,a.value=r.el.href.replace(window.location.origin,"").replace("/",""),n.appendChild(a)}return l.addObserver(r=>{var h;let a=r.el.hash,d=(h=t.querySelector(`[value$="${a}"]`))==null?void 0:h.value;d&&(t.value=d)},50),e}var E=class{constructor(e){this.el=e;this.handleResize=()=>{this.el.style.setProperty("--js-tree-height","100vh"),this.el.style.setProperty("--js-tree-height",this.el.clientHeight+"px")};this.treeitems=[],this.firstChars=[],this.firstTreeitem=null,this.lastTreeitem=null,this.observerCallbacks=[],this.init()}init(){this.handleResize(),window.addEventListener("resize",this.handleResize),this.findTreeItems(),this.updateVisibleTreeitems(),this.observeTargets(),this.firstTreeitem&&(this.firstTreeitem.el.tabIndex=0)}observeTargets(){this.addObserver(i=>{this.expandTreeitem(i),this.setSelected(i)});let e=new Map,t=new IntersectionObserver(i=>{for(let s of i)e.set(s.target.id,s.isIntersecting||s.intersectionRatio===1);for(let[s,n]of e)if(n){let r=this.treeitems.find(a=>{var d;return(d=a.el)==null?void 0:d.href.endsWith(`#${s}`)});if(r)for(let a of this.observerCallbacks)a(r);break}},{threshold:1,rootMargin:"-60px 0px 0px 0px"});for(let i of this.treeitems.map(s=>s.el.getAttribute("href")))if(i){let s=i.replace(window.location.origin,"").replace("/","").replace("#",""),n=document.getElementById(s);n&&!s.startsWith("example-")&&t.observe(n)}}addObserver(e,t=200){this.observerCallbacks.push(O(e,t))}setFocusToNextItem(e){let t=null;for(let i=e.index+1;i-1;i--){let s=this.treeitems[i];if(s.isVisible){t=s;break}}t&&this.setFocusToItem(t)}setFocusToParentItem(e){e.groupTreeitem&&this.setFocusToItem(e.groupTreeitem)}setFocusToFirstItem(){this.firstTreeitem&&this.setFocusToItem(this.firstTreeitem)}setFocusToLastItem(){this.lastTreeitem&&this.setFocusToItem(this.lastTreeitem)}setSelected(e){var t;for(let i of this.el.querySelectorAll('[aria-expanded="true"]'))i!==e.el&&((t=i.nextElementSibling)!=null&&t.contains(e.el)||i.setAttribute("aria-expanded","false"));for(let i of this.el.querySelectorAll("[aria-selected]"))i!==e.el&&i.setAttribute("aria-selected","false");e.el.setAttribute("aria-selected","true"),this.updateVisibleTreeitems(),this.setFocusToItem(e,!1)}expandTreeitem(e){let t=e;for(;t;)t.isExpandable&&t.el.setAttribute("aria-expanded","true"),t=t.groupTreeitem;this.updateVisibleTreeitems()}expandAllSiblingItems(e){for(let t of this.treeitems)t.groupTreeitem===e.groupTreeitem&&t.isExpandable&&this.expandTreeitem(t)}collapseTreeitem(e){let t=null;e.isExpanded()?t=e:t=e.groupTreeitem,t&&(t.el.setAttribute("aria-expanded","false"),this.updateVisibleTreeitems(),this.setFocusToItem(t))}setFocusByFirstCharacter(e,t){let i,s;t=t.toLowerCase(),i=e.index+1,i===this.treeitems.length&&(i=0),s=this.getIndexFirstChars(i,t),s===-1&&(s=this.getIndexFirstChars(0,t)),s>-1&&this.setFocusToItem(this.treeitems[s])}findTreeItems(){let e=(t,i)=>{let s=i,n=t.firstElementChild;for(;n;)(n.tagName==="A"||n.tagName==="SPAN")&&(s=new g(n,this,i),this.treeitems.push(s),this.firstChars.push(s.label.substring(0,1).toLowerCase())),n.firstElementChild&&e(n,s),n=n.nextElementSibling};e(this.el,null),this.treeitems.map((t,i)=>t.index=i)}updateVisibleTreeitems(){this.firstTreeitem=this.treeitems[0];for(let e of this.treeitems){let t=e.groupTreeitem;for(e.isVisible=!0;t&&t.el!==this.el;)t.isExpanded()||(e.isVisible=!1),t=t.groupTreeitem;e.isVisible&&(this.lastTreeitem=e)}}setFocusToItem(e,t=!0){e.el.tabIndex=0,t&&e.el.focus();for(let i of this.treeitems)i!==e&&(i.el.tabIndex=-1)}getIndexFirstChars(e,t){for(let i=e;i{let s=()=>{t=null,l(...i)};t&&clearTimeout(t),t=setTimeout(s,e)}}var T=class{constructor(e,t){this.table=e;this.toggleAll=t;this.expandAllItems=()=>{this.toggles.map(e=>e.setAttribute("aria-expanded","true")),this.update()};this.collapseAllItems=()=>{this.toggles.map(e=>e.setAttribute("aria-expanded","false")),this.update()};this.update=()=>{this.updateVisibleItems(),setTimeout(()=>this.updateGlobalToggle())};this.rows=Array.from(e.querySelectorAll("[data-aria-controls]")),this.toggles=Array.from(this.table.querySelectorAll("[aria-expanded]")),this.setAttributes(),this.attachEventListeners(),this.update()}setAttributes(){for(let e of["data-aria-controls","data-aria-labelledby","data-id"])this.table.querySelectorAll(`[${e}]`).forEach(t=>{var i;t.setAttribute(e.replace("data-",""),(i=t.getAttribute(e))!=null?i:""),t.removeAttribute(e)})}attachEventListeners(){var e;this.rows.forEach(t=>{t.addEventListener("click",i=>{this.handleToggleClick(i)})}),(e=this.toggleAll)==null||e.addEventListener("click",()=>{this.expandAllItems()}),document.addEventListener("keydown",t=>{(t.ctrlKey||t.metaKey)&&t.key==="f"&&this.expandAllItems()})}handleToggleClick(e){let t=e.currentTarget;t!=null&&t.hasAttribute("aria-expanded")||(t=this.table.querySelector(`button[aria-controls="${t==null?void 0:t.getAttribute("aria-controls")}"]`));let i=(t==null?void 0:t.getAttribute("aria-expanded"))==="true";t==null||t.setAttribute("aria-expanded",i?"false":"true"),e.stopPropagation(),this.update()}updateVisibleItems(){this.rows.map(e=>{var s;let t=(e==null?void 0:e.getAttribute("aria-expanded"))==="true",i=(s=e==null?void 0:e.getAttribute("aria-controls"))==null?void 0:s.trimEnd().split(" ");i==null||i.map(n=>{let r=document.getElementById(`${n}`);t?(r==null||r.classList.add("visible"),r==null||r.classList.remove("hidden")):(r==null||r.classList.add("hidden"),r==null||r.classList.remove("visible"))})})}updateGlobalToggle(){if(!this.toggleAll)return;this.rows.some(t=>t.hasAttribute("aria-expanded"))&&(this.toggleAll.style.display="block"),this.toggles.some(t=>t.getAttribute("aria-expanded")==="false")?(this.toggleAll.innerText="Expand all",this.toggleAll.onclick=this.expandAllItems,this.toggleAll.setAttribute("aria-label","Expand all directories"),this.toggleAll.setAttribute("aria-live","polite")):(this.toggleAll.innerText="Collapse all",this.toggleAll.onclick=this.collapseAllItems,this.toggleAll.setAttribute("aria-label","Collapse all directories"),this.toggleAll.setAttribute("aria-live","polite"))}};C();var m=document.querySelector(".js-expandableTable");if(m){let l=new T(m,document.querySelector(".js-expandAllDirectories"));window.location.search.includes("expand-directories")&&l.expandAllItems();let e=document.querySelector(".js-showInternalDirectories");e&&(document.querySelector(".UnitDirectories-internal")&&(e.style.display="block",e.setAttribute("aria-label","Show Internal Directories"),e.setAttribute("aria-describedby","showInternal-description")),e.addEventListener("click",()=>{m.classList.contains("UnitDirectories-showInternal")?(m.classList.remove("UnitDirectories-showInternal"),e.innerText="Show internal",e.setAttribute("aria-label","Show Internal Directories"),e.setAttribute("aria-live","polite"),e.setAttribute("aria-describedby","showInternal-description")):(m.classList.add("UnitDirectories-showInternal"),e.innerText="Hide internal",e.setAttribute("aria-label","Hide Internal Directories"),e.setAttribute("aria-live","polite"),e.setAttribute("aria-describedby","hideInternal-description"))})),document.querySelector('html[data-local="true"]')&&(e==null||e.click())}var S=document.querySelector(".js-tree");if(S){let l=new E(S),e=I(l),t=document.querySelector(".js-mainNavMobile");t&&t.firstElementChild&&(t==null||t.replaceChild(e,t.firstElementChild)),e.firstElementChild&&new f(e.firstElementChild)}var o=document.querySelector(".js-readme"),v=document.querySelector(".js-readmeContent"),k=document.querySelector(".js-readmeOutline"),b=document.querySelectorAll(".js-readmeExpand"),w=document.querySelector(".js-readmeCollapse"),y=document.querySelector(".DocNavMobile-select");o&&v&&k&&b.length&&w&&(o.clientHeight>320&&(o==null||o.classList.remove("UnitReadme--expanded"),o==null||o.classList.add("UnitReadme--toggle")),window.location.hash.includes("readme")&&c(),y==null||y.addEventListener("change",l=>{l.target.value.startsWith("readme-")&&c()}),b.forEach(l=>l.addEventListener("click",e=>{e.preventDefault(),c(),o.scrollIntoView()})),w.addEventListener("click",l=>{l.preventDefault(),o.classList.remove("UnitReadme--expanded"),b[1]&&b[1].scrollIntoView({block:"center"})}),v.addEventListener("keyup",()=>{c()}),v.addEventListener("click",()=>{c()}),k.addEventListener("click",()=>{c()}),document.addEventListener("keydown",l=>{(l.ctrlKey||l.metaKey)&&l.key==="f"&&c()}));function c(){history.replaceState(null,"",`${location.pathname}${location.search}#section-readme`),o==null||o.classList.add("UnitReadme--expanded")}function M(){var t;if(!location.hash)return;let l=document.getElementById(location.hash.slice(1)),e=(t=l==null?void 0:l.parentElement)==null?void 0:t.parentElement;(e==null?void 0:e.nodeName)==="DETAILS"&&(e.open=!0)}M();window.addEventListener("hashchange",()=>M());document.querySelectorAll(".js-buildContextSelect").forEach(l=>{l.addEventListener("change",e=>{window.location.search=`?GOOS=${e.target.value}`})});(()=>{if(!document.querySelector("h4[data-kind]"))return;let l=r=>{var d;if(r.startsWith("pkg-")||r.startsWith("section-")||r.startsWith("hdr-"))return!1;let a=(d=r.split(".").pop())!=null?d:r;return/^[a-z]/.test(a)};document.querySelectorAll("h4[data-kind][id]").forEach(r=>{if(!l(r.id))return;let a=r.closest(".Documentation-function, .Documentation-type, .Documentation-typeFunc, .Documentation-typeMethod");a==null||a.classList.add("Documentation-unexported")}),document.querySelectorAll('.Documentation-indexFunction a[href^="#"], .Documentation-indexType a[href^="#"], .Documentation-indexTypeFunctions a[href^="#"], .Documentation-indexTypeMethods a[href^="#"], .go-Tree a[href^="#"]').forEach(r=>{var a;l(r.getAttribute("href").slice(1))&&((a=r.closest("li"))==null||a.classList.add("Documentation-unexported"))});let e=document.createElement("style");e.textContent="body:not(.show-unexported) .Documentation-unexported{display:none}",document.head.appendChild(e);let t=document.querySelector("#pkg-index");if(!t)return;let i=document.createElement("button");i.type="button",i.className="go-Button go-Button--inline",i.style.marginLeft="0.75rem",i.style.fontSize="0.875rem",i.style.verticalAlign="middle";let s="pkgsitex:showUnexported",n=r=>{document.body.classList.toggle("show-unexported",r),i.textContent=r?"Hide unexported":"Show unexported";try{localStorage.setItem(s,r?"1":"0")}catch{}};n(localStorage.getItem(s)==="1"),i.addEventListener("click",()=>n(!document.body.classList.contains("show-unexported"))),t.appendChild(i)})(); /** * @license * Copyright 2024 The Go Authors. All rights reserved. diff --git a/static/frontend/unit/main/main.js.map b/static/frontend/unit/main/main.js.map index fbe0b88dd..fa66e7f31 100644 --- a/static/frontend/unit/main/main.js.map +++ b/static/frontend/unit/main/main.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../../shared/base-path/base-path.ts", "../../../shared/playground/playground.ts", "../../../shared/outline/select.ts", "../../../shared/outline/tree.ts", "../../../shared/table/table.ts", "main.ts"], - "sourcesContent": ["/**\n * @license\n * Copyright 2024 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * \u7AD9\u70B9 URL \u5B50\u8DEF\u5F84\u524D\u7F00\uFF0C\u4ECE \u8BFB\u51FA\u3002\n *\n * fork \u7528 -base-path=/gogodocs \u542F\u52A8\u65F6\uFF0Cserver \u7AEF\u6A21\u677F\u628A attribute \u5199\u6210\n * \"/gogodocs\"\uFF1B\u4E0A\u6E38\u516C\u7F51 pkg.go.dev \u9ED8\u8BA4\u6302\u6839\uFF0Cattribute \u4E3A\u7A7A\u5B57\u7B26\u4E32\u3002\n *\n * \u5728 \u4E4B\u5916\u7684\u811A\u672C\u521D\u59CB\u5316\u9636\u6BB5\u8C03\u7528\u90FD\u5B89\u5168\u2014\u2014documentElement \u4E00\u5B9A\u5B58\u5728\u3002\n */\nexport function getBasePath(): string {\n return document.documentElement.dataset.basePath ?? '';\n}\n\n/**\n * \u7ED9\u4EE5 / \u5F00\u5934\u7684\u7EDD\u5BF9\u8DEF\u5F84\u52A0 BasePath \u524D\u7F00\u3002\n *\n * \u7528\u4E8E fetch URL / innerHTML src / location \u6BD4\u8F83\u7B49\u4EFB\u4F55\u9700\u8981\u62FC\u7AD9\u70B9\u7EDD\u5BF9 path\n * \u7684\u5730\u65B9\u3002\u975E\u7EDD\u5BF9\u8DEF\u5F84\uFF08\u4E0D\u4EE5 / \u5F00\u5934\uFF09\u539F\u6837\u8FD4\u56DE\u2014\u2014caller \u81EA\u5DF1\u4FDD\u8BC1\u8BED\u4E49\u3002\n *\n * \u4F8B\uFF1A\n * abs('/play/share') // \u2192 '/gogodocs/play/share' \u6216 '/play/share'\n * abs('/static/foo.svg') // \u2192 '/gogodocs/static/foo.svg' \u6216 '/static/foo.svg'\n * abs('relative/x') // \u2192 'relative/x'\uFF08\u4E0D\u52A8\uFF09\n */\nexport function abs(p: string): string {\n if (!p.startsWith('/')) return p;\n return getBasePath() + p;\n}\n", "/*!\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n// This file implements the playground implementation of the documentation\n// page. The playground involves a \"play\" button that allows you to open up\n// a new link to play.golang.org using the example code.\n\n// The CSS is in static/frontend/unit/main/_doc.css\n\nimport { abs } from '../base-path/base-path';\n\n/**\n * CSS classes used by PlaygroundExampleController\n */\nconst PlayExampleClassName = {\n PLAY_HREF: '.js-exampleHref',\n PLAY_CONTAINER: '.js-exampleContainer',\n EXAMPLE_INPUT: '.Documentation-exampleCode',\n EXAMPLE_OUTPUT: '.Documentation-exampleOutput',\n EXAMPLE_OUTPUT_CONTAINER: '.js-exampleOutputContainer',\n EXAMPLE_ERROR: '.Documentation-exampleError',\n PLAY_BUTTON: '.Documentation-examplePlayButton',\n SHARE_BUTTON: '.Documentation-exampleShareButton',\n FORMAT_BUTTON: '.Documentation-exampleFormatButton',\n RUN_BUTTON: '.Documentation-exampleRunButton',\n};\n\n/**\n * This controller enables playground examples to expand their dropdown or\n * generate shareable Go Playground URLs.\n */\nexport class PlaygroundExampleController {\n /**\n * The anchor tag used to identify the container with an example href.\n * There is only one in an example container div.\n */\n private readonly anchorEl: HTMLAnchorElement | null;\n\n /**\n * The error element\n */\n private readonly errorEl: Element | null;\n\n /**\n * Buttons that redirect to an example's playground, this element\n * only exists in executable examples.\n */\n private readonly playButtonEl: Element | null;\n private readonly shareButtonEl: Element | null;\n\n /**\n * Button that formats the code in an example's playground.\n */\n private readonly formatButtonEl: Element | null;\n\n /**\n * Button that runs the code in an example's playground, this element\n * only exists in executable examples.\n */\n private readonly runButtonEl: Element | null;\n\n /**\n * The executable code of an example.\n */\n private readonly inputEl: HTMLTextAreaElement | null;\n\n /**\n * The output of the given example code. This only exists if the\n * author of the package provides an output for this example.\n */\n private readonly outputEl: Element | null;\n\n /**\n * The container for the output.\n */\n private readonly outputContainerEl: HTMLElement | null;\n\n /**\n * @param exampleEl The div that contains playground content for the given example.\n */\n constructor(private readonly exampleEl: HTMLDetailsElement) {\n this.exampleEl = exampleEl;\n this.anchorEl = exampleEl.querySelector('a');\n this.errorEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_ERROR);\n this.playButtonEl = exampleEl.querySelector(PlayExampleClassName.PLAY_BUTTON);\n this.shareButtonEl = exampleEl.querySelector(PlayExampleClassName.SHARE_BUTTON);\n this.formatButtonEl = exampleEl.querySelector(PlayExampleClassName.FORMAT_BUTTON);\n this.runButtonEl = exampleEl.querySelector(PlayExampleClassName.RUN_BUTTON);\n this.inputEl = this.makeTextArea(exampleEl.querySelector(PlayExampleClassName.EXAMPLE_INPUT));\n this.outputEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_OUTPUT);\n this.outputContainerEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_OUTPUT_CONTAINER);\n\n // This is legacy listener to be replaced the listener for shareButtonEl.\n this.playButtonEl?.addEventListener('click', () => this.handleShareButtonClick());\n this.shareButtonEl?.addEventListener('click', () => this.handleShareButtonClick());\n this.formatButtonEl?.addEventListener('click', () => this.handleFormatButtonClick());\n this.runButtonEl?.addEventListener('click', () => this.handleRunButtonClick());\n\n if (!this.inputEl) return;\n\n this.resize();\n this.inputEl.addEventListener('keyup', () => this.resize());\n this.inputEl.addEventListener('keydown', e => this.onKeydown(e));\n }\n\n /**\n * Replace the pre element with a textarea. The examples are initially rendered\n * as pre elements so they're fully visible when JS is disabled.\n */\n makeTextArea(el: Element | null): HTMLTextAreaElement {\n const t = document.createElement('textarea');\n t.classList.add('Documentation-exampleCode', 'code');\n t.spellcheck = false;\n t.value = el?.textContent ?? '';\n el?.parentElement?.replaceChild(t, el);\n return t;\n }\n\n /**\n * Retrieve the hash value of the anchor element.\n */\n getAnchorHash(): string | undefined {\n return this.anchorEl?.hash;\n }\n\n /**\n * Expands the current playground example.\n */\n expand(): void {\n this.exampleEl.open = true;\n }\n\n /**\n * Resizes the input element to accommodate the amount of text present.\n */\n private resize(): void {\n if (this.inputEl?.value) {\n const numLineBreaks = (this.inputEl.value.match(/\\n/g) || []).length;\n // min-height + lines x line-height + padding + border\n this.inputEl.style.height = `${(20 + numLineBreaks * 20 + 12 + 2) / 16}rem`;\n }\n }\n\n /**\n * Handler to override keyboard behavior in the playground's\n * textarea element.\n *\n * Tab key inserts tabs into the example playground instead of\n * switching to the next interactive element.\n * @param e input element keyboard event.\n */\n private onKeydown(e: KeyboardEvent) {\n if (e.key === 'Tab') {\n document.execCommand('insertText', false, '\\t');\n e.preventDefault();\n }\n }\n\n /**\n * Changes the text of the example's input box.\n */\n private setInputText(output: string) {\n if (this.inputEl) {\n this.inputEl.value = output;\n }\n }\n\n /**\n * Changes the text of the example's output box.\n */\n private setOutputText(output: string) {\n if (this.outputEl) {\n this.outputEl.textContent = output;\n }\n if (this.outputContainerEl) {\n this.outputContainerEl.hidden = false;\n }\n }\n\n /**\n * Appends to the text of the example's output box.\n */\n private appendToOutputText(output: string) {\n if (this.outputEl) {\n this.outputEl.textContent += output;\n }\n if (this.outputContainerEl) {\n this.outputContainerEl.hidden = false;\n }\n }\n\n private setOutputHTML(output: string) {\n if (this.outputEl) {\n this.outputEl.innerHTML = output;\n }\n }\n\n /**\n * Sets the error message text and overwrites\n * output box to indicate a failed response.\n */\n private setErrorText(err: string) {\n if (this.errorEl) {\n this.errorEl.textContent = err;\n }\n this.setOutputText('An error has occurred\u2026');\n }\n\n private getCodeWithModFile(): string {\n let codeWithModFile = this.inputEl?.value ?? '';\n const moduleVars = document.querySelector('.js-playgroundVars')?.dataset ?? {};\n if (moduleVars.modulepath !== 'std') {\n codeWithModFile = codeWithModFile.concat(`\n-- go.mod --\nmodule play.ground\n\nrequire ${moduleVars.modulepath} ${moduleVars.version}\n`);\n }\n\n return codeWithModFile;\n }\n\n /**\n * Opens a new window to play.golang.org using the\n * example snippet's code in the playground.\n */\n private handleShareButtonClick() {\n const PLAYGROUND_BASE_URL = 'https://play.golang.org/p/';\n\n this.setOutputText('Waiting for remote server\u2026');\n\n fetch(abs('/play/share'), {\n method: 'POST',\n body: this.getCodeWithModFile(),\n })\n .then(res => res.text())\n .then(shareId => {\n const href = PLAYGROUND_BASE_URL + shareId;\n this.setOutputHTML(`${href}`);\n window.open(href);\n })\n .catch(err => {\n this.setErrorText(err);\n });\n }\n\n /**\n * Runs gofmt on the example snippet in the playground.\n */\n private handleFormatButtonClick() {\n this.setOutputText('Waiting for remote server\u2026');\n const body = new FormData();\n body.append('body', this.inputEl?.value ?? '');\n\n fetch(abs('/play/fmt'), {\n method: 'POST',\n body: body,\n })\n .then(res => res.json())\n .then(({ Body, Error }) => {\n this.setOutputText(Error || 'Done.');\n if (Body) {\n this.setInputText(Body);\n this.resize();\n }\n })\n .catch(err => {\n this.setErrorText(err);\n });\n }\n\n /**\n * Runs the code snippet in the example playground.\n */\n private handleRunButtonClick() {\n this.setOutputText('Waiting for remote server\u2026');\n\n fetch(abs('/play/compile'), {\n method: 'POST',\n body: JSON.stringify({ body: this.getCodeWithModFile(), version: 2 }),\n })\n .then(res => res.json())\n .then(async ({ Events, Errors }) => {\n this.setOutputText(Errors || '');\n for (const e of Events || []) {\n this.appendToOutputText(e.Message);\n await new Promise(resolve => setTimeout(resolve, e.Delay / 1000000));\n }\n })\n .catch(err => {\n this.setErrorText(err);\n });\n }\n}\n\nexport function initPlaygrounds(): void {\n const exampleHashRegex = location.hash.match(/^#(example-.*)$/);\n if (exampleHashRegex) {\n const exampleHashEl = document.getElementById(exampleHashRegex[1]) as HTMLDetailsElement;\n if (exampleHashEl) {\n exampleHashEl.open = true;\n }\n }\n\n // We use a spread operator to convert a nodelist into an array of elements.\n const exampleHrefs = [\n ...document.querySelectorAll(PlayExampleClassName.PLAY_HREF),\n ];\n\n /**\n * Sometimes exampleHrefs and playContainers are in different order, so we\n * find an exampleHref from a common hash.\n * @param playContainer - playground container\n */\n const findExampleHash = (playContainer: PlaygroundExampleController) =>\n exampleHrefs.find(ex => {\n return ex.hash === playContainer.getAnchorHash();\n });\n\n for (const el of document.querySelectorAll(PlayExampleClassName.PLAY_CONTAINER)) {\n // There should be the same amount of hrefs referencing examples as example containers.\n const playContainer = new PlaygroundExampleController(el as HTMLDetailsElement);\n const exampleHref = findExampleHash(playContainer);\n if (exampleHref) {\n exampleHref.addEventListener('click', () => {\n playContainer.expand();\n });\n } else {\n console.warn('example href not found');\n }\n }\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { TreeNavController } from './tree.js';\n\nexport class SelectNavController {\n constructor(private el: Element) {\n this.el.addEventListener('change', e => {\n const target = e.target as HTMLSelectElement;\n let href = target.value;\n if (!target.value.startsWith('/')) {\n href = '/' + href;\n }\n window.location.href = href;\n });\n }\n}\n\nexport function makeSelectNav(tree: TreeNavController): HTMLLabelElement {\n const label = document.createElement('label');\n label.classList.add('go-Label');\n label.setAttribute('aria-label', 'Menu');\n const select = document.createElement('select');\n select.classList.add('go-Select', 'js-selectNav');\n label.appendChild(select);\n const outline = document.createElement('optgroup');\n outline.label = 'Outline';\n select.appendChild(outline);\n const groupMap: Record = {};\n let group: HTMLOptGroupElement;\n for (const t of tree.treeitems) {\n if (Number(t.depth) > 4) continue;\n if (t.groupTreeitem) {\n group = groupMap[t.groupTreeitem.label];\n if (!group) {\n group = groupMap[t.groupTreeitem.label] = document.createElement('optgroup');\n group.label = t.groupTreeitem.label;\n select.appendChild(group);\n }\n } else {\n group = outline;\n }\n const o = document.createElement('option');\n o.label = t.label;\n o.textContent = t.label;\n o.value = (t.el as HTMLAnchorElement).href.replace(window.location.origin, '').replace('/', '');\n group.appendChild(o);\n }\n tree.addObserver(t => {\n const hash = (t.el as HTMLAnchorElement).hash;\n const value = select.querySelector(`[value$=\"${hash}\"]`)?.value;\n if (value) {\n select.value = value;\n }\n }, 50);\n return label;\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * TreeNavController is the navigation tree component of the documentation page.\n * It adds accessiblity attributes to a tree, observes the heading elements\n * focus the topmost link for headings visible on the page, and implements the\n * WAI-ARIA Treeview Design Pattern with full\n * [keyboard support](https://www.w3.org/TR/wai-aria-practices/examples/treeview/treeview-2/treeview-2a.html#kbd_label).\n */\nexport class TreeNavController {\n treeitems: TreeItem[];\n\n /**\n * firstChars is the first character of each treeitem in the same order\n * as this.treeitems. We use this array to set focus by character when\n * navigating the tree with a keyboard.\n */\n private firstChars: string[];\n private firstTreeitem: TreeItem | null;\n private lastTreeitem: TreeItem | null;\n private observerCallbacks: ((t: TreeItem) => void)[];\n\n constructor(private el: HTMLElement) {\n this.treeitems = [];\n this.firstChars = [];\n this.firstTreeitem = null;\n this.lastTreeitem = null;\n this.observerCallbacks = [];\n this.init();\n }\n\n private init(): void {\n this.handleResize();\n window.addEventListener('resize', this.handleResize);\n this.findTreeItems();\n this.updateVisibleTreeitems();\n this.observeTargets();\n if (this.firstTreeitem) {\n this.firstTreeitem.el.tabIndex = 0;\n }\n }\n\n private handleResize = (): void => {\n this.el.style.setProperty('--js-tree-height', '100vh');\n this.el.style.setProperty('--js-tree-height', this.el.clientHeight + 'px');\n };\n\n private observeTargets() {\n this.addObserver(treeitem => {\n this.expandTreeitem(treeitem);\n this.setSelected(treeitem);\n // TODO: Fix scroll issue in https://golang.org/issue/47450.\n // treeitem.el.scrollIntoView({ block: 'nearest' });\n });\n\n const targets = new Map();\n const observer = new IntersectionObserver(\n entries => {\n for (const entry of entries) {\n targets.set(entry.target.id, entry.isIntersecting || entry.intersectionRatio === 1);\n }\n for (const [id, isIntersecting] of targets) {\n if (isIntersecting) {\n const active = this.treeitems.find(t =>\n (t.el as HTMLAnchorElement)?.href.endsWith(`#${id}`)\n );\n if (active) {\n for (const fn of this.observerCallbacks) {\n fn(active);\n }\n }\n break;\n }\n }\n },\n {\n threshold: 1.0,\n rootMargin: '-60px 0px 0px 0px',\n }\n );\n\n for (const href of this.treeitems.map(t => t.el.getAttribute('href'))) {\n if (href) {\n const id = href.replace(window.location.origin, '').replace('/', '').replace('#', '');\n const target = document.getElementById(id);\n if (target && !id.startsWith('example-')) {\n observer.observe(target);\n }\n }\n }\n }\n\n addObserver(fn: (t: TreeItem) => void, delay = 200): void {\n this.observerCallbacks.push(debounce(fn, delay));\n }\n\n setFocusToNextItem(currentItem: TreeItem): void {\n let nextItem = null;\n for (let i = currentItem.index + 1; i < this.treeitems.length; i++) {\n const ti = this.treeitems[i];\n if (ti.isVisible) {\n nextItem = ti;\n break;\n }\n }\n if (nextItem) {\n this.setFocusToItem(nextItem);\n }\n }\n\n setFocusToPreviousItem(currentItem: TreeItem): void {\n let prevItem = null;\n for (let i = currentItem.index - 1; i > -1; i--) {\n const ti = this.treeitems[i];\n if (ti.isVisible) {\n prevItem = ti;\n break;\n }\n }\n if (prevItem) {\n this.setFocusToItem(prevItem);\n }\n }\n\n setFocusToParentItem(currentItem: TreeItem): void {\n if (currentItem.groupTreeitem) {\n this.setFocusToItem(currentItem.groupTreeitem);\n }\n }\n\n setFocusToFirstItem(): void {\n this.firstTreeitem && this.setFocusToItem(this.firstTreeitem);\n }\n\n setFocusToLastItem(): void {\n this.lastTreeitem && this.setFocusToItem(this.lastTreeitem);\n }\n\n setSelected(currentItem: TreeItem): void {\n for (const l1 of this.el.querySelectorAll('[aria-expanded=\"true\"]')) {\n if (l1 === currentItem.el) continue;\n if (!l1.nextElementSibling?.contains(currentItem.el)) {\n l1.setAttribute('aria-expanded', 'false');\n }\n }\n for (const l1 of this.el.querySelectorAll('[aria-selected]')) {\n if (l1 !== currentItem.el) {\n l1.setAttribute('aria-selected', 'false');\n }\n }\n currentItem.el.setAttribute('aria-selected', 'true');\n this.updateVisibleTreeitems();\n this.setFocusToItem(currentItem, false);\n }\n\n expandTreeitem(treeitem: TreeItem): void {\n let currentItem: TreeItem | null = treeitem;\n while (currentItem) {\n if (currentItem.isExpandable) {\n currentItem.el.setAttribute('aria-expanded', 'true');\n }\n currentItem = currentItem.groupTreeitem;\n }\n this.updateVisibleTreeitems();\n }\n\n expandAllSiblingItems(currentItem: TreeItem): void {\n for (const ti of this.treeitems) {\n if (ti.groupTreeitem === currentItem.groupTreeitem && ti.isExpandable) {\n this.expandTreeitem(ti);\n }\n }\n }\n\n collapseTreeitem(currentItem: TreeItem): void {\n let groupTreeitem = null;\n\n if (currentItem.isExpanded()) {\n groupTreeitem = currentItem;\n } else {\n groupTreeitem = currentItem.groupTreeitem;\n }\n\n if (groupTreeitem) {\n groupTreeitem.el.setAttribute('aria-expanded', 'false');\n this.updateVisibleTreeitems();\n this.setFocusToItem(groupTreeitem);\n }\n }\n\n setFocusByFirstCharacter(currentItem: TreeItem, char: string): void {\n let start: number, index: number;\n char = char.toLowerCase();\n\n // Get start index for search based on position of currentItem\n start = currentItem.index + 1;\n if (start === this.treeitems.length) {\n start = 0;\n }\n\n // Check remaining slots in the menu\n index = this.getIndexFirstChars(start, char);\n\n // If not found in remaining slots, check from beginning\n if (index === -1) {\n index = this.getIndexFirstChars(0, char);\n }\n\n // If match was found...\n if (index > -1) {\n this.setFocusToItem(this.treeitems[index]);\n }\n }\n\n private findTreeItems() {\n const findItems = (el: HTMLElement, group: TreeItem | null) => {\n let ti = group;\n let curr = el.firstElementChild as HTMLElement;\n while (curr) {\n if (curr.tagName === 'A' || curr.tagName === 'SPAN') {\n ti = new TreeItem(curr, this, group);\n this.treeitems.push(ti);\n this.firstChars.push(ti.label.substring(0, 1).toLowerCase());\n }\n if (curr.firstElementChild) {\n findItems(curr, ti);\n }\n curr = curr.nextElementSibling as HTMLElement;\n }\n };\n findItems(this.el as HTMLElement, null);\n this.treeitems.map((ti, idx) => (ti.index = idx));\n }\n\n private updateVisibleTreeitems(): void {\n this.firstTreeitem = this.treeitems[0];\n\n for (const ti of this.treeitems) {\n let parent = ti.groupTreeitem;\n ti.isVisible = true;\n while (parent && parent.el !== this.el) {\n if (!parent.isExpanded()) {\n ti.isVisible = false;\n }\n parent = parent.groupTreeitem;\n }\n if (ti.isVisible) {\n this.lastTreeitem = ti;\n }\n }\n }\n\n private setFocusToItem(treeitem: TreeItem, focusEl = true) {\n treeitem.el.tabIndex = 0;\n if (focusEl) {\n treeitem.el.focus();\n }\n for (const ti of this.treeitems) {\n if (ti !== treeitem) {\n ti.el.tabIndex = -1;\n }\n }\n }\n\n private getIndexFirstChars(startIndex: number, char: string): number {\n for (let i = startIndex; i < this.firstChars.length; i++) {\n if (this.treeitems[i].isVisible && char === this.firstChars[i]) {\n return i;\n }\n }\n return -1;\n }\n}\n\nclass TreeItem {\n el: HTMLElement;\n groupTreeitem: TreeItem | null;\n label: string;\n isExpandable: boolean;\n isVisible: boolean;\n depth: number;\n index: number;\n\n private tree: TreeNavController;\n private isInGroup: boolean;\n\n constructor(el: HTMLElement, treeObj: TreeNavController, group: TreeItem | null) {\n el.tabIndex = -1;\n this.el = el;\n this.groupTreeitem = group;\n this.label = el.textContent?.trim() ?? '';\n this.tree = treeObj;\n this.depth = (group?.depth || 0) + 1;\n this.index = 0;\n\n const parent = el.parentElement;\n if (parent?.tagName.toLowerCase() === 'li') {\n parent?.setAttribute('role', 'none');\n }\n el.setAttribute('aria-level', this.depth + '');\n if (el.getAttribute('aria-label')) {\n this.label = el?.getAttribute('aria-label')?.trim() ?? '';\n }\n\n this.isExpandable = false;\n this.isVisible = false;\n this.isInGroup = !!group;\n\n let curr = el.nextElementSibling;\n while (curr) {\n if (curr.tagName.toLowerCase() == 'ul') {\n const groupId = `${group?.label ?? ''} nav group ${this.label}`.replace(/[\\W_]+/g, '_');\n el.setAttribute('aria-owns', groupId);\n el.setAttribute('aria-expanded', 'false');\n curr.setAttribute('role', 'group');\n curr.setAttribute('id', groupId);\n this.isExpandable = true;\n break;\n }\n\n curr = curr.nextElementSibling;\n }\n this.init();\n }\n\n private init() {\n this.el.tabIndex = -1;\n if (!this.el.getAttribute('role')) {\n this.el.setAttribute('role', 'treeitem');\n }\n this.el.addEventListener('keydown', this.handleKeydown.bind(this));\n this.el.addEventListener('click', this.handleClick.bind(this));\n this.el.addEventListener('focus', this.handleFocus.bind(this));\n this.el.addEventListener('blur', this.handleBlur.bind(this));\n }\n\n isExpanded() {\n if (this.isExpandable) {\n return this.el.getAttribute('aria-expanded') === 'true';\n }\n\n return false;\n }\n\n isSelected() {\n return this.el.getAttribute('aria-selected') === 'true';\n }\n\n private handleClick(event: MouseEvent) {\n // only process click events that directly happened on this treeitem\n if (event.target !== this.el && event.target !== this.el.firstElementChild) {\n return;\n }\n if (this.isExpandable) {\n if (this.isExpanded() && this.isSelected()) {\n this.tree.collapseTreeitem(this);\n } else {\n this.tree.expandTreeitem(this);\n }\n event.stopPropagation();\n }\n this.tree.setSelected(this);\n }\n\n private handleFocus() {\n let el = this.el;\n if (this.isExpandable) {\n el = (el.firstElementChild as HTMLElement) ?? el;\n }\n el.classList.add('focus');\n }\n\n private handleBlur() {\n let el = this.el;\n if (this.isExpandable) {\n el = (el.firstElementChild as HTMLElement) ?? el;\n }\n el.classList.remove('focus');\n }\n\n private handleKeydown(event: KeyboardEvent) {\n if (event.altKey || event.ctrlKey || event.metaKey) {\n return;\n }\n\n let captured = false;\n switch (event.key) {\n case ' ':\n case 'Enter':\n if (this.isExpandable) {\n if (this.isExpanded() && this.isSelected()) {\n this.tree.collapseTreeitem(this);\n } else {\n this.tree.expandTreeitem(this);\n }\n captured = true;\n } else {\n event.stopPropagation();\n }\n this.tree.setSelected(this);\n break;\n\n case 'ArrowUp':\n this.tree.setFocusToPreviousItem(this);\n captured = true;\n break;\n\n case 'ArrowDown':\n this.tree.setFocusToNextItem(this);\n captured = true;\n break;\n\n case 'ArrowRight':\n if (this.isExpandable) {\n if (this.isExpanded()) {\n this.tree.setFocusToNextItem(this);\n } else {\n this.tree.expandTreeitem(this);\n }\n }\n captured = true;\n break;\n\n case 'ArrowLeft':\n if (this.isExpandable && this.isExpanded()) {\n this.tree.collapseTreeitem(this);\n captured = true;\n } else {\n if (this.isInGroup) {\n this.tree.setFocusToParentItem(this);\n captured = true;\n }\n }\n break;\n\n case 'Home':\n this.tree.setFocusToFirstItem();\n captured = true;\n break;\n\n case 'End':\n this.tree.setFocusToLastItem();\n captured = true;\n break;\n\n default:\n if (event.key.length === 1 && event.key.match(/\\S/)) {\n if (event.key == '*') {\n this.tree.expandAllSiblingItems(this);\n } else {\n this.tree.setFocusByFirstCharacter(this, event.key);\n }\n captured = true;\n }\n break;\n }\n\n if (captured) {\n event.stopPropagation();\n event.preventDefault();\n }\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction debounce any>(func: T, wait: number) {\n let timeout: ReturnType | null;\n return (...args: Parameters) => {\n const later = () => {\n timeout = null;\n func(...args);\n };\n if (timeout) {\n clearTimeout(timeout);\n }\n timeout = setTimeout(later, wait);\n };\n}\n", "/*!\n * @license\n * Copyright 2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * Controller for a table element with expandable rows. Adds event listeners to\n * a toggle within a table row that controls visiblity of additional related\n * rows in the table.\n *\n * @example\n * ```typescript\n * import {ExpandableRowsTableController} from '/static/js/table';\n *\n * const el = document .querySelector('.js-myTableElement')\n * new ExpandableRowsTableController(el));\n * ```\n */\nexport class ExpandableRowsTableController {\n private rows: HTMLTableRowElement[];\n private toggles: HTMLButtonElement[];\n\n /**\n * Create a table controller.\n * @param table - The table element to which the controller binds.\n */\n constructor(private table: HTMLTableElement, private toggleAll?: HTMLButtonElement | null) {\n this.rows = Array.from(table.querySelectorAll('[data-aria-controls]'));\n this.toggles = Array.from(this.table.querySelectorAll('[aria-expanded]'));\n this.setAttributes();\n this.attachEventListeners();\n this.update();\n }\n\n /**\n * setAttributes sets data-aria-* and data-id attributes to regular\n * html attributes as a workaround for limitations from safehtml.\n */\n private setAttributes() {\n for (const a of ['data-aria-controls', 'data-aria-labelledby', 'data-id']) {\n this.table.querySelectorAll(`[${a}]`).forEach(t => {\n t.setAttribute(a.replace('data-', ''), t.getAttribute(a) ?? '');\n t.removeAttribute(a);\n });\n }\n }\n\n private attachEventListeners() {\n this.rows.forEach(t => {\n t.addEventListener('click', e => {\n this.handleToggleClick(e);\n });\n });\n this.toggleAll?.addEventListener('click', () => {\n this.expandAllItems();\n });\n\n document.addEventListener('keydown', e => {\n if ((e.ctrlKey || e.metaKey) && e.key === 'f') {\n this.expandAllItems();\n }\n });\n }\n\n private handleToggleClick(e: MouseEvent) {\n let target = e.currentTarget as HTMLTableRowElement | null;\n if (!target?.hasAttribute('aria-expanded')) {\n target = this.table.querySelector(\n `button[aria-controls=\"${target?.getAttribute('aria-controls')}\"]`\n );\n }\n const isExpanded = target?.getAttribute('aria-expanded') === 'true';\n target?.setAttribute('aria-expanded', isExpanded ? 'false' : 'true');\n e.stopPropagation();\n this.update();\n }\n\n expandAllItems = (): void => {\n this.toggles.map(t => t.setAttribute('aria-expanded', 'true'));\n this.update();\n };\n\n private collapseAllItems = () => {\n this.toggles.map(t => t.setAttribute('aria-expanded', 'false'));\n this.update();\n };\n\n private update = () => {\n this.updateVisibleItems();\n setTimeout(() => this.updateGlobalToggle());\n };\n\n private updateVisibleItems() {\n this.rows.map(t => {\n const isExpanded = t?.getAttribute('aria-expanded') === 'true';\n const rowIds = t?.getAttribute('aria-controls')?.trimEnd().split(' ');\n rowIds?.map(id => {\n const target = document.getElementById(`${id}`);\n if (isExpanded) {\n target?.classList.add('visible');\n target?.classList.remove('hidden');\n } else {\n target?.classList.add('hidden');\n target?.classList.remove('visible');\n }\n });\n });\n }\n\n private updateGlobalToggle() {\n if (!this.toggleAll) return;\n if (this.rows.some(t => t.hasAttribute('aria-expanded'))) {\n this.toggleAll.style.display = 'block';\n }\n const someCollapsed = this.toggles.some(el => el.getAttribute('aria-expanded') === 'false');\n if (someCollapsed) {\n this.toggleAll.innerText = 'Expand all';\n this.toggleAll.onclick = this.expandAllItems;\n this.toggleAll.setAttribute('aria-label', 'Expand all directories');\n this.toggleAll.setAttribute('aria-live', 'polite');\n } else {\n this.toggleAll.innerText = 'Collapse all';\n this.toggleAll.onclick = this.collapseAllItems;\n this.toggleAll.setAttribute('aria-label', 'Collapse all directories');\n this.toggleAll.setAttribute('aria-live', 'polite');\n }\n }\n}\n", "import { initPlaygrounds } from 'static/shared/playground/playground';\nimport { SelectNavController, makeSelectNav } from 'static/shared/outline/select';\nimport { TreeNavController } from 'static/shared/outline/tree';\nimport { ExpandableRowsTableController } from 'static/shared/table/table';\n\ninitPlaygrounds();\n\nconst directories = document.querySelector('.js-expandableTable');\nif (directories) {\n const table = new ExpandableRowsTableController(\n directories,\n document.querySelector('.js-expandAllDirectories')\n );\n // Expand directories on page load with expand-directories query param.\n if (window.location.search.includes('expand-directories')) {\n table.expandAllItems();\n }\n\n const internalToggle = document.querySelector('.js-showInternalDirectories');\n if (internalToggle) {\n if (document.querySelector('.UnitDirectories-internal')) {\n internalToggle.style.display = 'block';\n internalToggle.setAttribute('aria-label', 'Show Internal Directories');\n internalToggle.setAttribute('aria-describedby', 'showInternal-description');\n }\n internalToggle.addEventListener('click', () => {\n if (directories.classList.contains('UnitDirectories-showInternal')) {\n directories.classList.remove('UnitDirectories-showInternal');\n internalToggle.innerText = 'Show internal';\n internalToggle.setAttribute('aria-label', 'Show Internal Directories');\n internalToggle.setAttribute('aria-live', 'polite');\n internalToggle.setAttribute('aria-describedby', 'showInternal-description');\n } else {\n directories.classList.add('UnitDirectories-showInternal');\n internalToggle.innerText = 'Hide internal';\n internalToggle.setAttribute('aria-label', 'Hide Internal Directories');\n internalToggle.setAttribute('aria-live', 'polite');\n internalToggle.setAttribute('aria-describedby', 'hideInternal-description');\n }\n });\n }\n if (document.querySelector('html[data-local=\"true\"]')) {\n internalToggle?.click();\n }\n}\n\nconst treeEl = document.querySelector('.js-tree');\nif (treeEl) {\n const treeCtrl = new TreeNavController(treeEl);\n const select = makeSelectNav(treeCtrl);\n const mobileNav = document.querySelector('.js-mainNavMobile');\n if (mobileNav && mobileNav.firstElementChild) {\n mobileNav?.replaceChild(select, mobileNav.firstElementChild);\n }\n if (select.firstElementChild) {\n new SelectNavController(select.firstElementChild);\n }\n}\n\n/**\n * Event handlers for expanding and collapsing the readme section.\n */\nconst readme = document.querySelector('.js-readme');\nconst readmeContent = document.querySelector('.js-readmeContent');\nconst readmeOutline = document.querySelector('.js-readmeOutline');\nconst readmeExpand = document.querySelectorAll('.js-readmeExpand');\nconst readmeCollapse = document.querySelector('.js-readmeCollapse');\nconst mobileNavSelect = document.querySelector('.DocNavMobile-select');\nif (readme && readmeContent && readmeOutline && readmeExpand.length && readmeCollapse) {\n if (readme.clientHeight > 320) {\n readme?.classList.remove('UnitReadme--expanded');\n readme?.classList.add('UnitReadme--toggle');\n }\n if (window.location.hash.includes('readme')) {\n expandReadme();\n }\n mobileNavSelect?.addEventListener('change', e => {\n if ((e.target as HTMLSelectElement).value.startsWith('readme-')) {\n expandReadme();\n }\n });\n readmeExpand.forEach(el =>\n el.addEventListener('click', e => {\n e.preventDefault();\n expandReadme();\n readme.scrollIntoView();\n })\n );\n readmeCollapse.addEventListener('click', e => {\n e.preventDefault();\n readme.classList.remove('UnitReadme--expanded');\n if (readmeExpand[1]) {\n readmeExpand[1].scrollIntoView({ block: 'center' });\n }\n });\n readmeContent.addEventListener('keyup', () => {\n expandReadme();\n });\n readmeContent.addEventListener('click', () => {\n expandReadme();\n });\n readmeOutline.addEventListener('click', () => {\n expandReadme();\n });\n document.addEventListener('keydown', e => {\n if ((e.ctrlKey || e.metaKey) && e.key === 'f') {\n expandReadme();\n }\n });\n}\n\n/**\n * expandReadme expands the readme and adds the section-readme hash to the\n * URL so it stays expanded when navigating back from an external link.\n */\nfunction expandReadme() {\n history.replaceState(null, '', `${location.pathname}${location.search}#section-readme`);\n readme?.classList.add('UnitReadme--expanded');\n}\n\n/**\n * Expand details items that are focused. This will expand\n * deprecated symbols when they are navigated to from the index\n * or a direct link.\n */\nfunction openDeprecatedSymbol() {\n if (!location.hash) return;\n const heading = document.getElementById(location.hash.slice(1));\n const grandParent = heading?.parentElement?.parentElement as HTMLDetailsElement | null;\n if (grandParent?.nodeName === 'DETAILS') {\n grandParent.open = true;\n }\n}\nopenDeprecatedSymbol();\nwindow.addEventListener('hashchange', () => openDeprecatedSymbol());\n\n/**\n * Listen for changes in the build context dropdown.\n */\ndocument.querySelectorAll('.js-buildContextSelect').forEach(el => {\n el.addEventListener('change', e => {\n window.location.search = `?GOOS=${(e.target as HTMLSelectElement).value}`;\n });\n});\n\n/**\n * fork\uFF1Aunexported\uFF08\u79C1\u6709\uFF09\u7B26\u53F7 toggle\u3002\n *\n * \u80CC\u666F\uFF1Apkgsite -show-unexported flag \u8BA9 godoc \u628A\u79C1\u6709 type/func/method\n * \u90FD\u6E32\u5230 page\u3002\u4F46\u8BFB\u8005\u5927\u591A\u6570\u65F6\u5019\u53EA\u5173\u5FC3 public API\uFF0C\u79C1\u6709\u7684\u592A\u591A\u53CD\u800C\u62D6\u6162\n * \u9605\u8BFB\u3002\u8FD9\u5C42\u5728 client \u7AEF\u6309 id \u9996\u5B57\u6BCD\u5927\u5C0F\u5199\u81EA\u52A8 hide \u79C1\u6709 declaration +\n * index \u94FE\u63A5\uFF0C\u518D\u6CE8\u5165\u4E00\u4E2A toggle button \u4E00\u952E\u5207\u663E\u793A\u3002\u72B6\u6001\u7528 localStorage\n * \u8BB0\u4E0B\uFF0C\u8DE8\u9875\u4FDD\u7559\u3002\n */\n(() => {\n if (!document.querySelector('h4[data-kind]')) return; // \u975E godoc \u8BE6\u60C5\u9875\n\n // method id \u5F62\u5982 \"Type.method\"\uFF0C\u53D6\u6700\u540E\u6BB5\u5224\u79C1\u6709\uFF1B\u5176\u4ED6\u76F4\u63A5\u5224 id \u672C\u8EAB\u3002\n // \u6392\u9664 pkg-overview / section-readme / pkg-index \u8FD9\u7C7B\u9875\u9762 anchor\u2014\u2014\u5B83\u4EEC\u662F\n // \u5C0F\u5199\u8D77\u5934\u4F46\u4E0D\u662F Go \u7B26\u53F7\uFF0C\u626B\u5230\u4F1A\u8BEF\u6807\u9690\u85CF\u3002\n const isUnexported = (id: string): boolean => {\n if (id.startsWith('pkg-') || id.startsWith('section-') || id.startsWith('hdr-')) {\n return false;\n }\n const last = id.split('.').pop() ?? id;\n return /^[a-z]/.test(last);\n };\n\n // \u6807 declaration wrapper\uFF1Afunc \u5305\u5728 .Documentation-function\uFF1Btype / method\n // \u5305\u5728 .Documentation-type / .Documentation-typeFunc / .Documentation-typeMethod\u3002\n document.querySelectorAll('h4[data-kind][id]').forEach(h => {\n if (!isUnexported(h.id)) return;\n const wrapper = h.closest(\n '.Documentation-function, .Documentation-type, .Documentation-typeFunc, .Documentation-typeMethod'\n );\n wrapper?.classList.add('Documentation-unexported');\n });\n\n // index \u5217\u8868\u9879 + \u5DE6\u4FA7\u8FB9\u680F\uFF08go-Tree outline\uFF09\u6309\u94FE\u63A5\u9996\u5B57\u6BCD\u5224\u3002\n // \u4FA7\u8FB9\u680F\u94FE\u63A5 selector \u76F4\u63A5\u5339\u914D\u6240\u6709 .go-Tree a[href^=\"#\"]\u2014\u2014pkg-overview /\n // section-readme \u7B49\u5BFC\u822A anchor \u5DF2\u88AB isUnexported \u5934\u90E8\u6392\u9664\uFF0C\u4E0D\u4F1A\u8BEF\u6807\u3002\n document\n .querySelectorAll(\n '.Documentation-indexFunction a[href^=\"#\"], ' +\n '.Documentation-indexType a[href^=\"#\"], ' +\n '.Documentation-indexTypeFunctions a[href^=\"#\"], ' +\n '.Documentation-indexTypeMethods a[href^=\"#\"], ' +\n '.go-Tree a[href^=\"#\"]'\n )\n .forEach(a => {\n if (isUnexported(a.getAttribute('href')!.slice(1))) {\n a.closest('li')?.classList.add('Documentation-unexported');\n }\n });\n\n // \u6CE8\u5165 CSS\uFF08\u4E0D\u52A8 main.css build pipeline\uFF0C\u907F\u514D\u589E\u91CF\u6539 esbuild \u8F93\u51FA\uFF09\n const style = document.createElement('style');\n style.textContent =\n 'body:not(.show-unexported) .Documentation-unexported{display:none}';\n document.head.appendChild(style);\n\n // \u6CE8\u5165 toggle button\u2014\u2014\u653E Index \u6807\u9898\u65C1\u8FB9\u6700\u663E\u773C\uFF0C\u8DDF \"Show internal\" \u540C\u4F4D\n const indexHeader = document.querySelector('#pkg-index');\n if (!indexHeader) return;\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'go-Button go-Button--inline';\n btn.style.marginLeft = '0.75rem';\n btn.style.fontSize = '0.875rem';\n btn.style.verticalAlign = 'middle';\n\n const STORE_KEY = 'gogodocs:showUnexported';\n const apply = (show: boolean) => {\n document.body.classList.toggle('show-unexported', show);\n btn.textContent = show ? 'Hide unexported' : 'Show unexported';\n try {\n localStorage.setItem(STORE_KEY, show ? '1' : '0');\n } catch {\n /* localStorage \u4E0D\u53EF\u7528\uFF08\u9690\u79C1\u6A21\u5F0F / \u6587\u4EF6\u534F\u8BAE\uFF09\u65F6\u5FFD\u7565\uFF0C\u53EA\u4E22\u5931\u8DE8\u9875\u8BB0\u5FC6 */\n }\n };\n\n apply(localStorage.getItem(STORE_KEY) === '1');\n btn.addEventListener('click', () =>\n apply(!document.body.classList.contains('show-unexported'))\n );\n indexHeader.appendChild(btn);\n})();\n"], + "sourcesContent": ["/**\n * @license\n * Copyright 2024 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * \u7AD9\u70B9 URL \u5B50\u8DEF\u5F84\u524D\u7F00\uFF0C\u4ECE \u8BFB\u51FA\u3002\n *\n * fork \u7528 -base-path=/pkgsitex \u542F\u52A8\u65F6\uFF0Cserver \u7AEF\u6A21\u677F\u628A attribute \u5199\u6210\n * \"/pkgsitex\"\uFF1B\u4E0A\u6E38\u516C\u7F51 pkg.go.dev \u9ED8\u8BA4\u6302\u6839\uFF0Cattribute \u4E3A\u7A7A\u5B57\u7B26\u4E32\u3002\n *\n * \u5728 \u4E4B\u5916\u7684\u811A\u672C\u521D\u59CB\u5316\u9636\u6BB5\u8C03\u7528\u90FD\u5B89\u5168\u2014\u2014documentElement \u4E00\u5B9A\u5B58\u5728\u3002\n */\nexport function getBasePath(): string {\n return document.documentElement.dataset.basePath ?? '';\n}\n\n/**\n * \u7ED9\u4EE5 / \u5F00\u5934\u7684\u7EDD\u5BF9\u8DEF\u5F84\u52A0 BasePath \u524D\u7F00\u3002\n *\n * \u7528\u4E8E fetch URL / innerHTML src / location \u6BD4\u8F83\u7B49\u4EFB\u4F55\u9700\u8981\u62FC\u7AD9\u70B9\u7EDD\u5BF9 path\n * \u7684\u5730\u65B9\u3002\u975E\u7EDD\u5BF9\u8DEF\u5F84\uFF08\u4E0D\u4EE5 / \u5F00\u5934\uFF09\u539F\u6837\u8FD4\u56DE\u2014\u2014caller \u81EA\u5DF1\u4FDD\u8BC1\u8BED\u4E49\u3002\n *\n * \u4F8B\uFF1A\n * abs('/play/share') // \u2192 '/pkgsitex/play/share' \u6216 '/play/share'\n * abs('/static/foo.svg') // \u2192 '/pkgsitex/static/foo.svg' \u6216 '/static/foo.svg'\n * abs('relative/x') // \u2192 'relative/x'\uFF08\u4E0D\u52A8\uFF09\n */\nexport function abs(p: string): string {\n if (!p.startsWith('/')) return p;\n return getBasePath() + p;\n}\n", "/*!\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n// This file implements the playground implementation of the documentation\n// page. The playground involves a \"play\" button that allows you to open up\n// a new link to play.golang.org using the example code.\n\n// The CSS is in static/frontend/unit/main/_doc.css\n\nimport { abs } from '../base-path/base-path';\n\n/**\n * CSS classes used by PlaygroundExampleController\n */\nconst PlayExampleClassName = {\n PLAY_HREF: '.js-exampleHref',\n PLAY_CONTAINER: '.js-exampleContainer',\n EXAMPLE_INPUT: '.Documentation-exampleCode',\n EXAMPLE_OUTPUT: '.Documentation-exampleOutput',\n EXAMPLE_OUTPUT_CONTAINER: '.js-exampleOutputContainer',\n EXAMPLE_ERROR: '.Documentation-exampleError',\n PLAY_BUTTON: '.Documentation-examplePlayButton',\n SHARE_BUTTON: '.Documentation-exampleShareButton',\n FORMAT_BUTTON: '.Documentation-exampleFormatButton',\n RUN_BUTTON: '.Documentation-exampleRunButton',\n};\n\n/**\n * This controller enables playground examples to expand their dropdown or\n * generate shareable Go Playground URLs.\n */\nexport class PlaygroundExampleController {\n /**\n * The anchor tag used to identify the container with an example href.\n * There is only one in an example container div.\n */\n private readonly anchorEl: HTMLAnchorElement | null;\n\n /**\n * The error element\n */\n private readonly errorEl: Element | null;\n\n /**\n * Buttons that redirect to an example's playground, this element\n * only exists in executable examples.\n */\n private readonly playButtonEl: Element | null;\n private readonly shareButtonEl: Element | null;\n\n /**\n * Button that formats the code in an example's playground.\n */\n private readonly formatButtonEl: Element | null;\n\n /**\n * Button that runs the code in an example's playground, this element\n * only exists in executable examples.\n */\n private readonly runButtonEl: Element | null;\n\n /**\n * The executable code of an example.\n */\n private readonly inputEl: HTMLTextAreaElement | null;\n\n /**\n * The output of the given example code. This only exists if the\n * author of the package provides an output for this example.\n */\n private readonly outputEl: Element | null;\n\n /**\n * The container for the output.\n */\n private readonly outputContainerEl: HTMLElement | null;\n\n /**\n * @param exampleEl The div that contains playground content for the given example.\n */\n constructor(private readonly exampleEl: HTMLDetailsElement) {\n this.exampleEl = exampleEl;\n this.anchorEl = exampleEl.querySelector('a');\n this.errorEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_ERROR);\n this.playButtonEl = exampleEl.querySelector(PlayExampleClassName.PLAY_BUTTON);\n this.shareButtonEl = exampleEl.querySelector(PlayExampleClassName.SHARE_BUTTON);\n this.formatButtonEl = exampleEl.querySelector(PlayExampleClassName.FORMAT_BUTTON);\n this.runButtonEl = exampleEl.querySelector(PlayExampleClassName.RUN_BUTTON);\n this.inputEl = this.makeTextArea(exampleEl.querySelector(PlayExampleClassName.EXAMPLE_INPUT));\n this.outputEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_OUTPUT);\n this.outputContainerEl = exampleEl.querySelector(PlayExampleClassName.EXAMPLE_OUTPUT_CONTAINER);\n\n // This is legacy listener to be replaced the listener for shareButtonEl.\n this.playButtonEl?.addEventListener('click', () => this.handleShareButtonClick());\n this.shareButtonEl?.addEventListener('click', () => this.handleShareButtonClick());\n this.formatButtonEl?.addEventListener('click', () => this.handleFormatButtonClick());\n this.runButtonEl?.addEventListener('click', () => this.handleRunButtonClick());\n\n if (!this.inputEl) return;\n\n this.resize();\n this.inputEl.addEventListener('keyup', () => this.resize());\n this.inputEl.addEventListener('keydown', e => this.onKeydown(e));\n }\n\n /**\n * Replace the pre element with a textarea. The examples are initially rendered\n * as pre elements so they're fully visible when JS is disabled.\n */\n makeTextArea(el: Element | null): HTMLTextAreaElement {\n const t = document.createElement('textarea');\n t.classList.add('Documentation-exampleCode', 'code');\n t.spellcheck = false;\n t.value = el?.textContent ?? '';\n el?.parentElement?.replaceChild(t, el);\n return t;\n }\n\n /**\n * Retrieve the hash value of the anchor element.\n */\n getAnchorHash(): string | undefined {\n return this.anchorEl?.hash;\n }\n\n /**\n * Expands the current playground example.\n */\n expand(): void {\n this.exampleEl.open = true;\n }\n\n /**\n * Resizes the input element to accommodate the amount of text present.\n */\n private resize(): void {\n if (this.inputEl?.value) {\n const numLineBreaks = (this.inputEl.value.match(/\\n/g) || []).length;\n // min-height + lines x line-height + padding + border\n this.inputEl.style.height = `${(20 + numLineBreaks * 20 + 12 + 2) / 16}rem`;\n }\n }\n\n /**\n * Handler to override keyboard behavior in the playground's\n * textarea element.\n *\n * Tab key inserts tabs into the example playground instead of\n * switching to the next interactive element.\n * @param e input element keyboard event.\n */\n private onKeydown(e: KeyboardEvent) {\n if (e.key === 'Tab') {\n document.execCommand('insertText', false, '\\t');\n e.preventDefault();\n }\n }\n\n /**\n * Changes the text of the example's input box.\n */\n private setInputText(output: string) {\n if (this.inputEl) {\n this.inputEl.value = output;\n }\n }\n\n /**\n * Changes the text of the example's output box.\n */\n private setOutputText(output: string) {\n if (this.outputEl) {\n this.outputEl.textContent = output;\n }\n if (this.outputContainerEl) {\n this.outputContainerEl.hidden = false;\n }\n }\n\n /**\n * Appends to the text of the example's output box.\n */\n private appendToOutputText(output: string) {\n if (this.outputEl) {\n this.outputEl.textContent += output;\n }\n if (this.outputContainerEl) {\n this.outputContainerEl.hidden = false;\n }\n }\n\n private setOutputHTML(output: string) {\n if (this.outputEl) {\n this.outputEl.innerHTML = output;\n }\n }\n\n /**\n * Sets the error message text and overwrites\n * output box to indicate a failed response.\n */\n private setErrorText(err: string) {\n if (this.errorEl) {\n this.errorEl.textContent = err;\n }\n this.setOutputText('An error has occurred\u2026');\n }\n\n private getCodeWithModFile(): string {\n let codeWithModFile = this.inputEl?.value ?? '';\n const moduleVars = document.querySelector('.js-playgroundVars')?.dataset ?? {};\n if (moduleVars.modulepath !== 'std') {\n codeWithModFile = codeWithModFile.concat(`\n-- go.mod --\nmodule play.ground\n\nrequire ${moduleVars.modulepath} ${moduleVars.version}\n`);\n }\n\n return codeWithModFile;\n }\n\n /**\n * Opens a new window to play.golang.org using the\n * example snippet's code in the playground.\n */\n private handleShareButtonClick() {\n const PLAYGROUND_BASE_URL = 'https://play.golang.org/p/';\n\n this.setOutputText('Waiting for remote server\u2026');\n\n fetch(abs('/play/share'), {\n method: 'POST',\n body: this.getCodeWithModFile(),\n })\n .then(res => res.text())\n .then(shareId => {\n const href = PLAYGROUND_BASE_URL + shareId;\n this.setOutputHTML(`${href}`);\n window.open(href);\n })\n .catch(err => {\n this.setErrorText(err);\n });\n }\n\n /**\n * Runs gofmt on the example snippet in the playground.\n */\n private handleFormatButtonClick() {\n this.setOutputText('Waiting for remote server\u2026');\n const body = new FormData();\n body.append('body', this.inputEl?.value ?? '');\n\n fetch(abs('/play/fmt'), {\n method: 'POST',\n body: body,\n })\n .then(res => res.json())\n .then(({ Body, Error }) => {\n this.setOutputText(Error || 'Done.');\n if (Body) {\n this.setInputText(Body);\n this.resize();\n }\n })\n .catch(err => {\n this.setErrorText(err);\n });\n }\n\n /**\n * Runs the code snippet in the example playground.\n */\n private handleRunButtonClick() {\n this.setOutputText('Waiting for remote server\u2026');\n\n fetch(abs('/play/compile'), {\n method: 'POST',\n body: JSON.stringify({ body: this.getCodeWithModFile(), version: 2 }),\n })\n .then(res => res.json())\n .then(async ({ Events, Errors }) => {\n this.setOutputText(Errors || '');\n for (const e of Events || []) {\n this.appendToOutputText(e.Message);\n await new Promise(resolve => setTimeout(resolve, e.Delay / 1000000));\n }\n })\n .catch(err => {\n this.setErrorText(err);\n });\n }\n}\n\nexport function initPlaygrounds(): void {\n const exampleHashRegex = location.hash.match(/^#(example-.*)$/);\n if (exampleHashRegex) {\n const exampleHashEl = document.getElementById(exampleHashRegex[1]) as HTMLDetailsElement;\n if (exampleHashEl) {\n exampleHashEl.open = true;\n }\n }\n\n // We use a spread operator to convert a nodelist into an array of elements.\n const exampleHrefs = [\n ...document.querySelectorAll(PlayExampleClassName.PLAY_HREF),\n ];\n\n /**\n * Sometimes exampleHrefs and playContainers are in different order, so we\n * find an exampleHref from a common hash.\n * @param playContainer - playground container\n */\n const findExampleHash = (playContainer: PlaygroundExampleController) =>\n exampleHrefs.find(ex => {\n return ex.hash === playContainer.getAnchorHash();\n });\n\n for (const el of document.querySelectorAll(PlayExampleClassName.PLAY_CONTAINER)) {\n // There should be the same amount of hrefs referencing examples as example containers.\n const playContainer = new PlaygroundExampleController(el as HTMLDetailsElement);\n const exampleHref = findExampleHash(playContainer);\n if (exampleHref) {\n exampleHref.addEventListener('click', () => {\n playContainer.expand();\n });\n } else {\n console.warn('example href not found');\n }\n }\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { TreeNavController } from './tree.js';\n\nexport class SelectNavController {\n constructor(private el: Element) {\n this.el.addEventListener('change', e => {\n const target = e.target as HTMLSelectElement;\n let href = target.value;\n if (!target.value.startsWith('/')) {\n href = '/' + href;\n }\n window.location.href = href;\n });\n }\n}\n\nexport function makeSelectNav(tree: TreeNavController): HTMLLabelElement {\n const label = document.createElement('label');\n label.classList.add('go-Label');\n label.setAttribute('aria-label', 'Menu');\n const select = document.createElement('select');\n select.classList.add('go-Select', 'js-selectNav');\n label.appendChild(select);\n const outline = document.createElement('optgroup');\n outline.label = 'Outline';\n select.appendChild(outline);\n const groupMap: Record = {};\n let group: HTMLOptGroupElement;\n for (const t of tree.treeitems) {\n if (Number(t.depth) > 4) continue;\n if (t.groupTreeitem) {\n group = groupMap[t.groupTreeitem.label];\n if (!group) {\n group = groupMap[t.groupTreeitem.label] = document.createElement('optgroup');\n group.label = t.groupTreeitem.label;\n select.appendChild(group);\n }\n } else {\n group = outline;\n }\n const o = document.createElement('option');\n o.label = t.label;\n o.textContent = t.label;\n o.value = (t.el as HTMLAnchorElement).href.replace(window.location.origin, '').replace('/', '');\n group.appendChild(o);\n }\n tree.addObserver(t => {\n const hash = (t.el as HTMLAnchorElement).hash;\n const value = select.querySelector(`[value$=\"${hash}\"]`)?.value;\n if (value) {\n select.value = value;\n }\n }, 50);\n return label;\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * TreeNavController is the navigation tree component of the documentation page.\n * It adds accessiblity attributes to a tree, observes the heading elements\n * focus the topmost link for headings visible on the page, and implements the\n * WAI-ARIA Treeview Design Pattern with full\n * [keyboard support](https://www.w3.org/TR/wai-aria-practices/examples/treeview/treeview-2/treeview-2a.html#kbd_label).\n */\nexport class TreeNavController {\n treeitems: TreeItem[];\n\n /**\n * firstChars is the first character of each treeitem in the same order\n * as this.treeitems. We use this array to set focus by character when\n * navigating the tree with a keyboard.\n */\n private firstChars: string[];\n private firstTreeitem: TreeItem | null;\n private lastTreeitem: TreeItem | null;\n private observerCallbacks: ((t: TreeItem) => void)[];\n\n constructor(private el: HTMLElement) {\n this.treeitems = [];\n this.firstChars = [];\n this.firstTreeitem = null;\n this.lastTreeitem = null;\n this.observerCallbacks = [];\n this.init();\n }\n\n private init(): void {\n this.handleResize();\n window.addEventListener('resize', this.handleResize);\n this.findTreeItems();\n this.updateVisibleTreeitems();\n this.observeTargets();\n if (this.firstTreeitem) {\n this.firstTreeitem.el.tabIndex = 0;\n }\n }\n\n private handleResize = (): void => {\n this.el.style.setProperty('--js-tree-height', '100vh');\n this.el.style.setProperty('--js-tree-height', this.el.clientHeight + 'px');\n };\n\n private observeTargets() {\n this.addObserver(treeitem => {\n this.expandTreeitem(treeitem);\n this.setSelected(treeitem);\n // TODO: Fix scroll issue in https://golang.org/issue/47450.\n // treeitem.el.scrollIntoView({ block: 'nearest' });\n });\n\n const targets = new Map();\n const observer = new IntersectionObserver(\n entries => {\n for (const entry of entries) {\n targets.set(entry.target.id, entry.isIntersecting || entry.intersectionRatio === 1);\n }\n for (const [id, isIntersecting] of targets) {\n if (isIntersecting) {\n const active = this.treeitems.find(t =>\n (t.el as HTMLAnchorElement)?.href.endsWith(`#${id}`)\n );\n if (active) {\n for (const fn of this.observerCallbacks) {\n fn(active);\n }\n }\n break;\n }\n }\n },\n {\n threshold: 1.0,\n rootMargin: '-60px 0px 0px 0px',\n }\n );\n\n for (const href of this.treeitems.map(t => t.el.getAttribute('href'))) {\n if (href) {\n const id = href.replace(window.location.origin, '').replace('/', '').replace('#', '');\n const target = document.getElementById(id);\n if (target && !id.startsWith('example-')) {\n observer.observe(target);\n }\n }\n }\n }\n\n addObserver(fn: (t: TreeItem) => void, delay = 200): void {\n this.observerCallbacks.push(debounce(fn, delay));\n }\n\n setFocusToNextItem(currentItem: TreeItem): void {\n let nextItem = null;\n for (let i = currentItem.index + 1; i < this.treeitems.length; i++) {\n const ti = this.treeitems[i];\n if (ti.isVisible) {\n nextItem = ti;\n break;\n }\n }\n if (nextItem) {\n this.setFocusToItem(nextItem);\n }\n }\n\n setFocusToPreviousItem(currentItem: TreeItem): void {\n let prevItem = null;\n for (let i = currentItem.index - 1; i > -1; i--) {\n const ti = this.treeitems[i];\n if (ti.isVisible) {\n prevItem = ti;\n break;\n }\n }\n if (prevItem) {\n this.setFocusToItem(prevItem);\n }\n }\n\n setFocusToParentItem(currentItem: TreeItem): void {\n if (currentItem.groupTreeitem) {\n this.setFocusToItem(currentItem.groupTreeitem);\n }\n }\n\n setFocusToFirstItem(): void {\n this.firstTreeitem && this.setFocusToItem(this.firstTreeitem);\n }\n\n setFocusToLastItem(): void {\n this.lastTreeitem && this.setFocusToItem(this.lastTreeitem);\n }\n\n setSelected(currentItem: TreeItem): void {\n for (const l1 of this.el.querySelectorAll('[aria-expanded=\"true\"]')) {\n if (l1 === currentItem.el) continue;\n if (!l1.nextElementSibling?.contains(currentItem.el)) {\n l1.setAttribute('aria-expanded', 'false');\n }\n }\n for (const l1 of this.el.querySelectorAll('[aria-selected]')) {\n if (l1 !== currentItem.el) {\n l1.setAttribute('aria-selected', 'false');\n }\n }\n currentItem.el.setAttribute('aria-selected', 'true');\n this.updateVisibleTreeitems();\n this.setFocusToItem(currentItem, false);\n }\n\n expandTreeitem(treeitem: TreeItem): void {\n let currentItem: TreeItem | null = treeitem;\n while (currentItem) {\n if (currentItem.isExpandable) {\n currentItem.el.setAttribute('aria-expanded', 'true');\n }\n currentItem = currentItem.groupTreeitem;\n }\n this.updateVisibleTreeitems();\n }\n\n expandAllSiblingItems(currentItem: TreeItem): void {\n for (const ti of this.treeitems) {\n if (ti.groupTreeitem === currentItem.groupTreeitem && ti.isExpandable) {\n this.expandTreeitem(ti);\n }\n }\n }\n\n collapseTreeitem(currentItem: TreeItem): void {\n let groupTreeitem = null;\n\n if (currentItem.isExpanded()) {\n groupTreeitem = currentItem;\n } else {\n groupTreeitem = currentItem.groupTreeitem;\n }\n\n if (groupTreeitem) {\n groupTreeitem.el.setAttribute('aria-expanded', 'false');\n this.updateVisibleTreeitems();\n this.setFocusToItem(groupTreeitem);\n }\n }\n\n setFocusByFirstCharacter(currentItem: TreeItem, char: string): void {\n let start: number, index: number;\n char = char.toLowerCase();\n\n // Get start index for search based on position of currentItem\n start = currentItem.index + 1;\n if (start === this.treeitems.length) {\n start = 0;\n }\n\n // Check remaining slots in the menu\n index = this.getIndexFirstChars(start, char);\n\n // If not found in remaining slots, check from beginning\n if (index === -1) {\n index = this.getIndexFirstChars(0, char);\n }\n\n // If match was found...\n if (index > -1) {\n this.setFocusToItem(this.treeitems[index]);\n }\n }\n\n private findTreeItems() {\n const findItems = (el: HTMLElement, group: TreeItem | null) => {\n let ti = group;\n let curr = el.firstElementChild as HTMLElement;\n while (curr) {\n if (curr.tagName === 'A' || curr.tagName === 'SPAN') {\n ti = new TreeItem(curr, this, group);\n this.treeitems.push(ti);\n this.firstChars.push(ti.label.substring(0, 1).toLowerCase());\n }\n if (curr.firstElementChild) {\n findItems(curr, ti);\n }\n curr = curr.nextElementSibling as HTMLElement;\n }\n };\n findItems(this.el as HTMLElement, null);\n this.treeitems.map((ti, idx) => (ti.index = idx));\n }\n\n private updateVisibleTreeitems(): void {\n this.firstTreeitem = this.treeitems[0];\n\n for (const ti of this.treeitems) {\n let parent = ti.groupTreeitem;\n ti.isVisible = true;\n while (parent && parent.el !== this.el) {\n if (!parent.isExpanded()) {\n ti.isVisible = false;\n }\n parent = parent.groupTreeitem;\n }\n if (ti.isVisible) {\n this.lastTreeitem = ti;\n }\n }\n }\n\n private setFocusToItem(treeitem: TreeItem, focusEl = true) {\n treeitem.el.tabIndex = 0;\n if (focusEl) {\n treeitem.el.focus();\n }\n for (const ti of this.treeitems) {\n if (ti !== treeitem) {\n ti.el.tabIndex = -1;\n }\n }\n }\n\n private getIndexFirstChars(startIndex: number, char: string): number {\n for (let i = startIndex; i < this.firstChars.length; i++) {\n if (this.treeitems[i].isVisible && char === this.firstChars[i]) {\n return i;\n }\n }\n return -1;\n }\n}\n\nclass TreeItem {\n el: HTMLElement;\n groupTreeitem: TreeItem | null;\n label: string;\n isExpandable: boolean;\n isVisible: boolean;\n depth: number;\n index: number;\n\n private tree: TreeNavController;\n private isInGroup: boolean;\n\n constructor(el: HTMLElement, treeObj: TreeNavController, group: TreeItem | null) {\n el.tabIndex = -1;\n this.el = el;\n this.groupTreeitem = group;\n this.label = el.textContent?.trim() ?? '';\n this.tree = treeObj;\n this.depth = (group?.depth || 0) + 1;\n this.index = 0;\n\n const parent = el.parentElement;\n if (parent?.tagName.toLowerCase() === 'li') {\n parent?.setAttribute('role', 'none');\n }\n el.setAttribute('aria-level', this.depth + '');\n if (el.getAttribute('aria-label')) {\n this.label = el?.getAttribute('aria-label')?.trim() ?? '';\n }\n\n this.isExpandable = false;\n this.isVisible = false;\n this.isInGroup = !!group;\n\n let curr = el.nextElementSibling;\n while (curr) {\n if (curr.tagName.toLowerCase() == 'ul') {\n const groupId = `${group?.label ?? ''} nav group ${this.label}`.replace(/[\\W_]+/g, '_');\n el.setAttribute('aria-owns', groupId);\n el.setAttribute('aria-expanded', 'false');\n curr.setAttribute('role', 'group');\n curr.setAttribute('id', groupId);\n this.isExpandable = true;\n break;\n }\n\n curr = curr.nextElementSibling;\n }\n this.init();\n }\n\n private init() {\n this.el.tabIndex = -1;\n if (!this.el.getAttribute('role')) {\n this.el.setAttribute('role', 'treeitem');\n }\n this.el.addEventListener('keydown', this.handleKeydown.bind(this));\n this.el.addEventListener('click', this.handleClick.bind(this));\n this.el.addEventListener('focus', this.handleFocus.bind(this));\n this.el.addEventListener('blur', this.handleBlur.bind(this));\n }\n\n isExpanded() {\n if (this.isExpandable) {\n return this.el.getAttribute('aria-expanded') === 'true';\n }\n\n return false;\n }\n\n isSelected() {\n return this.el.getAttribute('aria-selected') === 'true';\n }\n\n private handleClick(event: MouseEvent) {\n // only process click events that directly happened on this treeitem\n if (event.target !== this.el && event.target !== this.el.firstElementChild) {\n return;\n }\n if (this.isExpandable) {\n if (this.isExpanded() && this.isSelected()) {\n this.tree.collapseTreeitem(this);\n } else {\n this.tree.expandTreeitem(this);\n }\n event.stopPropagation();\n }\n this.tree.setSelected(this);\n }\n\n private handleFocus() {\n let el = this.el;\n if (this.isExpandable) {\n el = (el.firstElementChild as HTMLElement) ?? el;\n }\n el.classList.add('focus');\n }\n\n private handleBlur() {\n let el = this.el;\n if (this.isExpandable) {\n el = (el.firstElementChild as HTMLElement) ?? el;\n }\n el.classList.remove('focus');\n }\n\n private handleKeydown(event: KeyboardEvent) {\n if (event.altKey || event.ctrlKey || event.metaKey) {\n return;\n }\n\n let captured = false;\n switch (event.key) {\n case ' ':\n case 'Enter':\n if (this.isExpandable) {\n if (this.isExpanded() && this.isSelected()) {\n this.tree.collapseTreeitem(this);\n } else {\n this.tree.expandTreeitem(this);\n }\n captured = true;\n } else {\n event.stopPropagation();\n }\n this.tree.setSelected(this);\n break;\n\n case 'ArrowUp':\n this.tree.setFocusToPreviousItem(this);\n captured = true;\n break;\n\n case 'ArrowDown':\n this.tree.setFocusToNextItem(this);\n captured = true;\n break;\n\n case 'ArrowRight':\n if (this.isExpandable) {\n if (this.isExpanded()) {\n this.tree.setFocusToNextItem(this);\n } else {\n this.tree.expandTreeitem(this);\n }\n }\n captured = true;\n break;\n\n case 'ArrowLeft':\n if (this.isExpandable && this.isExpanded()) {\n this.tree.collapseTreeitem(this);\n captured = true;\n } else {\n if (this.isInGroup) {\n this.tree.setFocusToParentItem(this);\n captured = true;\n }\n }\n break;\n\n case 'Home':\n this.tree.setFocusToFirstItem();\n captured = true;\n break;\n\n case 'End':\n this.tree.setFocusToLastItem();\n captured = true;\n break;\n\n default:\n if (event.key.length === 1 && event.key.match(/\\S/)) {\n if (event.key == '*') {\n this.tree.expandAllSiblingItems(this);\n } else {\n this.tree.setFocusByFirstCharacter(this, event.key);\n }\n captured = true;\n }\n break;\n }\n\n if (captured) {\n event.stopPropagation();\n event.preventDefault();\n }\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction debounce any>(func: T, wait: number) {\n let timeout: ReturnType | null;\n return (...args: Parameters) => {\n const later = () => {\n timeout = null;\n func(...args);\n };\n if (timeout) {\n clearTimeout(timeout);\n }\n timeout = setTimeout(later, wait);\n };\n}\n", "/*!\n * @license\n * Copyright 2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * Controller for a table element with expandable rows. Adds event listeners to\n * a toggle within a table row that controls visiblity of additional related\n * rows in the table.\n *\n * @example\n * ```typescript\n * import {ExpandableRowsTableController} from '/static/js/table';\n *\n * const el = document .querySelector('.js-myTableElement')\n * new ExpandableRowsTableController(el));\n * ```\n */\nexport class ExpandableRowsTableController {\n private rows: HTMLTableRowElement[];\n private toggles: HTMLButtonElement[];\n\n /**\n * Create a table controller.\n * @param table - The table element to which the controller binds.\n */\n constructor(private table: HTMLTableElement, private toggleAll?: HTMLButtonElement | null) {\n this.rows = Array.from(table.querySelectorAll('[data-aria-controls]'));\n this.toggles = Array.from(this.table.querySelectorAll('[aria-expanded]'));\n this.setAttributes();\n this.attachEventListeners();\n this.update();\n }\n\n /**\n * setAttributes sets data-aria-* and data-id attributes to regular\n * html attributes as a workaround for limitations from safehtml.\n */\n private setAttributes() {\n for (const a of ['data-aria-controls', 'data-aria-labelledby', 'data-id']) {\n this.table.querySelectorAll(`[${a}]`).forEach(t => {\n t.setAttribute(a.replace('data-', ''), t.getAttribute(a) ?? '');\n t.removeAttribute(a);\n });\n }\n }\n\n private attachEventListeners() {\n this.rows.forEach(t => {\n t.addEventListener('click', e => {\n this.handleToggleClick(e);\n });\n });\n this.toggleAll?.addEventListener('click', () => {\n this.expandAllItems();\n });\n\n document.addEventListener('keydown', e => {\n if ((e.ctrlKey || e.metaKey) && e.key === 'f') {\n this.expandAllItems();\n }\n });\n }\n\n private handleToggleClick(e: MouseEvent) {\n let target = e.currentTarget as HTMLTableRowElement | null;\n if (!target?.hasAttribute('aria-expanded')) {\n target = this.table.querySelector(\n `button[aria-controls=\"${target?.getAttribute('aria-controls')}\"]`\n );\n }\n const isExpanded = target?.getAttribute('aria-expanded') === 'true';\n target?.setAttribute('aria-expanded', isExpanded ? 'false' : 'true');\n e.stopPropagation();\n this.update();\n }\n\n expandAllItems = (): void => {\n this.toggles.map(t => t.setAttribute('aria-expanded', 'true'));\n this.update();\n };\n\n private collapseAllItems = () => {\n this.toggles.map(t => t.setAttribute('aria-expanded', 'false'));\n this.update();\n };\n\n private update = () => {\n this.updateVisibleItems();\n setTimeout(() => this.updateGlobalToggle());\n };\n\n private updateVisibleItems() {\n this.rows.map(t => {\n const isExpanded = t?.getAttribute('aria-expanded') === 'true';\n const rowIds = t?.getAttribute('aria-controls')?.trimEnd().split(' ');\n rowIds?.map(id => {\n const target = document.getElementById(`${id}`);\n if (isExpanded) {\n target?.classList.add('visible');\n target?.classList.remove('hidden');\n } else {\n target?.classList.add('hidden');\n target?.classList.remove('visible');\n }\n });\n });\n }\n\n private updateGlobalToggle() {\n if (!this.toggleAll) return;\n if (this.rows.some(t => t.hasAttribute('aria-expanded'))) {\n this.toggleAll.style.display = 'block';\n }\n const someCollapsed = this.toggles.some(el => el.getAttribute('aria-expanded') === 'false');\n if (someCollapsed) {\n this.toggleAll.innerText = 'Expand all';\n this.toggleAll.onclick = this.expandAllItems;\n this.toggleAll.setAttribute('aria-label', 'Expand all directories');\n this.toggleAll.setAttribute('aria-live', 'polite');\n } else {\n this.toggleAll.innerText = 'Collapse all';\n this.toggleAll.onclick = this.collapseAllItems;\n this.toggleAll.setAttribute('aria-label', 'Collapse all directories');\n this.toggleAll.setAttribute('aria-live', 'polite');\n }\n }\n}\n", "import { initPlaygrounds } from 'static/shared/playground/playground';\nimport { SelectNavController, makeSelectNav } from 'static/shared/outline/select';\nimport { TreeNavController } from 'static/shared/outline/tree';\nimport { ExpandableRowsTableController } from 'static/shared/table/table';\n\ninitPlaygrounds();\n\nconst directories = document.querySelector('.js-expandableTable');\nif (directories) {\n const table = new ExpandableRowsTableController(\n directories,\n document.querySelector('.js-expandAllDirectories')\n );\n // Expand directories on page load with expand-directories query param.\n if (window.location.search.includes('expand-directories')) {\n table.expandAllItems();\n }\n\n const internalToggle = document.querySelector('.js-showInternalDirectories');\n if (internalToggle) {\n if (document.querySelector('.UnitDirectories-internal')) {\n internalToggle.style.display = 'block';\n internalToggle.setAttribute('aria-label', 'Show Internal Directories');\n internalToggle.setAttribute('aria-describedby', 'showInternal-description');\n }\n internalToggle.addEventListener('click', () => {\n if (directories.classList.contains('UnitDirectories-showInternal')) {\n directories.classList.remove('UnitDirectories-showInternal');\n internalToggle.innerText = 'Show internal';\n internalToggle.setAttribute('aria-label', 'Show Internal Directories');\n internalToggle.setAttribute('aria-live', 'polite');\n internalToggle.setAttribute('aria-describedby', 'showInternal-description');\n } else {\n directories.classList.add('UnitDirectories-showInternal');\n internalToggle.innerText = 'Hide internal';\n internalToggle.setAttribute('aria-label', 'Hide Internal Directories');\n internalToggle.setAttribute('aria-live', 'polite');\n internalToggle.setAttribute('aria-describedby', 'hideInternal-description');\n }\n });\n }\n if (document.querySelector('html[data-local=\"true\"]')) {\n internalToggle?.click();\n }\n}\n\nconst treeEl = document.querySelector('.js-tree');\nif (treeEl) {\n const treeCtrl = new TreeNavController(treeEl);\n const select = makeSelectNav(treeCtrl);\n const mobileNav = document.querySelector('.js-mainNavMobile');\n if (mobileNav && mobileNav.firstElementChild) {\n mobileNav?.replaceChild(select, mobileNav.firstElementChild);\n }\n if (select.firstElementChild) {\n new SelectNavController(select.firstElementChild);\n }\n}\n\n/**\n * Event handlers for expanding and collapsing the readme section.\n */\nconst readme = document.querySelector('.js-readme');\nconst readmeContent = document.querySelector('.js-readmeContent');\nconst readmeOutline = document.querySelector('.js-readmeOutline');\nconst readmeExpand = document.querySelectorAll('.js-readmeExpand');\nconst readmeCollapse = document.querySelector('.js-readmeCollapse');\nconst mobileNavSelect = document.querySelector('.DocNavMobile-select');\nif (readme && readmeContent && readmeOutline && readmeExpand.length && readmeCollapse) {\n if (readme.clientHeight > 320) {\n readme?.classList.remove('UnitReadme--expanded');\n readme?.classList.add('UnitReadme--toggle');\n }\n if (window.location.hash.includes('readme')) {\n expandReadme();\n }\n mobileNavSelect?.addEventListener('change', e => {\n if ((e.target as HTMLSelectElement).value.startsWith('readme-')) {\n expandReadme();\n }\n });\n readmeExpand.forEach(el =>\n el.addEventListener('click', e => {\n e.preventDefault();\n expandReadme();\n readme.scrollIntoView();\n })\n );\n readmeCollapse.addEventListener('click', e => {\n e.preventDefault();\n readme.classList.remove('UnitReadme--expanded');\n if (readmeExpand[1]) {\n readmeExpand[1].scrollIntoView({ block: 'center' });\n }\n });\n readmeContent.addEventListener('keyup', () => {\n expandReadme();\n });\n readmeContent.addEventListener('click', () => {\n expandReadme();\n });\n readmeOutline.addEventListener('click', () => {\n expandReadme();\n });\n document.addEventListener('keydown', e => {\n if ((e.ctrlKey || e.metaKey) && e.key === 'f') {\n expandReadme();\n }\n });\n}\n\n/**\n * expandReadme expands the readme and adds the section-readme hash to the\n * URL so it stays expanded when navigating back from an external link.\n */\nfunction expandReadme() {\n history.replaceState(null, '', `${location.pathname}${location.search}#section-readme`);\n readme?.classList.add('UnitReadme--expanded');\n}\n\n/**\n * Expand details items that are focused. This will expand\n * deprecated symbols when they are navigated to from the index\n * or a direct link.\n */\nfunction openDeprecatedSymbol() {\n if (!location.hash) return;\n const heading = document.getElementById(location.hash.slice(1));\n const grandParent = heading?.parentElement?.parentElement as HTMLDetailsElement | null;\n if (grandParent?.nodeName === 'DETAILS') {\n grandParent.open = true;\n }\n}\nopenDeprecatedSymbol();\nwindow.addEventListener('hashchange', () => openDeprecatedSymbol());\n\n/**\n * Listen for changes in the build context dropdown.\n */\ndocument.querySelectorAll('.js-buildContextSelect').forEach(el => {\n el.addEventListener('change', e => {\n window.location.search = `?GOOS=${(e.target as HTMLSelectElement).value}`;\n });\n});\n\n/**\n * fork\uFF1Aunexported\uFF08\u79C1\u6709\uFF09\u7B26\u53F7 toggle\u3002\n *\n * \u80CC\u666F\uFF1Apkgsite -show-unexported flag \u8BA9 godoc \u628A\u79C1\u6709 type/func/method\n * \u90FD\u6E32\u5230 page\u3002\u4F46\u8BFB\u8005\u5927\u591A\u6570\u65F6\u5019\u53EA\u5173\u5FC3 public API\uFF0C\u79C1\u6709\u7684\u592A\u591A\u53CD\u800C\u62D6\u6162\n * \u9605\u8BFB\u3002\u8FD9\u5C42\u5728 client \u7AEF\u6309 id \u9996\u5B57\u6BCD\u5927\u5C0F\u5199\u81EA\u52A8 hide \u79C1\u6709 declaration +\n * index \u94FE\u63A5\uFF0C\u518D\u6CE8\u5165\u4E00\u4E2A toggle button \u4E00\u952E\u5207\u663E\u793A\u3002\u72B6\u6001\u7528 localStorage\n * \u8BB0\u4E0B\uFF0C\u8DE8\u9875\u4FDD\u7559\u3002\n */\n(() => {\n if (!document.querySelector('h4[data-kind]')) return; // \u975E godoc \u8BE6\u60C5\u9875\n\n // method id \u5F62\u5982 \"Type.method\"\uFF0C\u53D6\u6700\u540E\u6BB5\u5224\u79C1\u6709\uFF1B\u5176\u4ED6\u76F4\u63A5\u5224 id \u672C\u8EAB\u3002\n // \u6392\u9664 pkg-overview / section-readme / pkg-index \u8FD9\u7C7B\u9875\u9762 anchor\u2014\u2014\u5B83\u4EEC\u662F\n // \u5C0F\u5199\u8D77\u5934\u4F46\u4E0D\u662F Go \u7B26\u53F7\uFF0C\u626B\u5230\u4F1A\u8BEF\u6807\u9690\u85CF\u3002\n const isUnexported = (id: string): boolean => {\n if (id.startsWith('pkg-') || id.startsWith('section-') || id.startsWith('hdr-')) {\n return false;\n }\n const last = id.split('.').pop() ?? id;\n return /^[a-z]/.test(last);\n };\n\n // \u6807 declaration wrapper\uFF1Afunc \u5305\u5728 .Documentation-function\uFF1Btype / method\n // \u5305\u5728 .Documentation-type / .Documentation-typeFunc / .Documentation-typeMethod\u3002\n document.querySelectorAll('h4[data-kind][id]').forEach(h => {\n if (!isUnexported(h.id)) return;\n const wrapper = h.closest(\n '.Documentation-function, .Documentation-type, .Documentation-typeFunc, .Documentation-typeMethod'\n );\n wrapper?.classList.add('Documentation-unexported');\n });\n\n // index \u5217\u8868\u9879 + \u5DE6\u4FA7\u8FB9\u680F\uFF08go-Tree outline\uFF09\u6309\u94FE\u63A5\u9996\u5B57\u6BCD\u5224\u3002\n // \u4FA7\u8FB9\u680F\u94FE\u63A5 selector \u76F4\u63A5\u5339\u914D\u6240\u6709 .go-Tree a[href^=\"#\"]\u2014\u2014pkg-overview /\n // section-readme \u7B49\u5BFC\u822A anchor \u5DF2\u88AB isUnexported \u5934\u90E8\u6392\u9664\uFF0C\u4E0D\u4F1A\u8BEF\u6807\u3002\n document\n .querySelectorAll(\n '.Documentation-indexFunction a[href^=\"#\"], ' +\n '.Documentation-indexType a[href^=\"#\"], ' +\n '.Documentation-indexTypeFunctions a[href^=\"#\"], ' +\n '.Documentation-indexTypeMethods a[href^=\"#\"], ' +\n '.go-Tree a[href^=\"#\"]'\n )\n .forEach(a => {\n if (isUnexported(a.getAttribute('href')!.slice(1))) {\n a.closest('li')?.classList.add('Documentation-unexported');\n }\n });\n\n // \u6CE8\u5165 CSS\uFF08\u4E0D\u52A8 main.css build pipeline\uFF0C\u907F\u514D\u589E\u91CF\u6539 esbuild \u8F93\u51FA\uFF09\n const style = document.createElement('style');\n style.textContent =\n 'body:not(.show-unexported) .Documentation-unexported{display:none}';\n document.head.appendChild(style);\n\n // \u6CE8\u5165 toggle button\u2014\u2014\u653E Index \u6807\u9898\u65C1\u8FB9\u6700\u663E\u773C\uFF0C\u8DDF \"Show internal\" \u540C\u4F4D\n const indexHeader = document.querySelector('#pkg-index');\n if (!indexHeader) return;\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'go-Button go-Button--inline';\n btn.style.marginLeft = '0.75rem';\n btn.style.fontSize = '0.875rem';\n btn.style.verticalAlign = 'middle';\n\n const STORE_KEY = 'pkgsitex:showUnexported';\n const apply = (show: boolean) => {\n document.body.classList.toggle('show-unexported', show);\n btn.textContent = show ? 'Hide unexported' : 'Show unexported';\n try {\n localStorage.setItem(STORE_KEY, show ? '1' : '0');\n } catch {\n /* localStorage \u4E0D\u53EF\u7528\uFF08\u9690\u79C1\u6A21\u5F0F / \u6587\u4EF6\u534F\u8BAE\uFF09\u65F6\u5FFD\u7565\uFF0C\u53EA\u4E22\u5931\u8DE8\u9875\u8BB0\u5FC6 */\n }\n };\n\n apply(localStorage.getItem(STORE_KEY) === '1');\n btn.addEventListener('click', () =>\n apply(!document.body.classList.contains('show-unexported'))\n );\n indexHeader.appendChild(btn);\n})();\n"], "mappings": "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", "names": ["getBasePath", "_a", "abs", "p", "PlayExampleClassName", "PlaygroundExampleController", "exampleEl", "_a", "_b", "_c", "_d", "e", "el", "numLineBreaks", "output", "err", "codeWithModFile", "moduleVars", "PLAYGROUND_BASE_URL", "abs", "res", "shareId", "href", "body", "Body", "Error", "Events", "Errors", "resolve", "initPlaygrounds", "exampleHashRegex", "exampleHashEl", "exampleHrefs", "findExampleHash", "playContainer", "ex", "exampleHref", "SelectNavController", "el", "e", "target", "href", "makeSelectNav", "tree", "label", "select", "outline", "groupMap", "group", "t", "o", "_a", "hash", "value", "TreeNavController", "el", "treeitem", "targets", "observer", "entries", "entry", "id", "isIntersecting", "active", "t", "_a", "fn", "href", "target", "delay", "debounce", "currentItem", "nextItem", "ti", "prevItem", "l1", "groupTreeitem", "char", "start", "index", "findItems", "group", "curr", "TreeItem", "idx", "parent", "focusEl", "startIndex", "treeObj", "_b", "_c", "_d", "_e", "groupId", "event", "captured", "func", "wait", "timeout", "args", "later", "ExpandableRowsTableController", "table", "toggleAll", "t", "a", "_a", "e", "target", "isExpanded", "rowIds", "id", "el", "initPlaygrounds", "directories", "table", "ExpandableRowsTableController", "internalToggle", "treeEl", "treeCtrl", "TreeNavController", "select", "makeSelectNav", "mobileNav", "SelectNavController", "readme", "readmeContent", "readmeOutline", "readmeExpand", "readmeCollapse", "mobileNavSelect", "expandReadme", "e", "el", "openDeprecatedSymbol", "_a", "heading", "grandParent", "isUnexported", "id", "last", "h", "wrapper", "a", "style", "indexHeader", "btn", "STORE_KEY", "apply", "show"] } diff --git a/static/frontend/unit/main/main.ts b/static/frontend/unit/main/main.ts index 1c612d45e..a86f381d9 100644 --- a/static/frontend/unit/main/main.ts +++ b/static/frontend/unit/main/main.ts @@ -209,7 +209,7 @@ document.querySelectorAll('.js-buildContextSelect').forEach(el => { btn.style.fontSize = '0.875rem'; btn.style.verticalAlign = 'middle'; - const STORE_KEY = 'gogodocs:showUnexported'; + const STORE_KEY = 'pkgsitex:showUnexported'; const apply = (show: boolean) => { document.body.classList.toggle('show-unexported', show); btn.textContent = show ? 'Hide unexported' : 'Show unexported'; diff --git a/static/shared/base-path/base-path.ts b/static/shared/base-path/base-path.ts index 8c2bec412..bca6aa3bc 100644 --- a/static/shared/base-path/base-path.ts +++ b/static/shared/base-path/base-path.ts @@ -8,8 +8,8 @@ /** * 站点 URL 子路径前缀,从 读出。 * - * fork 用 -base-path=/gogodocs 启动时,server 端模板把 attribute 写成 - * "/gogodocs";上游公网 pkg.go.dev 默认挂根,attribute 为空字符串。 + * fork 用 -base-path=/pkgsitex 启动时,server 端模板把 attribute 写成 + * "/pkgsitex";上游公网 pkg.go.dev 默认挂根,attribute 为空字符串。 * * 在 之外的脚本初始化阶段调用都安全——documentElement 一定存在。 */ @@ -24,8 +24,8 @@ export function getBasePath(): string { * 的地方。非绝对路径(不以 / 开头)原样返回——caller 自己保证语义。 * * 例: - * abs('/play/share') // → '/gogodocs/play/share' 或 '/play/share' - * abs('/static/foo.svg') // → '/gogodocs/static/foo.svg' 或 '/static/foo.svg' + * abs('/play/share') // → '/pkgsitex/play/share' 或 '/play/share' + * abs('/static/foo.svg') // → '/pkgsitex/static/foo.svg' 或 '/static/foo.svg' * abs('relative/x') // → 'relative/x'(不动) */ export function abs(p: string): string { From df57ec271ddae452932b19462b1fbd305d8dbe78 Mon Sep 17 00:00:00 2001 From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com> Date: Thu, 7 May 2026 22:19:16 +0800 Subject: [PATCH 14/26] =?UTF-8?q?feat(C):=20=E5=8A=A0=20compose.yaml=20+?= =?UTF-8?q?=20=E9=87=8D=E5=86=99=20README=20=E8=AE=A9=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E9=9A=8F=E5=B7=A5=E5=85=B7=E8=B5=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 跟 Chat 端 PR #91 close 配套:fork own 启动配置,业务仓库不再放 docker- compose / deploy/gogodocs README——这些应该跟工具仓库走,新接入仓库 (UniAuth / open-platform / ...)不需要各自维护一份 docker-compose。 - compose.yaml:multi-repo mount 默认覆盖 Chat / Doubao-Speech-Service / UniAuth/uniauth-gf / UniAuth/ittools_sync / open-platform 5 个内部 module。开发者 \`cd ~/Repo/pkgsitex && docker compose up -d\` 即跑通; 增 / 减仓库改 volumes + command 末尾即可 - README.md:完全重写从 fork 视角描述——快速启动(本地 binary 推荐 + docker 备选)、配置选项、增减仓库、浏览体验表(含 fork 独有的 Show unexported toggle / mermaid 渲染 / view source 等)、patch 集 说明、prod 部署 TODO、维护策略(master = upstream / fork/main = long-lived patch)、故障排查。末尾保留上游 README 引用块和 license Chat PR #91 已 close(commit 提到原因:配置随工具走);Chat docker- compose.dev.yml 这边对应 revert(撤回 gogodocs service + 删 deploy/gogodocs/ 目录)单独提一个 follow-up PR。 --- README.md | 180 +++++++++++++++++++++++++++++++++++++-------------- compose.yaml | 53 +++++++++++++++ 2 files changed, 183 insertions(+), 50 deletions(-) create mode 100644 compose.yaml diff --git a/README.md b/README.md index cfbd5618e..30dada286 100644 --- a/README.md +++ b/README.md @@ -1,78 +1,158 @@ -# golang.org/x/pkgsite +# pkgsitex -This repository hosts the source code of the [pkg.go.dev](https://pkg.go.dev) website, -and [`pkgsite`](https://pkg.go.dev/golang.org/x/pkgsite/cmd/pkgsite), a documentation -server program. +[`golang/pkgsite`](https://github.com/golang/pkgsite) 的 fork,加了内网部署 / 私有仓库浏览常用能力——`-base-path` 子路径、未导出符号显示 + 浏览器 toggle、godoc 注释 markdown 扩展(` `code` ` / `**bold**` / mermaid 围栏)、view source 走本地 file mux 等。 -[![Go Reference](https://pkg.go.dev/badge/golang.org/x/pkgsite.svg)](https://pkg.go.dev/golang.org/x/pkgsite) +> **Fork 维护策略**:master 永远 = upstream master,patches 落在 long-lived branch [`fork/main`](https://github.com/NickWilde18/pkgsitex/tree/fork/main)。GitHub "Sync fork" 永远 fast-forward 0 conflict。所有用法 / 启动 / patch 详见 fork/main 分支的 README。 -## pkg.go.dev: a site for discovering Go packages +```sh +git clone https://github.com/NickWilde18/pkgsitex.git ~/Repo/pkgsitex +cd ~/Repo/pkgsitex +git checkout fork/main # 切到 fork patches 分支 +cat README.md # ← 你看到的 fork 说明 +``` -Pkg.go.dev is a website for discovering and evaluating Go packages and modules. +## 快速启动(本地,秒级) -You can check it out at [https://pkg.go.dev](https://pkg.go.dev). +前置:Go 1.24+,本地把要浏览的 repo clone 到 `~/Repo/`。 -## pkgsite: a documentation server +```sh +go run ./cmd/pkgsite -base-path=/pkgsitex -show-unexported -http=:8089 \ + ~/Repo/Chat \ + ~/Repo/Doubao-Speech-Service \ + ~/Repo/UniAuth/uniauth-gf \ + ~/Repo/UniAuth/ittools_sync \ + ~/Repo/open-platform -`pkgsite` program extracts and generates documentation for Go projects. +open http://localhost:8089/pkgsitex/ +``` -Example usage: +或者编译一次本地复用: +```sh +go install ./cmd/pkgsite +pkgsite -base-path=/pkgsitex -show-unexported -http=:8089 ~/Repo/Chat ... ``` -$ go install golang.org/x/pkgsite/cmd/pkgsite@latest -$ cd myproject -$ pkgsite -open . + +首次 `go run` 拉 esbuild / safehtml 等 dep 走 GOPROXY,约 30 秒,后续秒启。 + +## docker-compose 启动(备选,给没 Go toolchain 的同事) + +仓库根有 [`compose.yaml`](compose.yaml): + +```sh +docker compose up -d +open http://localhost:8089/pkgsitex/ ``` -For more information, see the [pkgsite documentation](https://pkg.go.dev/golang.org/x/pkgsite/cmd/pkgsite). +首次 build 拉 `golang:1.24` image(~700 MB) + esbuild bundle,约 3-5 分钟。改 `fork/main` 后 `docker compose build pkgsitex` 增量很快。 -## API +## 配置选项 -`pkgsite` provides a REST API for retrieving package and module information. -The API endpoints include: +| flag | 用途 | 默认 | +|---|---|---| +| `-base-path=/pkgsitex` | 站点挂子路径下,反代 / 共享域名场景。空 = 挂根(pkg.go.dev 行为) | `""` | +| `-show-unexported` | godoc 渲染保留未导出符号;浏览器用 toggle 控制显隐 | `false` | +| `-http=:8089` | 监听端口 | `localhost:8080` | +| `-cache` | 走 GOMODCACHE | `false` | +| `-proxy` | 走 GOPROXY 拉远程 module | `false` | +| `...` | 要索引的 Go module 路径(多个) | `.` | -- `/v1/package/{path}`: Information about the package at `{path}`. -- `/v1/module/{path}`: Information about the module at `{path}`. -- `/v1/versions/{path}`: Versions of the module at `{path}`. -- `/v1/packages/{path}`: Information about packages of the module at `{path}`. -- `/v1/search`: Search results. -- `/v1/symbols/{path}`: List of symbols for the package at `{path}`. -- `/v1/imported-by/{path}`: Paths of packages importing the package at `{path}`. -- `/v1/vulns/{path}`: Vulnerabilities of the module at `{path}`. +完整 flag:`go run ./cmd/pkgsite -h`。 -For detailed documentation of parameters and response schemas, see the documentation page served at `/api`, or refer directly to the annotated source code in `internal/api/api.go`. +## 增 / 减仓库 -## Issues +**本地 binary**:直接改 command 末尾 path 列表。 -If you want to report a bug or have a feature suggestion, please first check -the [known issues](https://github.com/golang/go/labels/pkgsite) to see if your -issue is already being discussed. If an issue does not already exist, feel free -to [file an issue](https://golang.org/s/pkgsite-feedback). +**docker-compose**:编辑 [`compose.yaml`](compose.yaml) `volumes:` 加一行 `../:/repos/:ro`,`command:` 末尾加 `/repos/`。 -For answers to frequently asked questions, see [pkg.go.dev/about](https://pkg.go.dev/about). +约定:内部仓库都 clone 在 `~/Repo/`。UniAuth 是 monorepo 含 `uniauth-gf/` + `ittools_sync/` 两个 Go module,各 mount 一份——pkgsite 不支持 nested module 自动发现。 -You can also chat with us on the -[#pkgsite Slack channel](https://gophers.slack.com/archives/C0166L4QGJV) on the -[Gophers Slack](https://invite.slack.golangbridge.org). +## 浏览体验 -## Contributing +| 元素 | 行为 | +|---|---| +| URL `/pkgsitex/` | module overview | +| URL `/pkgsitex//` | 子包 | +| URL `/pkgsitex/@` | 指定 git tag(local mode 走 module cache,prod worker 模式才有完整 tag 历史) | +| **Show unexported** button | Index 标题旁,切私有 declaration / 侧边栏 / index 链接的显隐,状态 localStorage 跨页保留 | +| **Show internal directories** button | 上游内置,切 `internal/` 子目录显示。`data-local=true` 时自动开启 | +| ` `code` ` / `**bold**` / ` ```mermaid ` | godoc 注释里这些 markdown 写法都渲染(fork 加的 dochtml ext) | +| **View Source** | 包详情页每个声明右侧链接,跳本地 mount 的源码文件(不依赖 GitHub 在线访问) | -We would love your help! +## Patch 集合(fork 改了什么) -Our canonical Git repository is located at -[go.googlesource.com/pkgsite](https://go.googlesource.com/pkgsite). -There is a mirror of the repository at -[github.com/golang/pkgsite](https://github.com/golang/pkgsite). +- **`-base-path`**:URL 子路径前缀,所有 mux pattern / template helper / godoc cross-reference / view source 自动 prefix +- **`-show-unexported`**:让 `internal/fetch/load.go` 在 AST 阶段保留 unexported FuncDecl + `doc.NewFromFiles` 用 `doc.AllDecls` +- **godoc markdown ext**(`internal/godoc/dochtml/internal/render/markdown_ext.go`):post-process HTML 加 inline code / bold / mermaid fence 识别 +- **mermaid client lazy-load**(`static/frontend/frontend.tmpl`):页面有 `code.language-mermaid` 才动态 import mermaid@10 +- **unexported toggle**(`static/frontend/unit/main/main.ts`):client-side hide + button + localStorage +- **view source 本地 file mux** + base path 拼接修 +- **multi-repo command line**:直接 list 多个 module path +- **trailing slash 死循环修**:`internal/frontend/details.go` 在 base path 模式下区分"挂根"和"base path 自身" -To contribute, please read our [contributing guide](CONTRIBUTING.md). +## Prod 部署(待做) -## License +cmd/pkgsite + 文件 mount 是 dev / 个人模式,prod 走另一套: + +- **cmd/frontend + cmd/worker + Postgres** 三件套(pkg.go.dev 自身架构) +- worker 异步拉私有 GitHub repo 入 DB(需配 GitHub PAT + git credential helper) +- frontend 从 DB 读 + 缓存命中 +- 配置文件驱动 repo list + token +- license 放行 patch(让 `all rights reserved` / unknown / proprietary license 也能渲) +- git tag 多版本切换 UI + +整套在 `docs/PROD_DEPLOY.md` 待写。 + +## 维护策略 + +- **master 永远 = upstream master**——不 merge `fork/main` 进 master。GitHub "Sync fork" 永远 fast-forward 0 conflict +- **`fork/main` 是 long-lived patch branch**——所有 fork 改动累积在此分支 +- **月度 rebase**:AI 跑 `git rebase upstream/master` 让 `fork/main` 跟随上游新进展。预测 conflict 集中在: + - `internal/frontend/server.go` 的 mux 列表(上游新加路由时) + - `static/**/*.tmpl` / `*.ts`(上游改样式 / 加交互时) + - 其他 fork 文件(如 `markdown_ext.go`、`base-path/base-path.ts`)几乎不会 conflict(fork 独有) + +历次 patch 详见 PR [#2](https://github.com/NickWilde18/pkgsitex/pull/2)。 + +## 故障排查 + +| 现象 | 排查 | +|---|---| +| `go run` 拉 dep 失败 | `GOPROXY=https://goproxy.cn,direct go run ...`(Dockerfile 默认 goproxy.cn) | +| docker `pull access denied` | 用 `docker compose build pkgsitex` 走本地 build,不要拉 image | +| 浏览 `/pkgsitex/...` 静态资源 404 | 改了 `static/`/模板后没重 build:`go run ./devtools/cmd/static` 重生 bundle,或 docker 路径 `docker compose build pkgsitex` | +| 解析 module 超时 | 走公司 Athens:`docker compose build --build-arg GOPROXY=https://athens.corp.com pkgsitex` 或本地 `export GOPROXY=...` | +| mermaid 不渲染 | 浏览器 console 看 `mermaid load failed`——内网拦了 jsdelivr CDN,需要本地 host mermaid(`third_party/mermaid/` 待 vendor) | +| Sidebar / Index 没"Show unexported"按钮 | 可能 localStorage 之前 toggle key 是老名字(`gogodocs:showUnexported`),点一次按钮即可重置成新 key | -Unless otherwise noted, the Go source files are distributed under the BSD-style -license found in the [LICENSE](LICENSE) file. +--- -## Links +## 上游 pkgsite + +下游 fork 需要跟随上游进展,保留上游 README 内容如下,方便核对功能 / 升级时定位差异。 + +# golang.org/x/pkgsite + +This repository hosts the source code of the [pkg.go.dev](https://pkg.go.dev) website, +and [`pkgsite`](https://pkg.go.dev/golang.org/x/pkgsite/cmd/pkgsite), a documentation +server program. + +[![Go Reference](https://pkg.go.dev/badge/golang.org/x/pkgsite.svg)](https://pkg.go.dev/golang.org/x/pkgsite) + +完整上游 README:[golang/pkgsite README](https://github.com/golang/pkgsite#readme)。 + +## Issues + +Fork 自身 issues(base-path / markdown ext / etc):[NickWilde18/pkgsitex/issues](https://github.com/NickWilde18/pkgsitex/issues)。 + +上游 pkgsite issues 报到 [`golang/go`](https://golang.org/issues),前缀 `x/pkgsite:`,详见上游 README。 + +## Contributing + +Fork 内部 PR 投到 `fork/main` 分支(不进 master)。 + +上游 pkgsite contribution flow(gerrit code review)见 [Contribution Guide](https://golang.org/doc/contribute.html) + [上游 README contributing 段](https://github.com/golang/pkgsite#contributing)。 + +## License -- [Homepage](https://pkg.go.dev) -- [Feedback](https://golang.org/s/pkgsite-feedback) -- [Issue Tracker](https://golang.org/s/pkgsite-issues) +Unless otherwise noted, the source files are distributed under the BSD-style license found in the LICENSE file. diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 000000000..83996ff8a --- /dev/null +++ b/compose.yaml @@ -0,0 +1,53 @@ +# pkgsitex 本地启动 —— 索引平级 clone 在 ~/Repo/ 下的内部 Go module。 +# +# 使用: +# cd ~/Repo/pkgsitex +# docker compose up -d +# open http://localhost:8089/pkgsitex/ +# +# 不需要 ~/Repo/pkgsitex 自身平级——pkgsitex repo 是 build context 自己。其他 +# 内部仓库(Chat / UniAuth / open-platform / Doubao-Speech-Service 等)需要 +# 已 clone 到 ~/Repo/ 下,相对路径 ../ mount 进容器。 +# +# 增 / 减仓库: +# 1) volumes: 加一行 ../:/repos/:ro +# 2) command: 末尾加 /repos/ +# +# 本地有 Go toolchain 时直接 `go run ./cmd/pkgsite -base-path=... ` +# 比 docker 启动快 10 倍——docker 适合给没 Go toolchain 的同事用,或者 +# 想跟其他 service(mssql / redis / 业务 backend)一起编排时。 +# +# Prod 部署是另一回事:cmd/frontend + cmd/worker + Postgres 三件套,配置 +# 文件驱动 + GitHub PAT 拉私有 repo + license 放行——见 docs/PROD_DEPLOY.md +# (TODO,下一阶段做)。 + +services: + pkgsitex: + build: + context: . + image: pkgsitex:local + container_name: pkgsitex + ports: + - "8089:8080" + volumes: + # mount 公司内部 Go module 仓库(约定 ~/Repo/,docker-compose 的 + # working dir 是本 yaml 所在目录,相对 .. 就是 ~/Repo/) + - ../Chat:/repos/chat:ro + - ../Doubao-Speech-Service:/repos/doubao-speech-service:ro + # UniAuth 是 monorepo 含两个 Go module,分别 mount——pkgsite 不支持 + # nested module 自动发现 + - ../UniAuth/uniauth-gf:/repos/uniauth-gf:ro + - ../UniAuth/ittools_sync:/repos/ittools_sync:ro + - ../open-platform:/repos/open-platform:ro + command: + - "-base-path=/pkgsitex" + - "-show-unexported" + - "-http=:8080" + # mount 进来的 module path 全列出来——pkgsite 不会自动发现 /repos/ 下 + # 多 module,需要显式列。增减仓库时跟上面 volumes: 同步改 + - "/repos/chat" + - "/repos/doubao-speech-service" + - "/repos/uniauth-gf" + - "/repos/ittools_sync" + - "/repos/open-platform" + restart: unless-stopped From 4a90aced04ec252a06e697cb88802c4c4654663a Mon Sep 17 00:00:00 2001 From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com> Date: Thu, 7 May 2026 22:52:52 +0800 Subject: [PATCH 15/26] =?UTF-8?q?feat(L1):=20license=20Permissive=20?= =?UTF-8?q?=E5=85=A8=E5=B1=80=E5=BC=80=E5=85=B3=E2=80=94=E2=80=94prod=20?= =?UTF-8?q?=E5=86=85=E7=BD=91=E9=83=A8=E7=BD=B2=E6=94=BE=E8=A1=8C=20propri?= =?UTF-8?q?etary?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cmd/worker / cmd/frontend prod 模式下,让 pkgsite 内部所有 license 检查直接通过——内网仓库常见情况:私有 repo 没 LICENSE 文件、或者 LICENSE 写 \"All rights reserved\" / \"Proprietary\" 之类非 OSS license。 上游默认拒绝渲染("this package is not redistributable" page),fork 接受这种场景。 实现:internal/licenses/licenses.go 加全局 var Permissive bool。 - Detector.ModuleIsRedistributable():Permissive=true 直接返 true - Redistributable(types):Permissive=true 直接返 true(容许空 license 列表 + 任意 SPDX 类型) 默认 Permissive=false 跟上游零差异;prod config 显式开 permissive: true。 注:开启后 license metadata 仍被解析 + 展示在 UI——是否真对外公开仍 由 pkgsitex 的网络可达性 + UniAuth 鉴权决定。 go test ./internal/licenses/... 全绿——上游测试基于 default Permissive=false 不受影响。 --- internal/licenses/licenses.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/internal/licenses/licenses.go b/internal/licenses/licenses.go index a2f55d85f..1692a749c 100644 --- a/internal/licenses/licenses.go +++ b/internal/licenses/licenses.go @@ -370,8 +370,27 @@ func NewDetectorFS(modulePath, version string, fsys fs.FS, logf func(string, ... return d } +// Permissive 是 fork 加的全局开关——cmd/worker / cmd/frontend prod 模式下让 +// pkgsite 内部所有 license 检查直接通过,无视 SPDX 类型 / 文件内容。 +// +// 用途:内网部署给私有 / 闭源 repo 浏览 godoc,常见场景下 repo 没有 LICENSE +// 文件、或者只有 "All rights reserved" 之类的 proprietary 声明——上游 +// pkg.go.dev 默认会拒绝渲染这种 module("this package is not redistributable" +// page)。本 fork 在 internal 部署时显式接受非 OSS license。 +// +// 默认 false 跟上游零差异。配置文件 `licenses.permissive: true`(或 cmd +// flag `-permissive-licenses`)启用。 +// +// 安全提示:开启后所有 license metadata 仍被解析 + 展示在 UI;只是不再用 +// "is_redistributable=false" 这个开关锁页面。最终是否对外公开仍由 +// pkgsitex 的网络可达性 + UniAuth 鉴权决定(见 docs/PROD_DEPLOY.md)。 +var Permissive bool + // ModuleIsRedistributable reports whether the given module is redistributable. func (d *Detector) ModuleIsRedistributable() bool { + if Permissive { + return true + } return d.moduleRedist } @@ -601,6 +620,12 @@ func DetectFile(contents []byte, filename string, logf func(string, ...any)) ([] // All the licenses we see that are relevant must be redistributable, and // we must see at least one such license. func Redistributable(licenseTypes []string) bool { + if Permissive { + // fork:prod 内网部署模式下接受任意 license 类型(含 unknown / + // proprietary / "all rights reserved" 这种 fork 关心的场景)。 + // 同时也接受**空 license 列表**(很多内部 repo 根本没 LICENSE 文件)。 + return true + } sawRedist := false for _, t := range licenseTypes { if ignorableLicenseTypes[t] { From d8544dece0c96265b090cfe51d64ab83ae24ec07 Mon Sep 17 00:00:00 2001 From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com> Date: Thu, 7 May 2026 23:04:34 +0800 Subject: [PATCH 16/26] =?UTF-8?q?feat(L2-L5):=20prod=204=20=E4=BB=B6?= =?UTF-8?q?=E5=A5=97=20docker-compose=20+=20pkgsitex-init=20=E5=AE=B9?= =?UTF-8?q?=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit prod 部署架构(跟 cmd/pkgsite local mode 解耦):postgres + athens + worker + frontend,加 init / migrate / netrc-writer 一次性容器 共 7 个 service。Athens 当 GOPROXY 缓存层 + 用 PAT 拉私有 GitHub repo, worker 走 athens 而非 proxy.golang.org,frontend 从 postgres 读渲染。 文件: - cmd/pkgsitex-init/main.go:prod stack 的 bootstrap 容器。三 mode: - netrc-only:只写 athens netrc(含 PAT),athens 启动前跑(避免 athens→worker→athens 循环依赖) - init:等 worker /healthcheck OK + 对每个 module enqueue(versions: all-tags 时先 git ls-remote 拿全 tag) - refresh:周期 cron,跳过 netrc / wait,仅重新 ls-remote + enqueue 新版本 - compose.prod.yaml:完整 7 service 定义。docker-compose dependency graph:netrc-writer → athens → worker → init;postgres → migrate → worker / frontend。volumes 持久 postgres-data / athens-data / athens-config(netrc 落 disk volume) - deploy/prod/Dockerfile:multi-stage build worker / frontend / init 三个 target 共享 base(debian-slim + ca-certs + git)。git 给 init 跑 ls-remote 用 - deploy/prod/{.env.example, config.yaml, README.md}: .env 模板(GITHUB_TOKEN);config schema(module list + versions all-tags 开关 + monorepo 子模块的 repoUrl override);用户启动 README 含架构图 + 加 / 删 module 操作 + 周期 refresh cron 配方 + 故障排查表 - .gitignore:拦 deploy/prod/.env 防 PAT 明文 commit 未做(README TODO 列出,下一轮补): - cmd/frontend / cmd/worker 加 -base-path / -show-unexported / -license-permissive flag。当前 compose 用 PKGSITEX_* env var 占位,未通到代码层 - init 容器周期 cron 内置(当前依赖 host crontab) Smoke build:go build ./cmd/{worker,frontend,pkgsitex-init} 全过; docker-compose 真起验证留下一轮(牵涉拉 image + 私有 repo PAT 测, 做完会更新 PR description)。 --- .gitignore | 4 + cmd/pkgsitex-init/main.go | 302 ++++++++++++++++++++++++++++++++++++++ compose.prod.yaml | 186 +++++++++++++++++++++++ deploy/prod/.env.example | 15 ++ deploy/prod/Dockerfile | 50 +++++++ deploy/prod/README.md | 130 ++++++++++++++++ deploy/prod/config.yaml | 46 ++++++ 7 files changed, 733 insertions(+) create mode 100644 cmd/pkgsitex-init/main.go create mode 100644 compose.prod.yaml create mode 100644 deploy/prod/.env.example create mode 100644 deploy/prod/Dockerfile create mode 100644 deploy/prod/README.md create mode 100644 deploy/prod/config.yaml diff --git a/.gitignore b/.gitignore index 17e5a31db..0b74a38fd 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,7 @@ __diff_output__ #### Cloud Build Artifacts #### _BUILD_TAG _ID_TOKEN + +#### pkgsitex prod stack 配置 #### +# .env 含 GITHUB_TOKEN 明文,绝对不能提交 +deploy/prod/.env diff --git a/cmd/pkgsitex-init/main.go b/cmd/pkgsitex-init/main.go new file mode 100644 index 000000000..23dfed0b4 --- /dev/null +++ b/cmd/pkgsitex-init/main.go @@ -0,0 +1,302 @@ +// Copyright 2026 Yechi Yang. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file of the upstream pkgsite. + +// Command pkgsitex-init 是 prod stack 的 init container: +// +// 1. 从 /etc/pkgsitex/config.yaml 读 GitHub PAT + 要监控的 module 列表 +// 2. 写 /etc/athens/.netrc 让 athens 内部 git 操作走 PAT 认证拉私有 repo +// 3. 等 worker http 就绪 +// 4. 对每个 module:可选 git ls-remote --tags 拿所有版本(multi-version 模式) +// + POST worker /fetch/{module}/@v/{version} 触发 worker 异步入 DB +// +// 进程跑完即退出(Kubernetes init container 模式 / docker compose 一次性 service)。 +// 周期 refresh 由独立 cron container(同一镜像,--mode=refresh)做——首次启动 +// 拉 latest + 全 tag,后续 cron 拉新增 tag。 +package main + +import ( + "context" + "errors" + "flag" + "fmt" + "io" + "log" + "net/http" + "net/url" + "os" + "os/exec" + "path/filepath" + "strings" + "time" + + "gopkg.in/yaml.v3" +) + +// Config 是 prod stack 的配置文件 schema,对应 /etc/pkgsitex/config.yaml。 +// +// 用户视角只需关心 github.token + modules——其他字段在 docker-compose 模板里 +// 已固定指向同 stack 内部 service 名,不需要改。 +type Config struct { + GitHub struct { + // Token 是 classic GitHub PAT(repo scope full)——athens 内部 git + // clone 用它拉私有 repo。明文存入 .netrc,因此 config.yaml + .netrc + // 都需 600 权限。生产环境通过 env var GITHUB_TOKEN 注入更佳,配置里 + // 写 ${GITHUB_TOKEN} 启动时 expand。 + Token string `yaml:"token"` + } `yaml:"github"` + + // Modules:prod 启动时 enqueue 给 worker 的 module 列表。每条 path 是 + // Go module path(如 github.com/CUHKSZ-ITSO-Dev/Chat 或 monorepo 子模块 + // github.com/CUHKSZ-ITSO-Dev/UniAuth/uniauth-gf)。 + Modules []ModuleSpec `yaml:"modules"` + + // Worker 内部 service URL;docker-compose 默认 "http://worker:8000"。 + Worker struct { + URL string `yaml:"url"` + } `yaml:"worker"` + + // Athens 配置:netrc 输出位置,让 athens 内部 git 拿到 PAT。 + Athens struct { + NetrcPath string `yaml:"netrcPath"` + } `yaml:"athens"` +} + +// ModuleSpec 一个待索引 module 的配置。 +type ModuleSpec struct { + // Path 是 Go module path(pkg.go.dev URL 的 module 段) + Path string `yaml:"path"` + + // Versions 控制初始化时 enqueue 哪些版本: + // - "latest"(默认):只 enqueue @latest,worker resolve 成 default branch + // - "all-tags":git ls-remote --tags 拿所有 tag + enqueue 每个 + 还有 + // latest(fork 内网部署常用——团队希望看每个 release 的文档) + // + // 不在配置里枚举具体版本字符串——上游 pkgsite 已经支持任意 module + // version URL 直接访问会触发 worker 按需 fetch;这里只是启动预热。 + Versions string `yaml:"versions"` + + // RepoURL 可选——module path 跟 git repo URL 不一致时用(如 monorepo + // 子模块 github.com/CUHKSZ-ITSO-Dev/UniAuth/uniauth-gf 的 repo URL 是 + // github.com/CUHKSZ-ITSO-Dev/UniAuth)。默认从 module path 推断: + // 取 github.com// 前三段。 + RepoURL string `yaml:"repoUrl"` +} + +func main() { + configPath := flag.String("config", "/etc/pkgsitex/config.yaml", "path to pkgsitex config yaml") + mode := flag.String("mode", "init", + "netrc-only = 只写 netrc 退出(在 athens 启动前跑,避免 athens 等 worker 等 athens 循环依赖);"+ + "init = 等 worker 就绪 + enqueue 全 module(首次启动);"+ + "refresh = 跳过 netrc / wait,仅重 ls-remote tags + enqueue 新版本(周期 cron)") + flag.Parse() + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) + defer cancel() + + cfg, err := loadConfig(*configPath) + if err != nil { + log.Fatalf("load config %s: %v", *configPath, err) + } + cfg.GitHub.Token = os.ExpandEnv(cfg.GitHub.Token) + if cfg.GitHub.Token == "" { + log.Fatal("github.token 为空(环境变量 GITHUB_TOKEN 未设置 / config 没填)") + } + + switch *mode { + case "netrc-only": + if err := writeNetrc(cfg.Athens.NetrcPath, cfg.GitHub.Token); err != nil { + log.Fatalf("写 netrc 失败: %v", err) + } + log.Printf("[netrc-only] netrc 写入 %s", cfg.Athens.NetrcPath) + + case "init": + if err := writeNetrc(cfg.Athens.NetrcPath, cfg.GitHub.Token); err != nil { + log.Fatalf("写 netrc 失败: %v", err) + } + log.Printf("[init] netrc 写入 %s", cfg.Athens.NetrcPath) + + if err := waitWorkerReady(ctx, cfg.Worker.URL); err != nil { + log.Fatalf("等 worker 就绪超时: %v", err) + } + log.Printf("[init] worker %s 就绪", cfg.Worker.URL) + + enqueueAll(ctx, cfg) + log.Printf("[init] 完成") + + case "refresh": + // refresh 模式跳过 netrc / worker wait(cron 跑时 stack 已稳定运行) + enqueueAll(ctx, cfg) + log.Printf("[refresh] 完成") + + default: + log.Fatalf("未知 mode=%q(支持 netrc-only / init / refresh)", *mode) + } +} + +func loadConfig(path string) (*Config, error) { + b, err := os.ReadFile(path) + if err != nil { + return nil, err + } + var cfg Config + if err := yaml.Unmarshal(b, &cfg); err != nil { + return nil, fmt.Errorf("yaml unmarshal: %w", err) + } + if cfg.Worker.URL == "" { + cfg.Worker.URL = "http://worker:8000" + } + if cfg.Athens.NetrcPath == "" { + cfg.Athens.NetrcPath = "/etc/athens/.netrc" + } + return &cfg, nil +} + +// writeNetrc 输出 athens 用的 .netrc 让 git 走 PAT 认证。 +// +// 文件格式(git 标准): +// +// machine github.com +// login x-access-token +// password +// +// "x-access-token" 是 GitHub 的 PAT username 占位(HTTPS Basic Auth 协议要求 +// username 非空,PAT 走 password 字段)。 +// +// 同时确保 dir 存在 + 权限 600(含 PAT 明文)。 +func writeNetrc(path, token string) error { + if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil { + return err + } + content := fmt.Sprintf("machine github.com\nlogin x-access-token\npassword %s\n", token) + return os.WriteFile(path, []byte(content), 0o600) +} + +// waitWorkerReady 轮询 worker /healthcheck 端点直到就绪或 ctx 超时。 +// +// 间隔 2 秒;总超时由 caller ctx 控制。docker-compose 的 healthcheck 应该 +// 已让 worker 在 init 启动前就 ready,但这层兜底防容器启动顺序 race。 +func waitWorkerReady(ctx context.Context, workerURL string) error { + tick := time.NewTicker(2 * time.Second) + defer tick.Stop() + for { + req, _ := http.NewRequestWithContext(ctx, http.MethodGet, workerURL+"/healthcheck", nil) + resp, err := http.DefaultClient.Do(req) + if err == nil { + io.Copy(io.Discard, resp.Body) + resp.Body.Close() + if resp.StatusCode < 400 { + return nil + } + } + select { + case <-ctx.Done(): + return ctx.Err() + case <-tick.C: + } + } +} + +// enqueueAll 遍历 cfg.Modules,对每个 module 触发 worker 拉相应 version。 +// +// versions=all-tags 时先 git ls-remote --tags 拿全 tag 再 enqueue 每个;否则只 +// enqueue @latest(worker 自己 resolve)。错误只 log 不 fatal——单 module 失败 +// 不该阻塞其他。 +func enqueueAll(ctx context.Context, cfg *Config) { + for _, mod := range cfg.Modules { + versions := []string{"latest"} + if mod.Versions == "all-tags" { + tags, err := lsRemoteTags(ctx, mod, cfg.GitHub.Token) + if err != nil { + log.Printf("ls-remote %s: %v(跳过,仅 enqueue @latest)", mod.Path, err) + } else { + versions = append(versions, tags...) + } + } + for _, v := range versions { + if err := enqueueOne(ctx, cfg.Worker.URL, mod.Path, v); err != nil { + log.Printf("enqueue %s@%s: %v", mod.Path, v, err) + continue + } + log.Printf("enqueue %s@%s 成功", mod.Path, v) + } + } +} + +// lsRemoteTags 跑 git ls-remote --tags 拿 module 对应 repo 的所有 git tag。 +// +// 用 https://x-access-token:@github.com/ 形式让 git 走 PAT 认证。 +// PAT 出现在 cmd 行参数里——子进程不会 leak(exec.Command 不写 shell history), +// 但 ps 临时可见——内网容器内部环境可接受。 +// +// repoURL 推断:默认取 module path 前三段(github.com//), +// monorepo 子模块走显式 RepoURL 字段。 +// +// 返回值过滤:peeled tag refs(refs/tags/v1.0^{})跳掉,pkgsite 只认 vX.Y.Z +// 形式的 SemVer tag——非 SemVer tag 也 enqueue 但 worker 大概率会拒(log 警告 +// 但不 fatal,让 caller 看到详细错误码)。 +func lsRemoteTags(ctx context.Context, mod ModuleSpec, token string) ([]string, error) { + repoURL := mod.RepoURL + if repoURL == "" { + // 从 module path 推断:取前三段 + parts := strings.Split(mod.Path, "/") + if len(parts) < 3 || parts[0] != "github.com" { + return nil, fmt.Errorf("无法推断 repo URL(仅支持 github.com/owner/repo... 形式,得到 %q)", mod.Path) + } + repoURL = strings.Join(parts[:3], "/") + } + + authedURL := fmt.Sprintf("https://x-access-token:%s@%s", url.PathEscape(token), repoURL) + cmd := exec.CommandContext(ctx, "git", "ls-remote", "--tags", authedURL) + out, err := cmd.Output() + if err != nil { + // 不 leak token 到错误信息:只显示 repoURL 不含 token + if errors.Is(err, exec.ErrNotFound) { + return nil, fmt.Errorf("git 二进制不在 PATH(请检查 init container image)") + } + return nil, fmt.Errorf("git ls-remote %s 失败: %w", repoURL, err) + } + + var tags []string + for _, line := range strings.Split(string(out), "\n") { + idx := strings.Index(line, "refs/tags/") + if idx < 0 { + continue + } + tag := line[idx+len("refs/tags/"):] + // peeled tag refs(如 "refs/tags/v1.0.0^{}")跳过——它们指向 commit + // 而不是 tag object 本身,重复出现 + if strings.HasSuffix(tag, "^{}") { + continue + } + tags = append(tags, tag) + } + return tags, nil +} + +// enqueueOne 调 worker /fetch//@v/ 端点触发异步 fetch。 +// +// 该端点是 pkgsite 上游 worker 内置——worker.handleFetch 处理:先看 DB 是否 +// 已有此版本数据,没有就 enqueue 进 pgqueue / inmem queue 走 ProxyClient +// 拉模块(我们 stack 里 ProxyClient 指 athens,athens 走 git + PAT 拉私有 +// repo)。 +// +// HTTP 200 = 已 enqueue(不代表 fetch 成功,那是异步事),4xx/5xx 才是真 +// 错误(典型:module path 错 / version 格式不合 SemVer)。 +func enqueueOne(ctx context.Context, workerURL, modulePath, version string) error { + u := fmt.Sprintf("%s/fetch/%s/@v/%s", workerURL, modulePath, version) + req, err := http.NewRequestWithContext(ctx, http.MethodPost, u, nil) + if err != nil { + return err + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode >= 400 { + body, _ := io.ReadAll(io.LimitReader(resp.Body, 1024)) + return fmt.Errorf("HTTP %d: %s", resp.StatusCode, strings.TrimSpace(string(body))) + } + return nil +} diff --git a/compose.prod.yaml b/compose.prod.yaml new file mode 100644 index 000000000..d1da555ff --- /dev/null +++ b/compose.prod.yaml @@ -0,0 +1,186 @@ +# pkgsitex prod 部署 stack —— 4 件套 + 一次性 init / migrate: +# +# postgres: pkgsite schema 缓存 module 索引(disk volume 持久) +# athens: GOPROXY 缓存层 + 用 PAT 拉私有 GitHub repo(disk 持久) +# worker: 周期 fetch module → 入 postgres +# frontend: 用户浏览的 web,base-path / show-unexported / license +# permissive 全开,端口 8089 暴露 +# migrate: 一次性,启动时跑 migrations/ 下 SQL up(基于 migrate/migrate) +# init: 一次性,写 athens netrc + 用 PAT git ls-remote 拿 tag + +# 调 worker /fetch enqueue 各 module 各版本 +# +# 启动: +# cp deploy/prod/.env.example deploy/prod/.env +# vi deploy/prod/.env # 填 GITHUB_TOKEN +# vi deploy/prod/config.yaml # 填要监控的 module 列表 +# docker compose -f compose.prod.yaml --env-file deploy/prod/.env up -d +# open http://localhost:8089/pkgsitex/ +# +# 加新 module:编辑 deploy/prod/config.yaml;触发刷新: +# docker compose -f compose.prod.yaml --env-file deploy/prod/.env run --rm init --mode=refresh +# +# Prod (k8s) 部署:以本 yaml 为底稿改写 helm chart,DB / athens 用 cluster 内 +# 共享实例,init 改成 CronJob 周期 refresh。 + +services: + postgres: + image: postgres:16-alpine + environment: + POSTGRES_USER: pkgsitex + POSTGRES_PASSWORD: pkgsitex + POSTGRES_DB: discovery_db + volumes: + - postgres-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U pkgsitex -d discovery_db"] + interval: 5s + timeout: 3s + retries: 20 + restart: unless-stopped + + athens: + image: gomods/athens:v0.15.0 + environment: + ATHENS_STORAGE_TYPE: disk + ATHENS_DISK_STORAGE_ROOT: /var/lib/athens + ATHENS_DOWNLOAD_MODE: sync + ATHENS_NETRC_PATH: /etc/athens/.netrc + ATHENS_LOG_LEVEL: info + # athens 内部 git clone 时通过 .netrc 走 PAT 认证拉私有 repo; + # GOPROXY=direct 让 athens 直接 git 拉而不是再代理给上游 + GOPROXY: direct + volumes: + - athens-data:/var/lib/athens + - athens-config:/etc/athens + expose: + - "3000" + healthcheck: + test: ["CMD", "wget", "-q", "-O", "-", "http://localhost:3000/healthz"] + interval: 10s + timeout: 5s + retries: 30 + depends_on: + init: + # 等 init 写好 netrc 再起 athens + condition: service_completed_successfully + restart: unless-stopped + + migrate: + image: migrate/migrate:v4.18.1 + volumes: + - ../migrations:/migrations:ro + command: + - "-path=/migrations" + - "-database=postgres://pkgsitex:pkgsitex@postgres:5432/discovery_db?sslmode=disable" + - "up" + depends_on: + postgres: + condition: service_healthy + + worker: + build: + context: .. + dockerfile: deploy/prod/Dockerfile + target: worker + args: + GOPROXY: ${GOPROXY:-https://goproxy.cn,https://proxy.golang.org,direct} + environment: + GO_DISCOVERY_DATABASE_HOST: postgres + GO_DISCOVERY_DATABASE_USER: pkgsitex + GO_DISCOVERY_DATABASE_PASSWORD: pkgsitex + GO_DISCOVERY_DATABASE_NAME: discovery_db + GO_MODULE_PROXY_URL: http://athens:3000 + PORT: "8000" + command: + - "-queue=postgres" + - "-static=/app/static" + expose: + - "8000" + depends_on: + migrate: + condition: service_completed_successfully + athens: + condition: service_healthy + healthcheck: + test: ["CMD", "wget", "-q", "-O", "-", "http://localhost:8000/healthcheck"] + interval: 10s + timeout: 5s + retries: 30 + restart: unless-stopped + + frontend: + build: + context: .. + dockerfile: deploy/prod/Dockerfile + target: frontend + args: + GOPROXY: ${GOPROXY:-https://goproxy.cn,https://proxy.golang.org,direct} + environment: + GO_DISCOVERY_DATABASE_HOST: postgres + GO_DISCOVERY_DATABASE_USER: pkgsitex + GO_DISCOVERY_DATABASE_PASSWORD: pkgsitex + GO_DISCOVERY_DATABASE_NAME: discovery_db + PORT: "8080" + # fork 加的:base-path / show-unexported / license permissive 都通过 env + # 或 cmd flag——cmd/frontend 暂未支持这些 flag,需要 fork patch 让它读 + # 同 cmd/pkgsite 的 flag。L2 暂走 cmd/pkgsite 行为兼容(待 L4-bis 补 + # cmd/frontend flag 支持,见 docs/PROD_DEPLOY.md TODO)。 + PKGSITEX_BASE_PATH: "/pkgsitex" + PKGSITEX_SHOW_UNEXPORTED: "true" + PKGSITEX_LICENSE_PERMISSIVE: "true" + command: + - "-static=/app/static" + - "-third_party=/app/third_party" + ports: + - "8089:8080" + depends_on: + migrate: + condition: service_completed_successfully + restart: unless-stopped + + # init 一次性跑:写 athens netrc + 等 worker 起来 + enqueue 全 module。 + # athens 依赖 init(要 netrc 文件),所以 init 要先于 athens 跑——但 + # init 又要等 worker /healthcheck —— worker 又依赖 athens —— 循环依赖。 + # + # 解法:分两阶段—— + # 阶段 1(service netrc-writer):仅写 netrc 然后退出,依赖 0。athens 等它 + # 阶段 2(service init):等 worker 起来后 enqueue。worker 依赖 athens + # + # 用同一镜像不同 mode 跑两次。 + netrc-writer: + build: + context: .. + dockerfile: deploy/prod/Dockerfile + target: init + args: + GOPROXY: ${GOPROXY:-https://goproxy.cn,https://proxy.golang.org,direct} + image: pkgsitex-init:local + environment: + GITHUB_TOKEN: ${GITHUB_TOKEN} + volumes: + - ./deploy/prod/config.yaml:/etc/pkgsitex/config.yaml:ro + - athens-config:/etc/athens + command: + - "--config=/etc/pkgsitex/config.yaml" + - "--mode=netrc-only" + + init: + image: pkgsitex-init:local + environment: + GITHUB_TOKEN: ${GITHUB_TOKEN} + volumes: + - ./deploy/prod/config.yaml:/etc/pkgsitex/config.yaml:ro + - athens-config:/etc/athens + command: + - "--config=/etc/pkgsitex/config.yaml" + - "--mode=init" + depends_on: + netrc-writer: + condition: service_completed_successfully + worker: + condition: service_healthy + +volumes: + postgres-data: + athens-data: + athens-config: diff --git a/deploy/prod/.env.example b/deploy/prod/.env.example new file mode 100644 index 000000000..6f4ec4ff4 --- /dev/null +++ b/deploy/prod/.env.example @@ -0,0 +1,15 @@ +# pkgsitex prod stack 环境变量 +# +# 拷成 .env(同目录)后填实际值: +# cp deploy/prod/.env.example deploy/prod/.env +# vi deploy/prod/.env +# +# .env 含 PAT 明文,**不要 git commit**——已在 .gitignore 屏蔽。 + +# Classic GitHub PAT(repo scope full)。athens 内部用 git + PAT 拉私有 repo。 +# 生成:https://github.com/settings/tokens(Classic 不是 fine-grained) +GITHUB_TOKEN=ghp_PASTE_YOUR_TOKEN_HERE + +# GOPROXY for go build——空会走 https://goproxy.cn(fork 默认 build 用) +# 公司有 Athens 私有镜像就改这里: +# GOPROXY=https://athens.corp.com,https://goproxy.cn,direct diff --git a/deploy/prod/Dockerfile b/deploy/prod/Dockerfile new file mode 100644 index 000000000..2a5ae6e18 --- /dev/null +++ b/deploy/prod/Dockerfile @@ -0,0 +1,50 @@ +# pkgsitex prod 三进程镜像(worker / frontend / init)共享 base,build 一次 +# 输出三种 target,docker-compose 用 build.target 选。 +# +# 不同于 cmd/pkgsite local mode 的 Dockerfile(仓库根 Dockerfile):那个跑 +# fetchdatasource 直读文件系统,不走 DB 不走 GOPROXY;本镜像跑 cmd/worker +# + cmd/frontend,需要 PostgreSQL + athens GOPROXY,是 pkg.go.dev 自身的 +# prod 架构。 + +FROM golang:1.24 AS builder +WORKDIR /src + +COPY go.mod go.sum ./ +ARG GOPROXY=https://goproxy.cn,https://proxy.golang.org,direct +ENV GOPROXY=${GOPROXY} +RUN go mod download + +COPY . . +RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /bin/worker ./cmd/worker +RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /bin/frontend ./cmd/frontend +RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /bin/pkgsitex-init ./cmd/pkgsitex-init + +# 共享 base:ca-certificates 给 HTTPS / git;git 给 cmd/pkgsitex-init 跑 +# ls-remote 拿 tag 列表(worker / frontend 不直接调 git,但镜像统一一份) +FROM debian:bookworm-slim AS base +RUN apt-get update && \ + apt-get install -y --no-install-recommends ca-certificates git && \ + rm -rf /var/lib/apt/lists/* + +# === target: worker(跑 cmd/worker 拉 module 入 DB)=== +FROM base AS worker +COPY --from=builder /bin/worker /usr/local/bin/worker +COPY --from=builder /src/static /app/static +COPY --from=builder /src/migrations /app/migrations +WORKDIR /app +EXPOSE 8000 +ENTRYPOINT ["worker"] + +# === target: frontend(跑 cmd/frontend 渲染 godoc)=== +FROM base AS frontend +COPY --from=builder /bin/frontend /usr/local/bin/frontend +COPY --from=builder /src/static /app/static +COPY --from=builder /src/third_party /app/third_party +WORKDIR /app +EXPOSE 8080 +ENTRYPOINT ["frontend"] + +# === target: init(跑 cmd/pkgsitex-init 写 netrc + enqueue 全 module)=== +FROM base AS init +COPY --from=builder /bin/pkgsitex-init /usr/local/bin/pkgsitex-init +ENTRYPOINT ["pkgsitex-init"] diff --git a/deploy/prod/README.md b/deploy/prod/README.md new file mode 100644 index 000000000..b86a55dd9 --- /dev/null +++ b/deploy/prod/README.md @@ -0,0 +1,130 @@ +# pkgsitex prod 部署 + +完整 4 件套 stack(postgres + athens + worker + frontend)+ init / migrate 一次性容器,跑配置驱动 + 私有 repo 自动 fetch + multi-version 支持。 + +跟仓库根的 `compose.yaml`(cmd/pkgsite local mode,单进程,文件 mount)不同——本目录是 prod 模式,跟 pkg.go.dev 自身架构一致。 + +## 启动步骤 + +```sh +cd ~/Repo/pkgsitex # 仓库根,不是 deploy/prod/ + +# 1) 配 PAT +cp deploy/prod/.env.example deploy/prod/.env +$EDITOR deploy/prod/.env # GITHUB_TOKEN=ghp_xxx + +# 2) 配要监控的 module 列表 +$EDITOR deploy/prod/config.yaml # modules: [...] + +# 3) 起 stack(首次 build ~10 分钟下 image + 编译) +docker compose -f compose.prod.yaml --env-file deploy/prod/.env up -d + +# 4) 看日志确认 worker 在拉 module +docker compose -f compose.prod.yaml logs -f worker + +# 5) 浏览 +open http://localhost:8089/pkgsitex/ +``` + +## 运维操作 + +### 加 / 删 module + +编辑 [`config.yaml`](config.yaml) 后触发 init 一次性容器重新 enqueue: + +```sh +docker compose -f compose.prod.yaml --env-file deploy/prod/.env \ + run --rm init --mode=refresh +``` + +`refresh` 模式跳过 netrc 写 + worker wait(stack 已稳定运行),仅重 ls-remote tag + enqueue 新版本给 worker。 + +### 周期刷新(cron) + +每天凌晨 3 点跑 `init --mode=refresh` 增量拉新版本。host crontab: + +``` +0 3 * * * cd ~/Repo/pkgsitex && docker compose -f compose.prod.yaml \ + --env-file deploy/prod/.env run --rm init --mode=refresh +``` + +或者用 host systemd timer。 + +### 看 athens 缓存了哪些 module + +```sh +docker compose -f compose.prod.yaml exec athens ls /var/lib/athens +``` + +### 重置(清 DB + athens 缓存) + +```sh +docker compose -f compose.prod.yaml down -v # -v 删 volumes +``` + +## 架构 + +``` + ┌──────────────────┐ + 配置文件 │ config.yaml │ modules + token + worker URL + └────────┬─────────┘ + │ read + ▼ + ┌──────────────────┐ + │ init container │ 写 netrc + git ls-remote --tags + + │ (one-shot) │ POST worker /fetch//@v/ + └────────┬─────────┘ + │ + ▼ + ┌──────┐ GOPROXY ┌────────────┐ fetch+parse ┌────────┐ + │worker├──────────►│ athens │──── git ────► │ GitHub │ + └──┬───┘ │ (PAT auth) │ PAT │ 私有 │ + │ store └────────────┘ └────────┘ + ▼ + ┌────────┐ + │postgres│ ◄────── frontend(端口 8089)─── 用户浏览 + └────────┘ +``` + +数据流(首次访问 `module@v1.2.3`): + +1. 用户浏览器 → frontend 端口 8089 +2. frontend 查 postgres 没有此 module 数据 → 返 "fetching" 页面 +3. (并行)init 容器启动时已 enqueue(worker `/fetch/...`),worker 异步处理 +4. worker 调 athens GOPROXY 协议要 module zip +5. athens 第一次见 → git clone 用 PAT → 缓存到 disk → 返 zip +6. worker 解析 zip → 入 postgres +7. 用户刷新 → frontend 命中 postgres → 渲 godoc + +## 配置文件 schema + +[`config.yaml`](config.yaml) 字段: + +| 字段 | 类型 | 说明 | +|---|---|---| +| `github.token` | string | classic GitHub PAT(repo scope full);建议写 `${GITHUB_TOKEN}` 走 env | +| `worker.url` | string | worker 内部 URL,docker-compose 默认 `http://worker:8000` | +| `athens.netrcPath` | string | init 写 netrc 给 athens 用,默认 `/etc/athens/.netrc` | +| `modules[].path` | string | Go module path | +| `modules[].versions` | string | `latest`(默认) / `all-tags` | +| `modules[].repoUrl` | string | 可选——monorepo 子模块时指定 git repo URL(不含子目录) | + +## License + +prod stack frontend 默认开 `licenses.permissive`——所有 module 不论 license 类型都渲染(包括 `All Rights Reserved` / unknown / proprietary)。这是内网部署诉求;不希望渲染某些 module 时把它从 `config.yaml` 删除即可。 + +## 故障排查 + +| 现象 | 排查 | +|---|---| +| `worker` 容器拉 module 401 | `docker logs athens` 看是否 git clone 401,PAT 错或 scope 不够 | +| init 容器 git ls-remote `Permission denied` | 同上,PAT 没 `repo` scope | +| frontend 页面 "this package is not redistributable" | 检查 frontend 容器 env `PKGSITEX_LICENSE_PERMISSIVE=true`(cmd/frontend flag 集成完后生效)| +| migrate 失败 | `docker compose logs migrate` 看 SQL 错;通常是 schema 跟上游 mismatch(rebase 后 migrations 文件冲突)| +| athens 拉私有 repo 超时 | 内网 git 出口受限——把 athens 的 git config 走公司代理:编辑 athens 容器 `~/.gitconfig` 加 `http.proxy` | + +## TODO + +- [ ] cmd/frontend / cmd/worker 加 `-base-path` / `-show-unexported` / `-license-permissive` flag(当前用 env var 占位,未通到代码层) +- [ ] init container 周期 cron 内置(不靠 host crontab) +- [ ] athens config volume 改成 ConfigMap 形式让 helm chart 复用更方便 diff --git a/deploy/prod/config.yaml b/deploy/prod/config.yaml new file mode 100644 index 000000000..fd0316831 --- /dev/null +++ b/deploy/prod/config.yaml @@ -0,0 +1,46 @@ +# pkgsitex prod 配置——cmd/pkgsitex-init 启动时读,决定要监控的 module 列表。 +# +# 编辑此文件后跑: +# docker compose -f compose.prod.yaml --env-file deploy/prod/.env \ +# run --rm init --mode=refresh +# 让 init 容器重新枚举 tag + enqueue 给 worker。 + +github: + # ${GITHUB_TOKEN} 在容器里走 env var 注入,不要在这里写明文 PAT + token: ${GITHUB_TOKEN} + +# Worker 内部 service URL——docker-compose 默认 service 名是 worker,端口 8000 +worker: + url: http://worker:8000 + +athens: + # init 写 netrc 给 athens 内部 git 使用 + netrcPath: /etc/athens/.netrc + +# 要监控的 Go module 列表。每条目支持: +# path: Go module path(pkg.go.dev URL 段)—— 必填 +# versions: "latest"(默认,仅拉默认分支)/ "all-tags"(git ls-remote 拿 +# 所有 tag 全部 enqueue,团队希望看每个 release 文档时用) +# repoUrl: 可选——module path 跟 git repo URL 不一致时(如 monorepo 子模块) +# +# 加新 module:append 一行 + 跑 init --mode=refresh。删 module 不需要从 DB 清, +# 后续不再 fetch 自然过期。 +modules: + - path: github.com/CUHKSZ-ITSO-Dev/Chat + versions: all-tags + + - path: github.com/CUHKSZ-ITSO-Dev/Doubao-Speech-Service + versions: all-tags + + # UniAuth 是 monorepo 含两个 Go module(uniauth-gf / ittools_sync)。 + # path 是 module path(含子目录),repoUrl 指 git repo 不含子目录。 + - path: github.com/CUHKSZ-ITSO-Dev/UniAuth/uniauth-gf + versions: all-tags + repoUrl: github.com/CUHKSZ-ITSO-Dev/UniAuth + + - path: github.com/CUHKSZ-ITSO-Dev/UniAuth/ittools_sync + versions: all-tags + repoUrl: github.com/CUHKSZ-ITSO-Dev/UniAuth + + - path: github.com/CUHKSZ-ITSO-Dev/open-platform + versions: all-tags From 58c5c9e2c86f36999f6fcbda13640c075638f7ad Mon Sep 17 00:00:00 2001 From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com> Date: Thu, 7 May 2026 23:19:42 +0800 Subject: [PATCH 17/26] =?UTF-8?q?feat:=20docker.io=20image=20push=20+=20?= =?UTF-8?q?=E5=8D=95=20image=20=E5=A4=9A=20entrypoint=20+=20compose=20?= =?UTF-8?q?=E5=88=87=20image=20=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 切换 prod 部署模式:从"用户本地 build"改成"用户拉 docker.io 镜像即用"。 - deploy/prod/Dockerfile:multi-stage 改成单 stage 输出三 binary (worker / frontend / pkgsitex-init)共用一份 base + ca-certs + git。 减少 docker.io push 频次和拉取流量;compose 里通过 entrypoint: 选 跑哪个 - .github/workflows/docker.yml:fork/main push 时自动 build + push 到 docker.io/nickwilde18/pkgsitex;tag 策略 fork-main 浮动指最新 / immutable 方便 rollback。PR 触发只 build 不 push 验证 Dockerfile。GitHub Actions cache 加速。用户需在 fork repo Settings 加两个 secret:DOCKERHUB_USERNAME / DOCKERHUB_TOKEN - compose.prod.yaml:worker / frontend / netrc-writer / init 四个 service 从 build:context: 改 image: ${PKGSITEX_IMAGE:-...:fork-main}, pull_policy:always 自动跟新;entrypoint: 选具体 binary。env 重写 PKGSITEX_IMAGE 切 sha / 本地 build 镜像 - README.md:Prod 部署段从 "TODO 待写" 改成完整启动指引(4 步 copy env / edit config / up / open);强调拉 image 不需 build; 指 deploy/prod/README.md 看完整运维 - deploy/prod/README.md 启动段改 "首次 docker pull ~30 秒";新增 "切镜像版本 / 用本地 build" 段说明 PKGSITEX_IMAGE / PKGSITEX_PULL_POLICY env 下一步用户操作: 1) https://hub.docker.com/settings/security 生 access token 2) GitHub fork repo Settings → Actions secrets 加 DOCKERHUB_USERNAME / DOCKERHUB_TOKEN 3) push fork/main 触发 workflow,docker.io 上 nickwilde18/pkgsitex 仓库出现镜像即可在 Chat 仓库新 PR 引用 下一步我做:Chat 仓库新 PR 加 prod stack 到 docker-compose.dev.yml (profile=pkgsitex 默认不启)+ deploy/pkgsitex/{config.yaml,.env.example}。 --- .github/workflows/docker.yml | 68 ++++++++++++++++++++++++++++++++++++ README.md | 39 ++++++++++++++++----- compose.prod.yaml | 32 +++++++---------- deploy/prod/Dockerfile | 46 +++++++++++------------- deploy/prod/README.md | 18 +++++++++- 5 files changed, 148 insertions(+), 55 deletions(-) create mode 100644 .github/workflows/docker.yml diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 000000000..882e8b528 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,68 @@ +name: Build & push docker image + +# 推 fork/main 时自动 build + push docker.io/nickwilde18/pkgsitex 单 image +# 含 worker / frontend / pkgsitex-init 三 entrypoint。 +# +# Tag 策略: +# - fork-main:fork/main 分支 HEAD 永远指向最新(compose.prod.yaml 默认拉这个) +# - :每次 push 一份 immutable,方便 rollback +# +# 触发条件:fork/main 推送、PR 推送(PR 只 build 不 push 验证 Dockerfile)。 +# +# 用户操作: +# 1) https://hub.docker.com/settings/security 生成 access token +# 2) GitHub repo Settings → Secrets and variables → Actions 加两个 secret: +# DOCKERHUB_USERNAME = nickwilde18 +# DOCKERHUB_TOKEN = +# 3) push fork/main 触发本 workflow + +on: + push: + branches: + - fork/main + pull_request: + branches: + - master + - fork/main + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + # PR 触发时跳过 login(forks PR 拿不到 secret,build 跑通验证 Dockerfile 即可) + if: github.event_name == 'push' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: nickwilde18/pkgsitex + # tags:fork-main 浮动指向最新 / 不可变 + tags: | + type=raw,value=fork-main,enable=${{ github.ref == 'refs/heads/fork/main' }} + type=sha,format=short,prefix= + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + file: deploy/prod/Dockerfile + # PR 时只 build 不 push(验证 Dockerfile 不坏) + push: ${{ github.event_name == 'push' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + # 用 GitHub Actions cache 加速重 build + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/README.md b/README.md index 30dada286..23fd0a7f1 100644 --- a/README.md +++ b/README.md @@ -90,18 +90,39 @@ open http://localhost:8089/pkgsitex/ - **multi-repo command line**:直接 list 多个 module path - **trailing slash 死循环修**:`internal/frontend/details.go` 在 base path 模式下区分"挂根"和"base path 自身" -## Prod 部署(待做) +## Prod 部署 -cmd/pkgsite + 文件 mount 是 dev / 个人模式,prod 走另一套: +Prod 模式跟 dev 不同——跟 pkg.go.dev 自身架构一致 4 件套: -- **cmd/frontend + cmd/worker + Postgres** 三件套(pkg.go.dev 自身架构) -- worker 异步拉私有 GitHub repo 入 DB(需配 GitHub PAT + git credential helper) -- frontend 从 DB 读 + 缓存命中 -- 配置文件驱动 repo list + token -- license 放行 patch(让 `all rights reserved` / unknown / proprietary license 也能渲) -- git tag 多版本切换 UI +- **postgres**:缓存 module 索引 / 包文档 +- **athens**:GOPROXY 缓存 + 用 GitHub PAT 拉私有 repo +- **worker**:异步 fetch module 入 DB +- **frontend**:从 DB 读渲染(端口 8089) -整套在 `docs/PROD_DEPLOY.md` 待写。 +镜像已发布到 docker.io [`nickwilde18/pkgsitex:fork-main`](https://hub.docker.com/r/nickwilde18/pkgsitex)(GitHub Actions 自动 push)。用户不需要 build——拉 image 即用。 + +```sh +git clone https://github.com/NickWilde18/pkgsitex.git +cd pkgsitex +git checkout fork/main + +# 1) 配 PAT +cp deploy/prod/.env.example deploy/prod/.env +$EDITOR deploy/prod/.env # GITHUB_TOKEN=ghp_xxx + +# 2) 编辑要监控的 module 列表(内置 5 个 CUHKSZ 内部 repo 示例) +$EDITOR deploy/prod/config.yaml + +# 3) 起 stack(自动拉镜像) +docker compose -f compose.prod.yaml --env-file deploy/prod/.env up -d + +# 4) 浏览 +open http://localhost:8089/pkgsitex/ +``` + +完整运维(加 / 删 module、周期 cron 刷新、故障排查):[`deploy/prod/README.md`](deploy/prod/README.md)。 + +支持 multi-version(git tag 切换)、license permissive(私有 / proprietary 也渲)、配置文件驱动。 ## 维护策略 diff --git a/compose.prod.yaml b/compose.prod.yaml index d1da555ff..6c050c084 100644 --- a/compose.prod.yaml +++ b/compose.prod.yaml @@ -78,12 +78,9 @@ services: condition: service_healthy worker: - build: - context: .. - dockerfile: deploy/prod/Dockerfile - target: worker - args: - GOPROXY: ${GOPROXY:-https://goproxy.cn,https://proxy.golang.org,direct} + image: ${PKGSITEX_IMAGE:-nickwilde18/pkgsitex:fork-main} + pull_policy: ${PKGSITEX_PULL_POLICY:-always} + entrypoint: ["/usr/local/bin/worker"] environment: GO_DISCOVERY_DATABASE_HOST: postgres GO_DISCOVERY_DATABASE_USER: pkgsitex @@ -109,12 +106,9 @@ services: restart: unless-stopped frontend: - build: - context: .. - dockerfile: deploy/prod/Dockerfile - target: frontend - args: - GOPROXY: ${GOPROXY:-https://goproxy.cn,https://proxy.golang.org,direct} + image: ${PKGSITEX_IMAGE:-nickwilde18/pkgsitex:fork-main} + pull_policy: ${PKGSITEX_PULL_POLICY:-always} + entrypoint: ["/usr/local/bin/frontend"] environment: GO_DISCOVERY_DATABASE_HOST: postgres GO_DISCOVERY_DATABASE_USER: pkgsitex @@ -148,13 +142,9 @@ services: # # 用同一镜像不同 mode 跑两次。 netrc-writer: - build: - context: .. - dockerfile: deploy/prod/Dockerfile - target: init - args: - GOPROXY: ${GOPROXY:-https://goproxy.cn,https://proxy.golang.org,direct} - image: pkgsitex-init:local + image: ${PKGSITEX_IMAGE:-nickwilde18/pkgsitex:fork-main} + pull_policy: ${PKGSITEX_PULL_POLICY:-always} + entrypoint: ["/usr/local/bin/pkgsitex-init"] environment: GITHUB_TOKEN: ${GITHUB_TOKEN} volumes: @@ -165,7 +155,9 @@ services: - "--mode=netrc-only" init: - image: pkgsitex-init:local + image: ${PKGSITEX_IMAGE:-nickwilde18/pkgsitex:fork-main} + pull_policy: ${PKGSITEX_PULL_POLICY:-always} + entrypoint: ["/usr/local/bin/pkgsitex-init"] environment: GITHUB_TOKEN: ${GITHUB_TOKEN} volumes: diff --git a/deploy/prod/Dockerfile b/deploy/prod/Dockerfile index 2a5ae6e18..f8ed3dfc1 100644 --- a/deploy/prod/Dockerfile +++ b/deploy/prod/Dockerfile @@ -1,10 +1,12 @@ -# pkgsitex prod 三进程镜像(worker / frontend / init)共享 base,build 一次 -# 输出三种 target,docker-compose 用 build.target 选。 +# pkgsitex prod 单 image 三 entrypoint(worker / frontend / init)。 # -# 不同于 cmd/pkgsite local mode 的 Dockerfile(仓库根 Dockerfile):那个跑 -# fetchdatasource 直读文件系统,不走 DB 不走 GOPROXY;本镜像跑 cmd/worker -# + cmd/frontend,需要 PostgreSQL + athens GOPROXY,是 pkg.go.dev 自身的 -# prod 架构。 +# 单 image 设计:减少 docker.io push 频次和拉取流量——用户拉一次就拿到三个 +# 二进制;compose 里通过 entrypoint: 选择跑哪个。三种角色共享一份 ca-certs +# 和 git,本来分开 image 也是同 base,单 image 没浪费。 +# +# 跟仓库根 `Dockerfile`(cmd/pkgsite local mode)不同:那个跑 fetchdatasource +# 直读文件系统,不走 DB 不走 GOPROXY;本镜像跑 cmd/worker / cmd/frontend, +# 需要 PostgreSQL + athens GOPROXY,跟 pkg.go.dev 自身架构一致。 FROM golang:1.24 AS builder WORKDIR /src @@ -19,32 +21,26 @@ RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /bin/worker ./cmd/worke RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /bin/frontend ./cmd/frontend RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /bin/pkgsitex-init ./cmd/pkgsitex-init -# 共享 base:ca-certificates 给 HTTPS / git;git 给 cmd/pkgsitex-init 跑 -# ls-remote 拿 tag 列表(worker / frontend 不直接调 git,但镜像统一一份) -FROM debian:bookworm-slim AS base +FROM debian:bookworm-slim +LABEL org.opencontainers.image.source="https://github.com/NickWilde18/pkgsitex" +LABEL org.opencontainers.image.description="Self-hosted pkgsite fork (worker/frontend/init in one image)" RUN apt-get update && \ apt-get install -y --no-install-recommends ca-certificates git && \ rm -rf /var/lib/apt/lists/* -# === target: worker(跑 cmd/worker 拉 module 入 DB)=== -FROM base AS worker COPY --from=builder /bin/worker /usr/local/bin/worker -COPY --from=builder /src/static /app/static -COPY --from=builder /src/migrations /app/migrations -WORKDIR /app -EXPOSE 8000 -ENTRYPOINT ["worker"] - -# === target: frontend(跑 cmd/frontend 渲染 godoc)=== -FROM base AS frontend COPY --from=builder /bin/frontend /usr/local/bin/frontend +COPY --from=builder /bin/pkgsitex-init /usr/local/bin/pkgsitex-init + +# 静态资源 / migrations / third_party 都内置——cmd/worker / cmd/frontend 都 +# 通过 -static / -third_party flag 指 /app 下,cmd/worker 内部跑 migrations +# 时也能找到 /app/migrations COPY --from=builder /src/static /app/static COPY --from=builder /src/third_party /app/third_party +COPY --from=builder /src/migrations /app/migrations WORKDIR /app -EXPOSE 8080 -ENTRYPOINT ["frontend"] -# === target: init(跑 cmd/pkgsitex-init 写 netrc + enqueue 全 module)=== -FROM base AS init -COPY --from=builder /bin/pkgsitex-init /usr/local/bin/pkgsitex-init -ENTRYPOINT ["pkgsitex-init"] +# 默认 entrypoint = frontend(用户最常用);compose 里其他 service 用 +# entrypoint: ["/usr/local/bin/worker"] / ["/usr/local/bin/pkgsitex-init"] 覆盖 +EXPOSE 8000 8080 +ENTRYPOINT ["/usr/local/bin/frontend"] diff --git a/deploy/prod/README.md b/deploy/prod/README.md index b86a55dd9..7437954d3 100644 --- a/deploy/prod/README.md +++ b/deploy/prod/README.md @@ -4,6 +4,8 @@ 跟仓库根的 `compose.yaml`(cmd/pkgsite local mode,单进程,文件 mount)不同——本目录是 prod 模式,跟 pkg.go.dev 自身架构一致。 +镜像 `nickwilde18/pkgsitex:fork-main` 由 GitHub Actions 自动 build & push 到 docker.io(推 `fork/main` 分支触发);用户拉 image 即用,不需要本地 build。 + ## 启动步骤 ```sh @@ -16,7 +18,7 @@ $EDITOR deploy/prod/.env # GITHUB_TOKEN=ghp_xxx # 2) 配要监控的 module 列表 $EDITOR deploy/prod/config.yaml # modules: [...] -# 3) 起 stack(首次 build ~10 分钟下 image + 编译) +# 3) 起 stack(首次 docker pull 镜像 ~30 秒) docker compose -f compose.prod.yaml --env-file deploy/prod/.env up -d # 4) 看日志确认 worker 在拉 module @@ -26,6 +28,20 @@ docker compose -f compose.prod.yaml logs -f worker open http://localhost:8089/pkgsitex/ ``` +## 切镜像版本 / 用本地 build + +`compose.prod.yaml` 默认用 `nickwilde18/pkgsitex:fork-main`,可通过 env 切: + +```sh +# 钉死某个 git sha(不可变 tag) +PKGSITEX_IMAGE=nickwilde18/pkgsitex:abc1234 docker compose -f compose.prod.yaml ... up -d + +# 用本地刚 build 的(开发 fork patch 时用) +docker build -t pkgsitex:local -f deploy/prod/Dockerfile . +PKGSITEX_IMAGE=pkgsitex:local PKGSITEX_PULL_POLICY=never \ + docker compose -f compose.prod.yaml ... up -d +``` + ## 运维操作 ### 加 / 删 module From c10eaff853f58fb500246362c2019fd974068c44 Mon Sep 17 00:00:00 2001 From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com> Date: Thu, 7 May 2026 23:53:41 +0800 Subject: [PATCH 18/26] =?UTF-8?q?fix(docker):=20builder=20=E7=94=A8=20gola?= =?UTF-8?q?ng:1.25=E2=80=94=E2=80=94upstream=20rebase=20=E5=90=8E=20go.mod?= =?UTF-8?q?=20=E8=A6=81=201.25.5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GitHub Actions build 失败: go: go.mod requires go >= 1.25.5 (running go 1.24.13; GOTOOLCHAIN=local) 升 deploy/prod/Dockerfile + 仓库根 Dockerfile 两处 FROM 到 golang:1.25。 --- Dockerfile | 4 ++-- deploy/prod/Dockerfile | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 27caf3e7f..428a253ad 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ # GOPROXY 默认走 goproxy.cn 应对国内网络环境;改 build arg / env 可换走 # 公司内网 Athens 之类。 -FROM golang:1.24 AS builder +FROM golang:1.25 AS builder WORKDIR /src # go.mod / go.sum 单独 COPY 让 dep 下载层可缓存(跟随 source 改动) @@ -23,7 +23,7 @@ RUN go mod download COPY . . RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /pkgsite ./cmd/pkgsite -FROM golang:1.24 +FROM golang:1.25 LABEL org.opencontainers.image.source="https://github.com/NickWilde18/pkgsitex" LABEL org.opencontainers.image.description="Self-hosted godoc browser (pkgsite fork) with base-path support" diff --git a/deploy/prod/Dockerfile b/deploy/prod/Dockerfile index f8ed3dfc1..c8e091bbc 100644 --- a/deploy/prod/Dockerfile +++ b/deploy/prod/Dockerfile @@ -8,7 +8,7 @@ # 直读文件系统,不走 DB 不走 GOPROXY;本镜像跑 cmd/worker / cmd/frontend, # 需要 PostgreSQL + athens GOPROXY,跟 pkg.go.dev 自身架构一致。 -FROM golang:1.24 AS builder +FROM golang:1.25 AS builder WORKDIR /src COPY go.mod go.sum ./ From e7d69bcf19d7473d514f09e161b2e82084f65765 Mon Sep 17 00:00:00 2001 From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com> Date: Fri, 8 May 2026 00:08:53 +0800 Subject: [PATCH 19/26] =?UTF-8?q?fix(worker):=20=E6=B3=A8=E5=86=8C=20abs?= =?UTF-8?q?=20template=20helper=E2=80=94=E2=80=94=20base-path=20patch=20?= =?UTF-8?q?=E6=BC=8F=E6=94=B9=E5=AF=BC=E8=87=B4=20worker=20=E5=90=AF?= =?UTF-8?q?=E5=8A=A8=E5=B4=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 启动 cmd/worker 时报 'template: index.tmpl:10: function "abs" not defined': fork base-path patch 把 static/worker/index.tmpl 里 URL prefix 改 {{abs ...}}, 但 internal/worker/server.go 的 FuncMap 没注册 abs。 worker UI 只给运维看不走前端反代——abs 直接 identity 返原值即可,省把 -base-path flag 重复连通到 cmd/worker。 --- internal/worker/server.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/worker/server.go b/internal/worker/server.go index 468741312..f86eeb08d 100644 --- a/internal/worker/server.go +++ b/internal/worker/server.go @@ -827,6 +827,10 @@ func parseTemplate(cfg *config.Config, staticPath template.TrustedSource, filena return t1.Sub(t2).Round(time.Second) }, "logURL": logURL, + // fork 改 base-path 时把 worker index.tmpl 里 hardcoded URL prefix + // 全替成 {{abs ...}},worker 端 UI 只给运维看不走前端反代——abs 直接 + // identity 返原值即可,省 -base-path flag 重复连通到 cmd/worker。 + "abs": func(p string) string { return p }, }).ParseFilesFromTrustedSources(templatePath) } From 2641fa20e81513a520b43e59aab2b17c7f25bce5 Mon Sep 17 00:00:00 2001 From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com> Date: Fri, 8 May 2026 00:12:54 +0800 Subject: [PATCH 20/26] =?UTF-8?q?fix(docker):=20image=20=E5=8A=A0=20wget?= =?UTF-8?q?=E2=80=94=E2=80=94compose=20healthcheck=20=E6=8E=A2=E9=92=88?= =?UTF-8?q?=E7=BC=BA=20binary?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit worker / athens / frontend 三个 service 的 compose.prod.yaml healthcheck 都 用 wget 探 /healthcheck endpoint,但 debian-bookworm-slim base 默认不带 wget, 导致容器在跑但 healthcheck 永远 unhealthy。 --- deploy/prod/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/prod/Dockerfile b/deploy/prod/Dockerfile index c8e091bbc..e32ff2df8 100644 --- a/deploy/prod/Dockerfile +++ b/deploy/prod/Dockerfile @@ -25,7 +25,7 @@ FROM debian:bookworm-slim LABEL org.opencontainers.image.source="https://github.com/NickWilde18/pkgsitex" LABEL org.opencontainers.image.description="Self-hosted pkgsite fork (worker/frontend/init in one image)" RUN apt-get update && \ - apt-get install -y --no-install-recommends ca-certificates git && \ + apt-get install -y --no-install-recommends ca-certificates git wget && \ rm -rf /var/lib/apt/lists/* COPY --from=builder /bin/worker /usr/local/bin/worker From 0f6ced898e592d4c823a073fb0610e942e8f9d80 Mon Sep 17 00:00:00 2001 From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com> Date: Fri, 8 May 2026 00:18:43 +0800 Subject: [PATCH 21/26] =?UTF-8?q?fix(compose):=20worker=20healthcheck=20pa?= =?UTF-8?q?th=20/healthcheck=20=E2=86=92=20/healthz?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit worker mux 注册的健康路径是 /healthz([`internal/worker/server.go`]), 之前 compose healthcheck 写成 /healthcheck 永远 404 → 容器跑得起来但 status 卡在 (health: starting) 直到 retries=30 耗尽。 --- compose.prod.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compose.prod.yaml b/compose.prod.yaml index 6c050c084..29454b935 100644 --- a/compose.prod.yaml +++ b/compose.prod.yaml @@ -99,7 +99,7 @@ services: athens: condition: service_healthy healthcheck: - test: ["CMD", "wget", "-q", "-O", "-", "http://localhost:8000/healthcheck"] + test: ["CMD", "wget", "-q", "-O", "-", "http://localhost:8000/healthz"] interval: 10s timeout: 5s retries: 30 @@ -134,7 +134,7 @@ services: # init 一次性跑:写 athens netrc + 等 worker 起来 + enqueue 全 module。 # athens 依赖 init(要 netrc 文件),所以 init 要先于 athens 跑——但 - # init 又要等 worker /healthcheck —— worker 又依赖 athens —— 循环依赖。 + # init 又要等 worker /healthz —— worker 又依赖 athens —— 循环依赖。 # # 解法:分两阶段—— # 阶段 1(service netrc-writer):仅写 netrc 然后退出,依赖 0。athens 等它 From ea4ab9d2a3a2113bb70f924594881b5e84347de4 Mon Sep 17 00:00:00 2001 From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com> Date: Fri, 8 May 2026 00:42:59 +0800 Subject: [PATCH 22/26] =?UTF-8?q?feat(frontend):=20-base-path=20/=20-show-?= =?UTF-8?q?unexported=20flag=E2=80=94=E2=80=94=E5=AD=90=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=20/=20unexported=20=E7=9C=9F=E8=B5=B7=E4=BD=9C=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 现在为止 PROD_DEPLOY 的 base-path / show-unexported / license-permissive 是 env var 占位,cmd/frontend 不读——挂 /pkgsitex 子路径完全没生效。 这次: - cmd/frontend/main.go 加 -base-path / -show-unexported flag,照搬 cmd/pkgsite/main.go 的语义:validateBasePath + 设 godoc.{IncludeUnexported, BasePath} + versions.BasePath + ServerConfig.BasePath - compose.prod.yaml frontend command 改成传真 flag(-base-path=/pkgsitex / -show-unexported / -bypass_license_check),删掉旧 env var 占位 license-permissive 复用上游已有 -bypass_license_check,不再单独包装。 worker 不暴露 base-path 段(运维直连),cmd/worker 暂不加 flag——abs template helper 走 identity 已够。 --- cmd/frontend/main.go | 39 +++++++++++++++++++++++++++++++++++++++ compose.prod.yaml | 12 +++++------- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/cmd/frontend/main.go b/cmd/frontend/main.go index 1684981f8..88a3fc9dd 100644 --- a/cmd/frontend/main.go +++ b/cmd/frontend/main.go @@ -8,8 +8,10 @@ package main import ( "context" "flag" + "fmt" "net/http" "os" + "strings" "time" "cloud.google.com/go/profiler" @@ -26,6 +28,8 @@ import ( "golang.org/x/pkgsite/internal/fetchdatasource" "golang.org/x/pkgsite/internal/frontend" "golang.org/x/pkgsite/internal/frontend/fetchserver" + "golang.org/x/pkgsite/internal/frontend/versions" + "golang.org/x/pkgsite/internal/godoc" "golang.org/x/pkgsite/internal/log" "golang.org/x/pkgsite/internal/middleware" "golang.org/x/pkgsite/internal/middleware/timeout" @@ -56,11 +60,45 @@ var ( bypassLicenseCheck = flag.Bool("bypass_license_check", false, "display all information, even for non-redistributable paths") hostAddr = flag.String("host", "localhost:8080", "Host address for the server") queueType = flag.String("queue", "inmemory", `queue implementation when not on GCP: "inmemory" or "postgres"`) + // fork: 把 frontend 整体挂在 URL 子路径下(如 -base-path=/pkgsitex), + // 跟 cmd/pkgsite local mode 同款语义。空 = 挂根(上游默认行为)。 + basePath = flag.String("base-path", "", "URL prefix to mount the site under (e.g. /pkgsitex). Must start with / and not end with /.") + // fork: godoc 渲染保留未导出符号(doc.AllDecls 模式),跟 cmd/pkgsite 同款。 + // 内网部署常见诉求——自家代码完整展示比 public-only 视图更有用。 + showUnexported = flag.Bool("show-unexported", false, "Render documentation including unexported declarations (doc.AllDecls mode).") ) +// validateBasePath 强制 base-path 形如 "/foo" 或空——空表示挂根。 +// 不允许尾部斜杠(mux pattern 拼起来会双斜杠引发匹配失败)。复制自 +// cmd/pkgsite/main.go——两个 cmd 各自校验避免引入 cmd/internal 共享层。 +func validateBasePath(p string) error { + if p == "" { + return nil + } + if !strings.HasPrefix(p, "/") { + return fmt.Errorf("-base-path %q must start with /", p) + } + if strings.HasSuffix(p, "/") { + return fmt.Errorf("-base-path %q must not end with /", p) + } + return nil +} + func main() { flag.Parse() ctx := context.Background() + + if err := validateBasePath(*basePath); err != nil { + log.Fatalf(ctx, "%v", err) + } + // 全局开关——godoc.DocPackage / dochtml 模板读包级 var 决定 unexported + // 显隐 + URL 子路径前缀。同 cmd/pkgsite 一种行为,没必要加 ServerConfig 字段层层透传。 + godoc.IncludeUnexported = *showUnexported + godoc.BasePath = *basePath + // versions 包的 ConstructUnitURL 是所有 unit / package / module 详情页链接 + // 的核心 URL builder——空 BasePath 时 no-op 跟上游一致。 + versions.BasePath = *basePath + cfg, err := serverconfig.Init(ctx) if err != nil { log.Fatal(ctx, err) @@ -201,6 +239,7 @@ func main() { VulndbClient: vc, HTTPClient: &http.Client{Transport: new(ochttp.Transport)}, RecordCodeWikiMetrics: dcensus.RecordClick, + BasePath: *basePath, }) if err != nil { log.Fatalf(ctx, "frontend.NewServer: %v", err) diff --git a/compose.prod.yaml b/compose.prod.yaml index 29454b935..c96e94f4d 100644 --- a/compose.prod.yaml +++ b/compose.prod.yaml @@ -115,16 +115,14 @@ services: GO_DISCOVERY_DATABASE_PASSWORD: pkgsitex GO_DISCOVERY_DATABASE_NAME: discovery_db PORT: "8080" - # fork 加的:base-path / show-unexported / license permissive 都通过 env - # 或 cmd flag——cmd/frontend 暂未支持这些 flag,需要 fork patch 让它读 - # 同 cmd/pkgsite 的 flag。L2 暂走 cmd/pkgsite 行为兼容(待 L4-bis 补 - # cmd/frontend flag 支持,见 docs/PROD_DEPLOY.md TODO)。 - PKGSITEX_BASE_PATH: "/pkgsitex" - PKGSITEX_SHOW_UNEXPORTED: "true" - PKGSITEX_LICENSE_PERMISSIVE: "true" command: - "-static=/app/static" - "-third_party=/app/third_party" + # fork 加的子路径 / unexported / license——直接走 cmd/frontend flag。 + # license 用上游已有 -bypass_license_check 不再单独包装。 + - "-base-path=/pkgsitex" + - "-show-unexported" + - "-bypass_license_check" ports: - "8089:8080" depends_on: From 6753ce085b291f81e6c4ad18cd95282b32cfc420 Mon Sep 17 00:00:00 2001 From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com> Date: Fri, 8 May 2026 00:43:01 +0800 Subject: [PATCH 23/26] =?UTF-8?q?fix(init):=20waitWorkerReady=20=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=20/healthcheck=20=E2=86=92=20/healthz?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit worker mux 注册的健康路径是 /healthz——init container 用 /healthcheck 永远 404,落到 ctx 超时整个流程才 fail。和前几个提交里 compose healthcheck 同 源 typo。 --- cmd/pkgsitex-init/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/pkgsitex-init/main.go b/cmd/pkgsitex-init/main.go index 23dfed0b4..a8564f1e0 100644 --- a/cmd/pkgsitex-init/main.go +++ b/cmd/pkgsitex-init/main.go @@ -172,7 +172,7 @@ func writeNetrc(path, token string) error { return os.WriteFile(path, []byte(content), 0o600) } -// waitWorkerReady 轮询 worker /healthcheck 端点直到就绪或 ctx 超时。 +// waitWorkerReady 轮询 worker /healthz 端点直到就绪或 ctx 超时。 // // 间隔 2 秒;总超时由 caller ctx 控制。docker-compose 的 healthcheck 应该 // 已让 worker 在 init 启动前就 ready,但这层兜底防容器启动顺序 race。 @@ -180,7 +180,7 @@ func waitWorkerReady(ctx context.Context, workerURL string) error { tick := time.NewTicker(2 * time.Second) defer tick.Stop() for { - req, _ := http.NewRequestWithContext(ctx, http.MethodGet, workerURL+"/healthcheck", nil) + req, _ := http.NewRequestWithContext(ctx, http.MethodGet, workerURL+"/healthz", nil) resp, err := http.DefaultClient.Do(req) if err == nil { io.Copy(io.Discard, resp.Body) From b50a5f7244a49855a3d12867c6b11e9862601d5a Mon Sep 17 00:00:00 2001 From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com> Date: Fri, 8 May 2026 00:43:01 +0800 Subject: [PATCH 24/26] =?UTF-8?q?ci(docker):=20=E5=A4=9A=20arch=20image?= =?UTF-8?q?=E2=80=94=E2=80=94=E5=8A=A0=20linux/arm64=20=E7=BB=99=20macOS?= =?UTF-8?q?=20/=20=E6=A0=91=E8=8E=93=E6=B4=BE=20native=20=E8=B7=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 之前只发 linux/amd64,macOS arm64 host 直接 docker pull no manifest, 解法是 docker-compose 加 platform: linux/amd64 走 Rosetta emulation。这次 build-push-action 加 platforms: linux/amd64,linux/arm64 + setup-qemu-action, 两个 arch 都直接 native,下游 docker-compose 不需要再声明 platform 字段。 --- .github/workflows/docker.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 882e8b528..8b8456521 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -54,6 +54,11 @@ jobs: type=raw,value=fork-main,enable=${{ github.ref == 'refs/heads/fork/main' }} type=sha,format=short,prefix= + - name: Set up QEMU + # 跨 arch build 需要 binfmt——QEMU emulation 让 amd64 runner 能交叉 + # 编出 arm64 二进制(buildx + Go 交叉编译速度差距很小) + uses: docker/setup-qemu-action@v3 + - name: Build and push uses: docker/build-push-action@v6 with: @@ -63,6 +68,9 @@ jobs: push: ${{ github.event_name == 'push' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + # 多 arch:macOS arm64 / 国产 CPU / 树莓派都能直接 docker pull + # native,省 Rosetta emulation;amd64 K8s prod 节点也照样 native + platforms: linux/amd64,linux/arm64 # 用 GitHub Actions cache 加速重 build cache-from: type=gha cache-to: type=gha,mode=max From 40bae06303a956273fe3a2085f3a7461441f775a Mon Sep 17 00:00:00 2001 From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com> Date: Fri, 8 May 2026 12:35:38 +0800 Subject: [PATCH 25/26] =?UTF-8?q?docs:=20=E9=BB=98=E8=AE=A4=20README=20?= =?UTF-8?q?=E6=94=B9=E8=8B=B1=E6=96=87=EF=BC=8C=E4=B8=AD=E6=96=87=E7=89=88?= =?UTF-8?q?=E5=88=86=E5=88=B0=20README.zh-CN.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GitHub 主页和搜索结果默认抓 README.md 显示——开源仓库默认英文更适合 全球用户首次接触;中文文档对内部团队仍重要,单独分到 README.zh-CN.md 保留全部内容,两边顶部互链。 顺手把"前置 Go 1.24+" / "build 拉 golang:1.24 image"两处过期版本文案 同步成 1.25(fork c10eaff8 已升 builder 到 golang:1.25)。 --- README.md | 157 +++++++++++++++++++++-------------------- README.zh-CN.md | 181 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 261 insertions(+), 77 deletions(-) create mode 100644 README.zh-CN.md diff --git a/README.md b/README.md index 23fd0a7f1..5e0643434 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,21 @@ # pkgsitex -[`golang/pkgsite`](https://github.com/golang/pkgsite) 的 fork,加了内网部署 / 私有仓库浏览常用能力——`-base-path` 子路径、未导出符号显示 + 浏览器 toggle、godoc 注释 markdown 扩展(` `code` ` / `**bold**` / mermaid 围栏)、view source 走本地 file mux 等。 +> English | [简体中文](README.zh-CN.md) -> **Fork 维护策略**:master 永远 = upstream master,patches 落在 long-lived branch [`fork/main`](https://github.com/NickWilde18/pkgsitex/tree/fork/main)。GitHub "Sync fork" 永远 fast-forward 0 conflict。所有用法 / 启动 / patch 详见 fork/main 分支的 README。 +A fork of [`golang/pkgsite`](https://github.com/golang/pkgsite) tuned for self-hosting Go module docs in private/internal environments. Adds URL subpath mounting, godoc comment markdown (mermaid / inline code / bold), unexported symbol display + browser toggle, view source via locally mounted files, and config-driven private GitHub repo support. + +> **Fork strategy**: master always equals upstream master; all patches live on the long-lived [`fork/main`](https://github.com/NickWilde18/pkgsitex/tree/fork/main) branch. GitHub "Sync fork" stays fast-forward, zero conflict. All usage / setup / patch docs live on `fork/main` — including this README. ```sh git clone https://github.com/NickWilde18/pkgsitex.git ~/Repo/pkgsitex cd ~/Repo/pkgsitex -git checkout fork/main # 切到 fork patches 分支 -cat README.md # ← 你看到的 fork 说明 +git checkout fork/main # switch to the fork patches branch +cat README.md # ← what you're reading ``` -## 快速启动(本地,秒级) +## Quick start (local, sub-second) -前置:Go 1.24+,本地把要浏览的 repo clone 到 `~/Repo/`。 +Requires Go 1.25+. Clone the repos you want to browse to `~/Repo/`. ```sh go run ./cmd/pkgsite -base-path=/pkgsitex -show-unexported -http=:8089 \ @@ -26,131 +28,132 @@ go run ./cmd/pkgsite -base-path=/pkgsitex -show-unexported -http=:8089 \ open http://localhost:8089/pkgsitex/ ``` -或者编译一次本地复用: +Or compile once and reuse locally: ```sh go install ./cmd/pkgsite pkgsite -base-path=/pkgsitex -show-unexported -http=:8089 ~/Repo/Chat ... ``` -首次 `go run` 拉 esbuild / safehtml 等 dep 走 GOPROXY,约 30 秒,后续秒启。 +The first `go run` fetches `esbuild` / `safehtml` deps via GOPROXY (~30s); subsequent runs are instant. -## docker-compose 启动(备选,给没 Go toolchain 的同事) +## docker-compose start (no Go toolchain required) -仓库根有 [`compose.yaml`](compose.yaml): +The repo root has [`compose.yaml`](compose.yaml): ```sh docker compose up -d open http://localhost:8089/pkgsitex/ ``` -首次 build 拉 `golang:1.24` image(~700 MB) + esbuild bundle,约 3-5 分钟。改 `fork/main` 后 `docker compose build pkgsitex` 增量很快。 +The first build pulls the `golang:1.25` image (~700 MB) and bundles esbuild — about 3-5 minutes. After editing `fork/main`, `docker compose build pkgsitex` is a fast incremental rebuild. -## 配置选项 +## Configuration -| flag | 用途 | 默认 | +| flag | purpose | default | |---|---|---| -| `-base-path=/pkgsitex` | 站点挂子路径下,反代 / 共享域名场景。空 = 挂根(pkg.go.dev 行为) | `""` | -| `-show-unexported` | godoc 渲染保留未导出符号;浏览器用 toggle 控制显隐 | `false` | -| `-http=:8089` | 监听端口 | `localhost:8080` | -| `-cache` | 走 GOMODCACHE | `false` | -| `-proxy` | 走 GOPROXY 拉远程 module | `false` | -| `...` | 要索引的 Go module 路径(多个) | `.` | +| `-base-path=/pkgsitex` | mount the site under a URL subpath (reverse-proxy / shared-domain scenarios). Empty = mount at root (pkg.go.dev behavior) | `""` | +| `-show-unexported` | godoc renders unexported declarations; the browser uses a toggle to control visibility | `false` | +| `-http=:8089` | listen address | `localhost:8080` | +| `-cache` | use GOMODCACHE | `false` | +| `-proxy` | use GOPROXY to fetch remote modules | `false` | +| `...` | Go module paths to index (multiple) | `.` | -完整 flag:`go run ./cmd/pkgsite -h`。 +Full flags: `go run ./cmd/pkgsite -h`. -## 增 / 减仓库 +## Adding / removing repos -**本地 binary**:直接改 command 末尾 path 列表。 +**Local binary**: edit the path list at the end of the command. -**docker-compose**:编辑 [`compose.yaml`](compose.yaml) `volumes:` 加一行 `../:/repos/:ro`,`command:` 末尾加 `/repos/`。 +**docker-compose**: edit [`compose.yaml`](compose.yaml) — add a `volumes:` entry `../:/repos/:ro` plus a `command:` arg `/repos/`. -约定:内部仓库都 clone 在 `~/Repo/`。UniAuth 是 monorepo 含 `uniauth-gf/` + `ittools_sync/` 两个 Go module,各 mount 一份——pkgsite 不支持 nested module 自动发现。 +Convention: clone internal repos to `~/Repo/`. UniAuth is a monorepo with two Go modules (`uniauth-gf/` + `ittools_sync/`) — mount each separately; pkgsite doesn't auto-discover nested modules. -## 浏览体验 +## Browse experience -| 元素 | 行为 | +| Element | Behavior | |---|---| | URL `/pkgsitex/` | module overview | -| URL `/pkgsitex//` | 子包 | -| URL `/pkgsitex/@` | 指定 git tag(local mode 走 module cache,prod worker 模式才有完整 tag 历史) | -| **Show unexported** button | Index 标题旁,切私有 declaration / 侧边栏 / index 链接的显隐,状态 localStorage 跨页保留 | -| **Show internal directories** button | 上游内置,切 `internal/` 子目录显示。`data-local=true` 时自动开启 | -| ` `code` ` / `**bold**` / ` ```mermaid ` | godoc 注释里这些 markdown 写法都渲染(fork 加的 dochtml ext) | -| **View Source** | 包详情页每个声明右侧链接,跳本地 mount 的源码文件(不依赖 GitHub 在线访问) | +| URL `/pkgsitex//` | subpackage | +| URL `/pkgsitex/@` | specific git tag (local mode reads from module cache; the prod worker mode has full tag history) | +| **Show unexported** button | next to the Index header — toggles visibility of private declarations / sidebar / index links; state persists in localStorage across pages | +| **Show internal directories** button | upstream feature — toggles `internal/` subdirectory display. Auto-enabled when `data-local=true` | +| ` `code` ` / `**bold**` / ` ```mermaid ` | godoc comments render these markdown forms (the fork's dochtml ext) | +| **View Source** | a link next to each declaration on the package detail page — jumps to the locally mounted source file (no GitHub access required) | -## Patch 集合(fork 改了什么) +## Patches (what the fork changes) -- **`-base-path`**:URL 子路径前缀,所有 mux pattern / template helper / godoc cross-reference / view source 自动 prefix -- **`-show-unexported`**:让 `internal/fetch/load.go` 在 AST 阶段保留 unexported FuncDecl + `doc.NewFromFiles` 用 `doc.AllDecls` -- **godoc markdown ext**(`internal/godoc/dochtml/internal/render/markdown_ext.go`):post-process HTML 加 inline code / bold / mermaid fence 识别 -- **mermaid client lazy-load**(`static/frontend/frontend.tmpl`):页面有 `code.language-mermaid` 才动态 import mermaid@10 -- **unexported toggle**(`static/frontend/unit/main/main.ts`):client-side hide + button + localStorage -- **view source 本地 file mux** + base path 拼接修 -- **multi-repo command line**:直接 list 多个 module path -- **trailing slash 死循环修**:`internal/frontend/details.go` 在 base path 模式下区分"挂根"和"base path 自身" +- **`-base-path`**: URL subpath prefix — all mux patterns / template helpers / godoc cross-references / view source links are auto-prefixed +- **`-show-unexported`**: makes `internal/fetch/load.go` keep unexported `FuncDecl`s during AST processing, and `doc.NewFromFiles` uses `doc.AllDecls` +- **godoc markdown ext** (`internal/godoc/dochtml/internal/render/markdown_ext.go`): post-processes HTML to recognize inline code / bold / mermaid fences +- **mermaid client lazy-load** (`static/frontend/frontend.tmpl`): dynamically imports `mermaid@10` only when a page contains `code.language-mermaid` +- **unexported toggle** (`static/frontend/unit/main/main.ts`): client-side hide + button + localStorage +- **view source local file mux** + base path prefix fix +- **multi-repo command line**: list multiple module paths in one command +- **trailing-slash redirect-loop fix**: `internal/frontend/details.go` distinguishes "mounted at root" from "the base path itself" in base-path mode +- **prod 4-piece stack**: postgres / athens / worker / frontend `compose.prod.yaml` plus a `cmd/pkgsitex-init` bootstrap container that writes athens netrc + enqueues modules to the worker -## Prod 部署 +## Prod deployment -Prod 模式跟 dev 不同——跟 pkg.go.dev 自身架构一致 4 件套: +Prod mode differs from dev — it runs the same 4-component architecture as pkg.go.dev itself: -- **postgres**:缓存 module 索引 / 包文档 -- **athens**:GOPROXY 缓存 + 用 GitHub PAT 拉私有 repo -- **worker**:异步 fetch module 入 DB -- **frontend**:从 DB 读渲染(端口 8089) +- **postgres**: caches module index / package docs +- **athens**: GOPROXY cache + private repo fetch via GitHub PAT +- **worker**: async fetches modules into the DB +- **frontend**: renders from the DB (port 8089) -镜像已发布到 docker.io [`nickwilde18/pkgsitex:fork-main`](https://hub.docker.com/r/nickwilde18/pkgsitex)(GitHub Actions 自动 push)。用户不需要 build——拉 image 即用。 +The image is published to docker.io: [`nickwilde18/pkgsitex:fork-main`](https://hub.docker.com/r/nickwilde18/pkgsitex) (multi-arch `linux/amd64` + `linux/arm64`, auto-pushed by GitHub Actions). Users don't need to build — pull the image and run. ```sh git clone https://github.com/NickWilde18/pkgsitex.git cd pkgsitex git checkout fork/main -# 1) 配 PAT +# 1) configure PAT cp deploy/prod/.env.example deploy/prod/.env $EDITOR deploy/prod/.env # GITHUB_TOKEN=ghp_xxx -# 2) 编辑要监控的 module 列表(内置 5 个 CUHKSZ 内部 repo 示例) +# 2) edit the module list (5 sample CUHKSZ internal repos pre-listed) $EDITOR deploy/prod/config.yaml -# 3) 起 stack(自动拉镜像) +# 3) start the stack (image is auto-pulled) docker compose -f compose.prod.yaml --env-file deploy/prod/.env up -d -# 4) 浏览 +# 4) browse open http://localhost:8089/pkgsitex/ ``` -完整运维(加 / 删 module、周期 cron 刷新、故障排查):[`deploy/prod/README.md`](deploy/prod/README.md)。 +Full ops (add/remove modules, periodic cron refresh, troubleshooting): [`deploy/prod/README.md`](deploy/prod/README.md). -支持 multi-version(git tag 切换)、license permissive(私有 / proprietary 也渲)、配置文件驱动。 +Supports multi-version (git tag switching), license-permissive rendering (proprietary too), and config-driven module list. -## 维护策略 +## Maintenance strategy -- **master 永远 = upstream master**——不 merge `fork/main` 进 master。GitHub "Sync fork" 永远 fast-forward 0 conflict -- **`fork/main` 是 long-lived patch branch**——所有 fork 改动累积在此分支 -- **月度 rebase**:AI 跑 `git rebase upstream/master` 让 `fork/main` 跟随上游新进展。预测 conflict 集中在: - - `internal/frontend/server.go` 的 mux 列表(上游新加路由时) - - `static/**/*.tmpl` / `*.ts`(上游改样式 / 加交互时) - - 其他 fork 文件(如 `markdown_ext.go`、`base-path/base-path.ts`)几乎不会 conflict(fork 独有) +- **master always equals upstream master** — never merge `fork/main` into master. GitHub "Sync fork" stays fast-forward, zero conflict +- **`fork/main` is a long-lived patch branch** — all fork changes accumulate here +- **Monthly rebase**: AI runs `git rebase upstream/master` so `fork/main` follows new upstream development. Predicted conflict hot spots: + - `internal/frontend/server.go` mux list (when upstream adds new routes) + - `static/**/*.tmpl` / `*.ts` (when upstream changes styles / interactions) + - other fork-only files (e.g. `markdown_ext.go`, `base-path/base-path.ts`) almost never conflict (fork-exclusive) -历次 patch 详见 PR [#2](https://github.com/NickWilde18/pkgsitex/pull/2)。 +Patch history: PR [#2](https://github.com/NickWilde18/pkgsitex/pull/2). -## 故障排查 +## Troubleshooting -| 现象 | 排查 | +| Symptom | Diagnosis | |---|---| -| `go run` 拉 dep 失败 | `GOPROXY=https://goproxy.cn,direct go run ...`(Dockerfile 默认 goproxy.cn) | -| docker `pull access denied` | 用 `docker compose build pkgsitex` 走本地 build,不要拉 image | -| 浏览 `/pkgsitex/...` 静态资源 404 | 改了 `static/`/模板后没重 build:`go run ./devtools/cmd/static` 重生 bundle,或 docker 路径 `docker compose build pkgsitex` | -| 解析 module 超时 | 走公司 Athens:`docker compose build --build-arg GOPROXY=https://athens.corp.com pkgsitex` 或本地 `export GOPROXY=...` | -| mermaid 不渲染 | 浏览器 console 看 `mermaid load failed`——内网拦了 jsdelivr CDN,需要本地 host mermaid(`third_party/mermaid/` 待 vendor) | -| Sidebar / Index 没"Show unexported"按钮 | 可能 localStorage 之前 toggle key 是老名字(`gogodocs:showUnexported`),点一次按钮即可重置成新 key | +| `go run` fails to fetch deps | `GOPROXY=https://goproxy.cn,direct go run ...` (the Dockerfile defaults to goproxy.cn) | +| docker `pull access denied` | use `docker compose build pkgsitex` to build locally instead of pulling | +| `/pkgsitex/...` static asset 404 | you edited `static/`/templates without rebuilding: run `go run ./devtools/cmd/static` to regenerate the bundle, or `docker compose build pkgsitex` | +| module resolution timeout | use the company Athens: `docker compose build --build-arg GOPROXY=https://athens.corp.com pkgsitex`, or set `GOPROXY=...` locally | +| mermaid doesn't render | check the browser console for `mermaid load failed` — your network blocks jsdelivr CDN; vendor mermaid locally (`third_party/mermaid/` pending) | +| Sidebar / Index has no "Show unexported" button | localStorage may have an old toggle key (`gogodocs:showUnexported`); click the button once to reset to the new key | --- -## 上游 pkgsite +## Upstream pkgsite -下游 fork 需要跟随上游进展,保留上游 README 内容如下,方便核对功能 / 升级时定位差异。 +A downstream fork has to follow upstream development — keeping the upstream README content below for cross-checking features / locating differences during upgrades. # golang.org/x/pkgsite @@ -160,19 +163,19 @@ server program. [![Go Reference](https://pkg.go.dev/badge/golang.org/x/pkgsite.svg)](https://pkg.go.dev/golang.org/x/pkgsite) -完整上游 README:[golang/pkgsite README](https://github.com/golang/pkgsite#readme)。 +Full upstream README: [golang/pkgsite README](https://github.com/golang/pkgsite#readme). ## Issues -Fork 自身 issues(base-path / markdown ext / etc):[NickWilde18/pkgsitex/issues](https://github.com/NickWilde18/pkgsitex/issues)。 +Fork issues (base-path / markdown ext / etc): [NickWilde18/pkgsitex/issues](https://github.com/NickWilde18/pkgsitex/issues). -上游 pkgsite issues 报到 [`golang/go`](https://golang.org/issues),前缀 `x/pkgsite:`,详见上游 README。 +Upstream pkgsite issues go to [`golang/go`](https://golang.org/issues), prefixed `x/pkgsite:` — see the upstream README. ## Contributing -Fork 内部 PR 投到 `fork/main` 分支(不进 master)。 +Fork-internal PRs target the `fork/main` branch (not master). -上游 pkgsite contribution flow(gerrit code review)见 [Contribution Guide](https://golang.org/doc/contribute.html) + [上游 README contributing 段](https://github.com/golang/pkgsite#contributing)。 +Upstream pkgsite contribution flow (Gerrit code review): [Contribution Guide](https://golang.org/doc/contribute.html) + [upstream README contributing section](https://github.com/golang/pkgsite#contributing). ## License diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 000000000..7a9a30702 --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,181 @@ +# pkgsitex + +> [English](README.md) | 简体中文 + +[`golang/pkgsite`](https://github.com/golang/pkgsite) 的 fork,加了内网部署 / 私有仓库浏览常用能力——`-base-path` 子路径、未导出符号显示 + 浏览器 toggle、godoc 注释 markdown 扩展(` `code` ` / `**bold**` / mermaid 围栏)、view source 走本地 file mux 等。 + +> **Fork 维护策略**:master 永远 = upstream master,patches 落在 long-lived branch [`fork/main`](https://github.com/NickWilde18/pkgsitex/tree/fork/main)。GitHub "Sync fork" 永远 fast-forward 0 conflict。所有用法 / 启动 / patch 详见 fork/main 分支的 README。 + +```sh +git clone https://github.com/NickWilde18/pkgsitex.git ~/Repo/pkgsitex +cd ~/Repo/pkgsitex +git checkout fork/main # 切到 fork patches 分支 +cat README.md # ← 你看到的 fork 说明 +``` + +## 快速启动(本地,秒级) + +前置:Go 1.25+,本地把要浏览的 repo clone 到 `~/Repo/`。 + +```sh +go run ./cmd/pkgsite -base-path=/pkgsitex -show-unexported -http=:8089 \ + ~/Repo/Chat \ + ~/Repo/Doubao-Speech-Service \ + ~/Repo/UniAuth/uniauth-gf \ + ~/Repo/UniAuth/ittools_sync \ + ~/Repo/open-platform + +open http://localhost:8089/pkgsitex/ +``` + +或者编译一次本地复用: + +```sh +go install ./cmd/pkgsite +pkgsite -base-path=/pkgsitex -show-unexported -http=:8089 ~/Repo/Chat ... +``` + +首次 `go run` 拉 esbuild / safehtml 等 dep 走 GOPROXY,约 30 秒,后续秒启。 + +## docker-compose 启动(备选,给没 Go toolchain 的同事) + +仓库根有 [`compose.yaml`](compose.yaml): + +```sh +docker compose up -d +open http://localhost:8089/pkgsitex/ +``` + +首次 build 拉 `golang:1.25` image(~700 MB) + esbuild bundle,约 3-5 分钟。改 `fork/main` 后 `docker compose build pkgsitex` 增量很快。 + +## 配置选项 + +| flag | 用途 | 默认 | +|---|---|---| +| `-base-path=/pkgsitex` | 站点挂子路径下,反代 / 共享域名场景。空 = 挂根(pkg.go.dev 行为) | `""` | +| `-show-unexported` | godoc 渲染保留未导出符号;浏览器用 toggle 控制显隐 | `false` | +| `-http=:8089` | 监听端口 | `localhost:8080` | +| `-cache` | 走 GOMODCACHE | `false` | +| `-proxy` | 走 GOPROXY 拉远程 module | `false` | +| `...` | 要索引的 Go module 路径(多个) | `.` | + +完整 flag:`go run ./cmd/pkgsite -h`。 + +## 增 / 减仓库 + +**本地 binary**:直接改 command 末尾 path 列表。 + +**docker-compose**:编辑 [`compose.yaml`](compose.yaml) `volumes:` 加一行 `../:/repos/:ro`,`command:` 末尾加 `/repos/`。 + +约定:内部仓库都 clone 在 `~/Repo/`。UniAuth 是 monorepo 含 `uniauth-gf/` + `ittools_sync/` 两个 Go module,各 mount 一份——pkgsite 不支持 nested module 自动发现。 + +## 浏览体验 + +| 元素 | 行为 | +|---|---| +| URL `/pkgsitex/` | module overview | +| URL `/pkgsitex//` | 子包 | +| URL `/pkgsitex/@` | 指定 git tag(local mode 走 module cache,prod worker 模式才有完整 tag 历史) | +| **Show unexported** button | Index 标题旁,切私有 declaration / 侧边栏 / index 链接的显隐,状态 localStorage 跨页保留 | +| **Show internal directories** button | 上游内置,切 `internal/` 子目录显示。`data-local=true` 时自动开启 | +| ` `code` ` / `**bold**` / ` ```mermaid ` | godoc 注释里这些 markdown 写法都渲染(fork 加的 dochtml ext) | +| **View Source** | 包详情页每个声明右侧链接,跳本地 mount 的源码文件(不依赖 GitHub 在线访问) | + +## Patch 集合(fork 改了什么) + +- **`-base-path`**:URL 子路径前缀,所有 mux pattern / template helper / godoc cross-reference / view source 自动 prefix +- **`-show-unexported`**:让 `internal/fetch/load.go` 在 AST 阶段保留 unexported FuncDecl + `doc.NewFromFiles` 用 `doc.AllDecls` +- **godoc markdown ext**(`internal/godoc/dochtml/internal/render/markdown_ext.go`):post-process HTML 加 inline code / bold / mermaid fence 识别 +- **mermaid client lazy-load**(`static/frontend/frontend.tmpl`):页面有 `code.language-mermaid` 才动态 import mermaid@10 +- **unexported toggle**(`static/frontend/unit/main/main.ts`):client-side hide + button + localStorage +- **view source 本地 file mux** + base path 拼接修 +- **multi-repo command line**:直接 list 多个 module path +- **trailing slash 死循环修**:`internal/frontend/details.go` 在 base path 模式下区分"挂根"和"base path 自身" + +## Prod 部署 + +Prod 模式跟 dev 不同——跟 pkg.go.dev 自身架构一致 4 件套: + +- **postgres**:缓存 module 索引 / 包文档 +- **athens**:GOPROXY 缓存 + 用 GitHub PAT 拉私有 repo +- **worker**:异步 fetch module 入 DB +- **frontend**:从 DB 读渲染(端口 8089) + +镜像已发布到 docker.io [`nickwilde18/pkgsitex:fork-main`](https://hub.docker.com/r/nickwilde18/pkgsitex)(GitHub Actions 自动 push)。用户不需要 build——拉 image 即用。 + +```sh +git clone https://github.com/NickWilde18/pkgsitex.git +cd pkgsitex +git checkout fork/main + +# 1) 配 PAT +cp deploy/prod/.env.example deploy/prod/.env +$EDITOR deploy/prod/.env # GITHUB_TOKEN=ghp_xxx + +# 2) 编辑要监控的 module 列表(内置 5 个 CUHKSZ 内部 repo 示例) +$EDITOR deploy/prod/config.yaml + +# 3) 起 stack(自动拉镜像) +docker compose -f compose.prod.yaml --env-file deploy/prod/.env up -d + +# 4) 浏览 +open http://localhost:8089/pkgsitex/ +``` + +完整运维(加 / 删 module、周期 cron 刷新、故障排查):[`deploy/prod/README.md`](deploy/prod/README.md)。 + +支持 multi-version(git tag 切换)、license permissive(私有 / proprietary 也渲)、配置文件驱动。 + +## 维护策略 + +- **master 永远 = upstream master**——不 merge `fork/main` 进 master。GitHub "Sync fork" 永远 fast-forward 0 conflict +- **`fork/main` 是 long-lived patch branch**——所有 fork 改动累积在此分支 +- **月度 rebase**:AI 跑 `git rebase upstream/master` 让 `fork/main` 跟随上游新进展。预测 conflict 集中在: + - `internal/frontend/server.go` 的 mux 列表(上游新加路由时) + - `static/**/*.tmpl` / `*.ts`(上游改样式 / 加交互时) + - 其他 fork 文件(如 `markdown_ext.go`、`base-path/base-path.ts`)几乎不会 conflict(fork 独有) + +历次 patch 详见 PR [#2](https://github.com/NickWilde18/pkgsitex/pull/2)。 + +## 故障排查 + +| 现象 | 排查 | +|---|---| +| `go run` 拉 dep 失败 | `GOPROXY=https://goproxy.cn,direct go run ...`(Dockerfile 默认 goproxy.cn) | +| docker `pull access denied` | 用 `docker compose build pkgsitex` 走本地 build,不要拉 image | +| 浏览 `/pkgsitex/...` 静态资源 404 | 改了 `static/`/模板后没重 build:`go run ./devtools/cmd/static` 重生 bundle,或 docker 路径 `docker compose build pkgsitex` | +| 解析 module 超时 | 走公司 Athens:`docker compose build --build-arg GOPROXY=https://athens.corp.com pkgsitex` 或本地 `export GOPROXY=...` | +| mermaid 不渲染 | 浏览器 console 看 `mermaid load failed`——内网拦了 jsdelivr CDN,需要本地 host mermaid(`third_party/mermaid/` 待 vendor) | +| Sidebar / Index 没"Show unexported"按钮 | 可能 localStorage 之前 toggle key 是老名字(`gogodocs:showUnexported`),点一次按钮即可重置成新 key | + +--- + +## 上游 pkgsite + +下游 fork 需要跟随上游进展,保留上游 README 内容如下,方便核对功能 / 升级时定位差异。 + +# golang.org/x/pkgsite + +This repository hosts the source code of the [pkg.go.dev](https://pkg.go.dev) website, +and [`pkgsite`](https://pkg.go.dev/golang.org/x/pkgsite/cmd/pkgsite), a documentation +server program. + +[![Go Reference](https://pkg.go.dev/badge/golang.org/x/pkgsite.svg)](https://pkg.go.dev/golang.org/x/pkgsite) + +完整上游 README:[golang/pkgsite README](https://github.com/golang/pkgsite#readme)。 + +## Issues + +Fork 自身 issues(base-path / markdown ext / etc):[NickWilde18/pkgsitex/issues](https://github.com/NickWilde18/pkgsitex/issues)。 + +上游 pkgsite issues 报到 [`golang/go`](https://golang.org/issues),前缀 `x/pkgsite:`,详见上游 README。 + +## Contributing + +Fork 内部 PR 投到 `fork/main` 分支(不进 master)。 + +上游 pkgsite contribution flow(gerrit code review)见 [Contribution Guide](https://golang.org/doc/contribute.html) + [上游 README contributing 段](https://github.com/golang/pkgsite#contributing)。 + +## License + +Unless otherwise noted, the source files are distributed under the BSD-style license found in the LICENSE file. From 6c9be5d03bdbabe3a68e8e2aff0ad9d428817886 Mon Sep 17 00:00:00 2001 From: Yechi Yang <95576172+NickWilde18@users.noreply.github.com> Date: Fri, 8 May 2026 15:20:32 +0800 Subject: [PATCH 26/26] =?UTF-8?q?docs:=20=E5=88=A0=20dev=20mode=20docker-c?= =?UTF-8?q?ompose=20=E6=AE=B5=E2=80=94=E2=80=94=E7=9B=AE=E6=A0=87=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E9=83=BD=E8=A3=85=20Go=20toolchain?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pkgsitex 是给 Go 开发者看自家代码 godoc 的工具——dev 路径就 \`go run\` 直接最快,所谓"给没 Go toolchain 的同事"在实务上不存在(看 godoc 的人 本来就是 Go 开发者)。同步删配套文件: - compose.yaml(cmd/pkgsite local mode 的 docker 化包装,无人用) - Dockerfile(同上,root 级,跟 deploy/prod/Dockerfile 是两个完全独立的 image) deploy/prod/{compose.prod.yaml,Dockerfile} 是给"看 godoc 的同事"准备的—— ops 在内部环境部署 prod stack,全公司浏览器访问 frontend;个人不需要本地 跑 docker。 troubleshooting 表里 docker 相关 row 一并清掉。 --- Dockerfile | 43 --------------------------------------- README.md | 24 +++++----------------- README.zh-CN.md | 24 +++++----------------- compose.yaml | 53 ------------------------------------------------- 4 files changed, 10 insertions(+), 134 deletions(-) delete mode 100644 Dockerfile delete mode 100644 compose.yaml diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 428a253ad..000000000 --- a/Dockerfile +++ /dev/null @@ -1,43 +0,0 @@ -# GoGoDocs(fork from golang/pkgsite)容器镜像。 -# -# 目标场景:内网部署 cmd/pkgsite local mode 服务多个 mount 进来的 Go module -# 仓库(不走上游 cmd/frontend + worker + Postgres 的全套,省一个数量级运维)。 -# -# 不能用 alpine 极简 base——cmd/pkgsite 在 local mode 下通过 go/packages.Load -# 调 `go list` 解析模块依赖,container 必须自带 Go toolchain。同 base image -# 用 golang:1.24 既 build 又 run,stage 1 复制二进制到 stage 2 也只是少几 MB -# 收益不显著。 -# -# GOPROXY 默认走 goproxy.cn 应对国内网络环境;改 build arg / env 可换走 -# 公司内网 Athens 之类。 - -FROM golang:1.25 AS builder -WORKDIR /src - -# go.mod / go.sum 单独 COPY 让 dep 下载层可缓存(跟随 source 改动) -COPY go.mod go.sum ./ -ARG GOPROXY=https://goproxy.cn,https://proxy.golang.org,direct -ENV GOPROXY=${GOPROXY} -RUN go mod download - -COPY . . -RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /pkgsite ./cmd/pkgsite - -FROM golang:1.25 -LABEL org.opencontainers.image.source="https://github.com/NickWilde18/pkgsitex" -LABEL org.opencontainers.image.description="Self-hosted godoc browser (pkgsite fork) with base-path support" - -COPY --from=builder /pkgsite /usr/local/bin/pkgsite - -# runtime 也需要 GOPROXY 给 go/packages 拉依赖(即便 mount 进来的仓库是 vendor 模式) -ARG GOPROXY=https://goproxy.cn,https://proxy.golang.org,direct -ENV GOPROXY=${GOPROXY} - -# /repos 是约定的 mount 目录,docker-compose 把每个 Go module 仓库挂进来 -WORKDIR /repos - -EXPOSE 8080 - -# 默认参数让 -h 能直接看 usage;docker-compose 通过 command: 覆盖 -ENTRYPOINT ["pkgsite"] -CMD ["-h"] diff --git a/README.md b/README.md index 5e0643434..70dd52be4 100644 --- a/README.md +++ b/README.md @@ -37,17 +37,6 @@ pkgsite -base-path=/pkgsitex -show-unexported -http=:8089 ~/Repo/Chat ... The first `go run` fetches `esbuild` / `safehtml` deps via GOPROXY (~30s); subsequent runs are instant. -## docker-compose start (no Go toolchain required) - -The repo root has [`compose.yaml`](compose.yaml): - -```sh -docker compose up -d -open http://localhost:8089/pkgsitex/ -``` - -The first build pulls the `golang:1.25` image (~700 MB) and bundles esbuild — about 3-5 minutes. After editing `fork/main`, `docker compose build pkgsitex` is a fast incremental rebuild. - ## Configuration | flag | purpose | default | @@ -63,11 +52,9 @@ Full flags: `go run ./cmd/pkgsite -h`. ## Adding / removing repos -**Local binary**: edit the path list at the end of the command. - -**docker-compose**: edit [`compose.yaml`](compose.yaml) — add a `volumes:` entry `../:/repos/:ro` plus a `command:` arg `/repos/`. +Edit the path list at the end of the `go run` / `pkgsite` command. -Convention: clone internal repos to `~/Repo/`. UniAuth is a monorepo with two Go modules (`uniauth-gf/` + `ittools_sync/`) — mount each separately; pkgsite doesn't auto-discover nested modules. +Convention: clone internal repos to `~/Repo/`. UniAuth is a monorepo with two Go modules (`uniauth-gf/` + `ittools_sync/`) — list each separately; pkgsite doesn't auto-discover nested modules. ## Browse experience @@ -142,10 +129,9 @@ Patch history: PR [#2](https://github.com/NickWilde18/pkgsitex/pull/2). | Symptom | Diagnosis | |---|---| -| `go run` fails to fetch deps | `GOPROXY=https://goproxy.cn,direct go run ...` (the Dockerfile defaults to goproxy.cn) | -| docker `pull access denied` | use `docker compose build pkgsitex` to build locally instead of pulling | -| `/pkgsitex/...` static asset 404 | you edited `static/`/templates without rebuilding: run `go run ./devtools/cmd/static` to regenerate the bundle, or `docker compose build pkgsitex` | -| module resolution timeout | use the company Athens: `docker compose build --build-arg GOPROXY=https://athens.corp.com pkgsitex`, or set `GOPROXY=...` locally | +| `go run` fails to fetch deps | use a closer GOPROXY: `GOPROXY=https://goproxy.cn,direct go run ...` | +| `/pkgsitex/...` static asset 404 | you edited `static/`/templates without rebuilding: run `go run ./devtools/cmd/static` to regenerate the bundle | +| module resolution timeout | point GOPROXY at the company Athens: `export GOPROXY=https://athens.corp.com,direct` | | mermaid doesn't render | check the browser console for `mermaid load failed` — your network blocks jsdelivr CDN; vendor mermaid locally (`third_party/mermaid/` pending) | | Sidebar / Index has no "Show unexported" button | localStorage may have an old toggle key (`gogodocs:showUnexported`); click the button once to reset to the new key | diff --git a/README.zh-CN.md b/README.zh-CN.md index 7a9a30702..7c289c120 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -37,17 +37,6 @@ pkgsite -base-path=/pkgsitex -show-unexported -http=:8089 ~/Repo/Chat ... 首次 `go run` 拉 esbuild / safehtml 等 dep 走 GOPROXY,约 30 秒,后续秒启。 -## docker-compose 启动(备选,给没 Go toolchain 的同事) - -仓库根有 [`compose.yaml`](compose.yaml): - -```sh -docker compose up -d -open http://localhost:8089/pkgsitex/ -``` - -首次 build 拉 `golang:1.25` image(~700 MB) + esbuild bundle,约 3-5 分钟。改 `fork/main` 后 `docker compose build pkgsitex` 增量很快。 - ## 配置选项 | flag | 用途 | 默认 | @@ -63,11 +52,9 @@ open http://localhost:8089/pkgsitex/ ## 增 / 减仓库 -**本地 binary**:直接改 command 末尾 path 列表。 - -**docker-compose**:编辑 [`compose.yaml`](compose.yaml) `volumes:` 加一行 `../:/repos/:ro`,`command:` 末尾加 `/repos/`。 +直接改 `go run` / `pkgsite` 命令末尾的 path 列表。 -约定:内部仓库都 clone 在 `~/Repo/`。UniAuth 是 monorepo 含 `uniauth-gf/` + `ittools_sync/` 两个 Go module,各 mount 一份——pkgsite 不支持 nested module 自动发现。 +约定:内部仓库都 clone 在 `~/Repo/`。UniAuth 是 monorepo 含 `uniauth-gf/` + `ittools_sync/` 两个 Go module,各列一份——pkgsite 不支持 nested module 自动发现。 ## 浏览体验 @@ -141,10 +128,9 @@ open http://localhost:8089/pkgsitex/ | 现象 | 排查 | |---|---| -| `go run` 拉 dep 失败 | `GOPROXY=https://goproxy.cn,direct go run ...`(Dockerfile 默认 goproxy.cn) | -| docker `pull access denied` | 用 `docker compose build pkgsitex` 走本地 build,不要拉 image | -| 浏览 `/pkgsitex/...` 静态资源 404 | 改了 `static/`/模板后没重 build:`go run ./devtools/cmd/static` 重生 bundle,或 docker 路径 `docker compose build pkgsitex` | -| 解析 module 超时 | 走公司 Athens:`docker compose build --build-arg GOPROXY=https://athens.corp.com pkgsitex` 或本地 `export GOPROXY=...` | +| `go run` 拉 dep 失败 | 走更近的 GOPROXY:`GOPROXY=https://goproxy.cn,direct go run ...` | +| 浏览 `/pkgsitex/...` 静态资源 404 | 改了 `static/`/模板后没重 build:`go run ./devtools/cmd/static` 重生 bundle | +| 解析 module 超时 | 把 GOPROXY 指向公司 Athens:`export GOPROXY=https://athens.corp.com,direct` | | mermaid 不渲染 | 浏览器 console 看 `mermaid load failed`——内网拦了 jsdelivr CDN,需要本地 host mermaid(`third_party/mermaid/` 待 vendor) | | Sidebar / Index 没"Show unexported"按钮 | 可能 localStorage 之前 toggle key 是老名字(`gogodocs:showUnexported`),点一次按钮即可重置成新 key | diff --git a/compose.yaml b/compose.yaml deleted file mode 100644 index 83996ff8a..000000000 --- a/compose.yaml +++ /dev/null @@ -1,53 +0,0 @@ -# pkgsitex 本地启动 —— 索引平级 clone 在 ~/Repo/ 下的内部 Go module。 -# -# 使用: -# cd ~/Repo/pkgsitex -# docker compose up -d -# open http://localhost:8089/pkgsitex/ -# -# 不需要 ~/Repo/pkgsitex 自身平级——pkgsitex repo 是 build context 自己。其他 -# 内部仓库(Chat / UniAuth / open-platform / Doubao-Speech-Service 等)需要 -# 已 clone 到 ~/Repo/ 下,相对路径 ../ mount 进容器。 -# -# 增 / 减仓库: -# 1) volumes: 加一行 ../:/repos/:ro -# 2) command: 末尾加 /repos/ -# -# 本地有 Go toolchain 时直接 `go run ./cmd/pkgsite -base-path=... ` -# 比 docker 启动快 10 倍——docker 适合给没 Go toolchain 的同事用,或者 -# 想跟其他 service(mssql / redis / 业务 backend)一起编排时。 -# -# Prod 部署是另一回事:cmd/frontend + cmd/worker + Postgres 三件套,配置 -# 文件驱动 + GitHub PAT 拉私有 repo + license 放行——见 docs/PROD_DEPLOY.md -# (TODO,下一阶段做)。 - -services: - pkgsitex: - build: - context: . - image: pkgsitex:local - container_name: pkgsitex - ports: - - "8089:8080" - volumes: - # mount 公司内部 Go module 仓库(约定 ~/Repo/,docker-compose 的 - # working dir 是本 yaml 所在目录,相对 .. 就是 ~/Repo/) - - ../Chat:/repos/chat:ro - - ../Doubao-Speech-Service:/repos/doubao-speech-service:ro - # UniAuth 是 monorepo 含两个 Go module,分别 mount——pkgsite 不支持 - # nested module 自动发现 - - ../UniAuth/uniauth-gf:/repos/uniauth-gf:ro - - ../UniAuth/ittools_sync:/repos/ittools_sync:ro - - ../open-platform:/repos/open-platform:ro - command: - - "-base-path=/pkgsitex" - - "-show-unexported" - - "-http=:8080" - # mount 进来的 module path 全列出来——pkgsite 不会自动发现 /repos/ 下 - # 多 module,需要显式列。增减仓库时跟上面 volumes: 同步改 - - "/repos/chat" - - "/repos/doubao-speech-service" - - "/repos/uniauth-gf" - - "/repos/ittools_sync" - - "/repos/open-platform" - restart: unless-stopped