@thebase/ui 1.1.1 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +21 -6
  3. package/bin/create-baseui-app.mjs +464 -4
  4. package/dist/apidoc/img/bash-plain.svg +1 -0
  5. package/dist/apidoc/img/c-original.svg +1 -0
  6. package/dist/apidoc/img/csharp-original.svg +1 -0
  7. package/dist/apidoc/img/dart-original.svg +1 -0
  8. package/dist/apidoc/img/go-original-wordmark.svg +1 -0
  9. package/dist/apidoc/img/java-original.svg +1 -0
  10. package/dist/apidoc/img/javascript-original.svg +1 -0
  11. package/dist/apidoc/img/kotlin-original.svg +10 -0
  12. package/dist/apidoc/img/lua.svg +7 -0
  13. package/dist/apidoc/img/nodejs-original.svg +1 -0
  14. package/dist/apidoc/img/objectivec-plain.svg +1 -0
  15. package/dist/apidoc/img/php-original.svg +2 -0
  16. package/dist/apidoc/img/python-original.svg +1 -0
  17. package/dist/apidoc/img/ruby-plain.svg +1 -0
  18. package/dist/apidoc/img/rust-original.svg +1 -0
  19. package/dist/apidoc/img/swift-original.svg +1 -0
  20. package/dist/apidoc/img/windows8-original.svg +1 -0
  21. package/dist/baseui.esm.js +33 -11
  22. package/dist/baseui.min.css +1 -1
  23. package/dist/baseui.min.js +33 -11
  24. package/dist/baseui.pwa.esm.js +53 -0
  25. package/dist/baseui.registry.json +42 -5
  26. package/dist/baseui.templates.xml +469 -1
  27. package/dist/pwa-icons/apple-touch-icon.png +0 -0
  28. package/dist/pwa-icons/favicon-16x16.png +0 -0
  29. package/dist/pwa-icons/favicon-32x32.png +0 -0
  30. package/dist/pwa-icons/favicon.ico +0 -0
  31. package/dist/pwa-icons/icon-192.png +0 -0
  32. package/dist/pwa-icons/icon-512.png +0 -0
  33. package/dist/types/pwa.d.ts +45 -0
  34. package/docs/analytics.md +4 -4
  35. package/docs/blocks.md +2 -1
  36. package/docs/components/apidoc.md +66 -0
  37. package/docs/components/input.md +17 -0
  38. package/docs/components/markdown.md +1 -1
  39. package/docs/icons.md +1 -1
  40. package/docs/installation.md +2 -2
  41. package/docs/owl-components.md +1 -1
  42. package/docs/registry.md +1 -1
  43. package/docs/theming.md +1 -1
  44. package/docs/usage-guide.md +43 -4
  45. package/package.json +8 -1
@@ -0,0 +1,53 @@
1
+ var d=["favicon.ico","favicon-16x16.png","favicon-32x32.png","apple-touch-icon.png","icon-192.png","icon-512.png"],l="/icons/";function m(e){return[{src:`${e}icon-192.png`,sizes:"192x192",type:"image/png",purpose:"any"},{src:`${e}icon-512.png`,sizes:"512x512",type:"image/png",purpose:"any"},{src:`${e}icon-512.png`,sizes:"512x512",type:"image/png",purpose:"maskable"}]}function E(e){let{name:t,shortName:s=t,description:c="",startUrl:r="/",scope:a="/",display:o="standalone",orientation:n="portrait-primary",backgroundColor:i="#ffffff",themeColor:p="#0f172a",lang:h="en",iconsBase:u=l,icons:f=m(u)}=e;return{name:t,short_name:s,description:c,start_url:r,scope:a,display:o,orientation:n,background_color:i,theme_color:p,lang:h,icons:f}}function C(e){let{cacheName:t,cdnVersion:s,iconsBase:c=l,manifestUrl:r="/manifest.webmanifest",appFiles:a=[],extraPrecacheUrls:o=[]}=e,n=`https://cdn.jsdelivr.net/npm/@thebase/ui@${s}/dist/`,i=["/",r,...d.map(p=>`${c}${p}`),...a,...o,`${n}baseui.min.css`,`${n}baseui.esm.js`,`${n}baseui.templates.xml`];return`const CACHE_NAME = ${JSON.stringify(t)};
2
+ const BASEUI_CDN = ${JSON.stringify(n)};
3
+
4
+ const PRECACHE_URLS = ${JSON.stringify(i,null,2)};
5
+
6
+ self.addEventListener("install", (event) => {
7
+ event.waitUntil(
8
+ caches.open(CACHE_NAME).then((cache) => cache.addAll(PRECACHE_URLS)).then(() => self.skipWaiting())
9
+ );
10
+ });
11
+
12
+ self.addEventListener("activate", (event) => {
13
+ event.waitUntil(
14
+ caches
15
+ .keys()
16
+ .then((keys) => Promise.all(keys.filter((key) => key !== CACHE_NAME).map((key) => caches.delete(key))))
17
+ .then(() => self.clients.claim())
18
+ );
19
+ });
20
+
21
+ async function fromCdnCache(request) {
22
+ const cache = await caches.open(CACHE_NAME);
23
+ const cached = await cache.match(request);
24
+ if (cached) return cached;
25
+ const response = await fetch(request);
26
+ if (response.ok) cache.put(request, response.clone());
27
+ return response;
28
+ }
29
+
30
+ self.addEventListener("fetch", (event) => {
31
+ const { request } = event;
32
+ if (request.method !== "GET") return;
33
+ if (request.url.startsWith(BASEUI_CDN)) {
34
+ event.respondWith(fromCdnCache(request));
35
+ return;
36
+ }
37
+ if (new URL(request.url).origin !== self.location.origin) return;
38
+
39
+ event.respondWith(
40
+ caches.open(CACHE_NAME).then(async (cache) => {
41
+ try {
42
+ const response = await fetch(request);
43
+ if (response.ok) cache.put(request, response.clone());
44
+ return response;
45
+ } catch {
46
+ const cached = await cache.match(request);
47
+ if (cached) return cached;
48
+ throw new Error(\`No cached response for \${request.url} and network unavailable\`);
49
+ }
50
+ })
51
+ );
52
+ });
53
+ `}export{d as DEFAULT_ICON_FILES,E as buildManifest,C as buildServiceWorker};
@@ -124,6 +124,43 @@
124
124
  ],
125
125
  "templateName": "baseui.Alert"
126
126
  },
127
+ {
128
+ "name": "apidoc",
129
+ "type": "owl",
130
+ "dependencies": [
131
+ "owl",
132
+ "marked",
133
+ "mermaid"
134
+ ],
135
+ "attributes": [
136
+ "b-ui",
137
+ "b-att-src",
138
+ "b-att-title",
139
+ "b-att-server-url",
140
+ "b-att-theme",
141
+ "b-att-theme-toggle",
142
+ "b-att-hash",
143
+ "b-att-operation",
144
+ "b-att-try-it",
145
+ "b-att-authorize",
146
+ "b-att-languages",
147
+ "b-att-language",
148
+ "b-att-asset-base",
149
+ "b-att-oauth-redirect-uri",
150
+ "b-att-height",
151
+ "b-att-templates"
152
+ ],
153
+ "events": [
154
+ "baseui:load",
155
+ "baseui:error",
156
+ "baseui:navigate"
157
+ ],
158
+ "owlComponents": [
159
+ "ApiDoc",
160
+ "ApiDocGroup"
161
+ ],
162
+ "templateName": "baseui.ApiDoc"
163
+ },
127
164
  {
128
165
  "name": "aspect-ratio",
129
166
  "type": "static",
@@ -1660,13 +1697,13 @@
1660
1697
  }
1661
1698
  ],
1662
1699
  "integrity": {
1663
- "baseui.min.css": "sha384-hYFwlV6T/ONYzawtDGDFUoruqgNwYl9AVvLDHx24IWgKSMXqwACo/nX04I3gnyT9",
1700
+ "baseui.min.css": "sha384-RP2d04etuqh6nmVNHdJFYqK79B7N55jITUg52YrEXI3lyla5kJA4AHa2hUq79v0m",
1664
1701
  "bootstrap.bundle.min.js": "sha384-RMbllTJcplkCRnELNTOc5Kz/3AvFDE/rNlJqWqfgmRWruobClBdfwvl10akQZkR1",
1665
- "baseui.min.js": "sha384-m91LivkZ3FsaSmngFXfmaGHxfBokU/tEXFAuYz3PIjeXuKMcJjdRmOEk596O8S8u",
1666
- "baseui.esm.js": "sha384-v3JjyzSIh67awBXIDScUTR40lyCHyuOue1zwObQHjH4OjOj9Cg3mPMkto3LVcVO7",
1702
+ "baseui.min.js": "sha384-3kwM3Q4HqvaVSHDfkmmIFzU9nr7VfuMAqeNpacnv8iWs2k8FAvCg3vW+j18Uoq4Y",
1703
+ "baseui.esm.js": "sha384-Kg7yHA4YxH2uOdvcC4sWN5SrLfMLT09TGKzE6+WldH1rveQTfE3okF7YZPYX1TY0",
1667
1704
  "baseui.icons.esm.js": "sha384-hj39VMhzcFvmYnjob2hlioMOAqwAXCURGreF6s9RitXFp/iYi7rUdqtfCI+ICQeS",
1668
- "baseui.templates.xml": "sha384-XkqmfurKcEF6WuIQHWAbnZfW6oz19n7SDVRDU3g0hc3qOr4kPd/qfNGcTmcDDIgQ",
1669
- "baseui.block-viewer.esm.js": "sha384-XlIb3jDxt6Q75dbC4rLctTdPU6u0vJFcbxp9Gl6VKOkhjpz3wRBe0E3dWd80425L",
1705
+ "baseui.templates.xml": "sha384-A1BT9HyTN/3AyKq21IwbLUfMWcvMRoESKDoDOzOcEWEEapHVc/7rBBHygOUWpree",
1706
+ "baseui.block-viewer.esm.js": "sha384-6GNPYUTwsoi1ZsfOWhVWqw4vI328WX6Xb5f7A4HygyT5s+fmJaEbL7ZQmPamoyJz",
1670
1707
  "baseui.analytics.esm.js": "sha384-M6TVmchmceTvpSzgI+DNA6zAn9xmz9a+YEexCLslSo/Ngxyp2kmzs9fm+PxWy+Da",
1671
1708
  "baseui.analytics.min.js": "sha384-B0hBkVDbT7gmDmUMazlN4Nc1KnYGSpL+IcW2hd8840knx73HGImPgGs84MvS3hKE",
1672
1709
  "icons/lucide.json": "sha384-mc5mzpmq9eOgZVa2IcjrL33Ty4nMXE8aoZMfy+lgkC/jMQ4aYnk2S58mFsSGFnW1",
@@ -89,6 +89,456 @@
89
89
  <div data-slot="alert-action" t-att-class="'bu-alert-action' + (props.className ? ' ' + props.className : '')" t-att-style="props.style"><t t-slot="default"/></div>
90
90
  </t>
91
91
  <t t-name="AlertAction" t-call="baseui.AlertAction"/>
92
+ <t t-name="baseui.ApiDoc">
93
+ <div t-ref="root" t-att-class="classNames" t-att-style="rootStyle" t-att-data-theme="theme" t-att-data-bs-theme="theme" data-slot="apidoc" t-att-data-status="state.status">
94
+ <header class="app-header">
95
+ <div class="header-left">
96
+ <Button variant="'ghost'" size="'icon'" icon="'menu'" className="'icon-button mobile-only sidebar-toggle'" aria-label="'Toggle sidebar'" attrs="{'aria-expanded': state.sidebarOpen ? 'true' : 'false'}" onClick="() => this.toggleSidebar()"/>
97
+ <a t-if="state.status === 'ready'" class="brand-link" t-att-href="href('introduction')" t-on-click="(ev) => this.go(ev, 'introduction')"><span class="brand-title" t-esc="title"/></a>
98
+ </div>
99
+ <div class="header-right">
100
+ <Button t-if="state.status === 'ready' and hasSecurity" variant="state.authed ? 'default' : 'outline'" size="'sm'" className="'btn-authorize'" label="state.authed ? 'Authorized' : 'Authorize'" onClick="() => this.openAuth()"/>
101
+ <Badge t-if="state.status === 'ready' and doc.info.version" variant="'muted'" pill="true" label="'v' + doc.info.version"/>
102
+ <Button t-if="props.themeToggle" variant="'ghost'" size="'icon'" icon="theme === 'dark' ? 'sun' : 'moon'" aria-label="'Switch to ' + (theme === 'dark' ? 'light' : 'dark') + ' mode'" ariaPressed="theme === 'dark' ? 'true' : 'false'" onClick="() => this.toggleTheme()"/>
103
+ </div>
104
+ </header>
105
+
106
+ <Dialog open="state.authOpen" onOpenChange="(open) => this.setAuthOpen(open)">
107
+ <DialogContent className="'auth-dialog-panel'">
108
+ <DialogHeader>
109
+ <DialogTitle>Available authorizations</DialogTitle>
110
+ <DialogDescription>Values are stored locally in your browser and applied to Try It requests.</DialogDescription>
111
+ </DialogHeader>
112
+ <section t-if="state.authOpen" class="auth-dialog-body">
113
+ <t t-call="baseui.ApiDocAuthBody"/>
114
+ </section>
115
+ <DialogFooter>
116
+ <Button variant="'ghost'" label="'Close'" onClick="() => this.closeAuth()"/>
117
+ <Button label="'Authorize'" onClick="() => this.saveAuth()"/>
118
+ </DialogFooter>
119
+ </DialogContent>
120
+ </Dialog>
121
+
122
+ <div class="app-shell">
123
+ <aside t-if="state.status === 'ready'" class="sidebar-shell" t-att-aria-hidden="state.sidebarOpen ? 'false' : 'true'">
124
+ <nav class="sidebar-nav" aria-label="API navigation">
125
+ <ul class="sidebar-root">
126
+ <li>
127
+ <a t-att-href="href('introduction')" t-att-class="'sidebar-intro-link' + (state.current === 'introduction' ? ' is-active' : '')" t-on-click="(ev) => this.go(ev, 'introduction')"><span>Introduction</span></a>
128
+ </li>
129
+ <t t-foreach="sidebarGroups" t-as="group" t-key="group.id">
130
+ <ApiDocGroup group="group" groups="doc.groups" current="state.current" href="(id) => this.href(id)" onSelect="(ev, id) => this.go(ev, id)"/>
131
+ </t>
132
+ </ul>
133
+ </nav>
134
+ </aside>
135
+
136
+ <main class="workspace">
137
+ <section t-ref="main" class="doc-main">
138
+ <div t-if="state.status === 'loading'" class="empty-state" role="status">Loading API documentation…</div>
139
+ <div t-elif="state.status === 'error'" class="empty-state" role="alert">Failed to load API spec: <t t-esc="state.error"/></div>
140
+ <t t-elif="op" t-call="baseui.ApiDocOperation"/>
141
+ <t t-else="" t-call="baseui.ApiDocIntro"/>
142
+ </section>
143
+ <aside t-if="state.status === 'ready' and showTryIt" class="tryit-panel">
144
+ <t t-call="baseui.ApiDocTryIt"/>
145
+ </aside>
146
+ </main>
147
+ </div>
148
+ </div>
149
+ </t>
150
+ <t t-name="ApiDoc" t-call="baseui.ApiDoc"/>
151
+
152
+ <t t-name="baseui.ApiDocGroup">
153
+ <li class="sidebar-group-wrap">
154
+ <details class="sidebar-group" open="open">
155
+ <summary>
156
+ <span class="sidebar-group-label"><t t-esc="props.group.name"/><span t-if="props.group.kind" class="apidoc-muted"> (<t t-esc="props.group.kind"/>)</span></span>
157
+ <span class="sidebar-group-arrow"><Icon name="'chevron-right'"/></span>
158
+ </summary>
159
+ <ul class="sidebar-group-items">
160
+ <li t-foreach="props.group.items" t-as="item" t-key="item.id">
161
+ <a t-att-href="props.href(item.id)" t-att-class="'sidebar-op-link' + (props.current === item.id ? ' is-active' : '')" t-on-click="(ev) => props.onSelect(ev, item.id)">
162
+ <span class="method-badge" t-att-data-method="item.method" t-esc="methodLabel(item.method)"/>
163
+ <span t-esc="item.summary"/>
164
+ </a>
165
+ </li>
166
+ <t t-foreach="children" t-as="child" t-key="child.id">
167
+ <ApiDocGroup group="child" groups="props.groups" current="props.current" href="props.href" onSelect="props.onSelect"/>
168
+ </t>
169
+ </ul>
170
+ </details>
171
+ </li>
172
+ </t>
173
+
174
+ <t t-name="baseui.ApiDocBreadcrumb">
175
+ <Breadcrumb>
176
+ <BreadcrumbList>
177
+ <t t-foreach="breadcrumb" t-as="part" t-key="part_index">
178
+ <BreadcrumbItem>
179
+ <BreadcrumbPage t-if="part_last"><t t-esc="part.label"/></BreadcrumbPage>
180
+ <BreadcrumbLink t-else="" href="href(part.id)" onClick="(ev) => this.go(ev, part.id)">
181
+ <Icon t-if="part.home" name="'house'"/>
182
+ <t t-else="" t-esc="part.label"/>
183
+ </BreadcrumbLink>
184
+ </BreadcrumbItem>
185
+ <BreadcrumbSeparator t-if="!part_last"/>
186
+ </t>
187
+ </BreadcrumbList>
188
+ </Breadcrumb>
189
+ </t>
190
+
191
+ <t t-name="baseui.ApiDocIntro">
192
+ <div class="doc-pane">
193
+ <t t-call="baseui.ApiDocBreadcrumb"/>
194
+ <h1 t-esc="title"/>
195
+ <Badge t-if="doc.info.version" variant="'muted'" label="'v' + doc.info.version"/>
196
+ <p t-if="doc.info.summary" class="param-desc" t-esc="doc.info.summary"/>
197
+ <div t-if="doc.info.description" class="prose"><Markdown t-key="theme" content="doc.info.description" baseUi="false"/></div>
198
+ <div class="intro-card">
199
+ <header><h3>Base URL</h3></header>
200
+ <section><code t-esc="doc.baseUrl"/></section>
201
+ </div>
202
+ <section class="doc-section">
203
+ <h2>Groups</h2>
204
+ <ul class="intro-group-list">
205
+ <li t-foreach="doc.groups" t-as="group" t-key="group.id">
206
+ <a t-att-href="href(group.items[0].id)" t-on-click="(ev) => this.go(ev, group.items[0].id)"><strong t-esc="group.name"/></a>
207
+ <span t-if="group.description"> — <t t-esc="group.description"/></span>
208
+ <span class="apidoc-muted"> (<t t-esc="group.items.length"/> endpoint<t t-if="group.items.length !== 1">s</t>)</span>
209
+ </li>
210
+ </ul>
211
+ </section>
212
+ </div>
213
+ </t>
214
+
215
+ <t t-name="baseui.ApiDocParams">
216
+ <details t-if="paramList.length" class="param-group" open="open">
217
+ <summary><span class="param-group-head"><span class="caret"/><t t-esc="paramTitle.toUpperCase()"/></span></summary>
218
+ <ul>
219
+ <li t-foreach="paramList" t-as="p" t-key="p.name" class="param-row">
220
+ <div>
221
+ <div class="param-row-head">
222
+ <span class="param-name" t-esc="p.name"/>
223
+ <span class="param-type"><t t-esc="p.type"/><t t-if="p.format"> (<t t-esc="p.format"/>)</t></span>
224
+ </div>
225
+ <p t-if="p.description" class="param-desc" t-esc="p.description"/>
226
+ <p t-if="p.enum" class="param-desc">enum: <t t-foreach="p.enum" t-as="e" t-key="e_index"><code t-esc="e"/><t t-if="!e_last">, </t></t></p>
227
+ <p t-if="p.example !== undefined" class="param-desc">example<t t-if="p.exampleLabel"> (<t t-esc="p.exampleLabel"/>)</t>: <code t-esc="p.example"/></p>
228
+ </div>
229
+ <Badge t-if="p.required" variant="'danger'" label="'Required'"/>
230
+ </li>
231
+ </ul>
232
+ </details>
233
+ </t>
234
+
235
+ <t t-name="baseui.ApiDocSchema">
236
+ <t t-set="rows" t-value="schemaRows(schemaNode)"/>
237
+ <p t-if="!rows.length" class="apidoc-muted">No schema available.</p>
238
+ <ul t-else="" class="schema-tree">
239
+ <li t-foreach="rows" t-as="row" t-key="row_index" t-attf-style="--depth:#{depth(row.path)}">
240
+ <span class="param-name"><t t-esc="row.path"/><span t-if="row.required" class="required-star">*</span></span>
241
+ <span class="param-type"><t t-esc="row.type"/><t t-if="row.format"> (<t t-esc="row.format"/>)</t></span>
242
+ <p t-if="row.description" class="param-desc" t-esc="row.description"/>
243
+ </li>
244
+ </ul>
245
+ </t>
246
+
247
+ <t t-name="baseui.ApiDocCode">
248
+ <pre class="snippet-block"><span class="snippet-gutter"><span t-foreach="lineNumbers(codeText)" t-as="n" t-key="n" t-esc="n"/></span><span class="snippet-code"><code t-esc="codeText"/></span></pre>
249
+ </t>
250
+
251
+ <t t-name="baseui.ApiDocOperation">
252
+ <div class="doc-pane">
253
+ <t t-call="baseui.ApiDocBreadcrumb"/>
254
+ <h1><t t-esc="op.summary"/><Badge t-if="op.deprecated" variant="'warning'" label="'Deprecated'"/></h1>
255
+ <div class="endpoint-box">
256
+ <span class="method-badge" t-att-data-method="op.method" t-esc="methodLabel(op.method)"/>
257
+ <code class="endpoint-url" t-esc="doc.baseUrl + op.path"/>
258
+ </div>
259
+ <section t-if="op.description" class="doc-section"><div class="prose"><Markdown t-key="op.id + theme" content="op.description" baseUi="false"/></div></section>
260
+ <p t-if="op.externalDocs and op.externalDocs.url" class="param-desc"><a t-att-href="op.externalDocs.url" target="_blank" rel="noopener noreferrer"><t t-esc="op.externalDocs.description || 'External documentation'"/></a></p>
261
+
262
+ <section class="doc-section">
263
+ <h2>Request</h2>
264
+ <t t-call="baseui.ApiDocParams"><t t-set="paramTitle" t-value="'Path Parameters'"/><t t-set="paramList" t-value="op.parameters.path"/></t>
265
+ <t t-call="baseui.ApiDocParams"><t t-set="paramTitle" t-value="'Query Parameters'"/><t t-set="paramList" t-value="op.parameters.query"/></t>
266
+ <t t-call="baseui.ApiDocParams"><t t-set="paramTitle" t-value="'Header Parameters'"/><t t-set="paramList" t-value="op.parameters.header"/></t>
267
+ <t t-call="baseui.ApiDocParams"><t t-set="paramTitle" t-value="'Form Data'"/><t t-set="paramList" t-value="op.parameters.formData"/></t>
268
+ <details t-if="op.parameters.body" class="param-group" open="open">
269
+ <t t-set="body" t-value="op.parameters.body"/>
270
+ <summary><span class="param-group-head"><span class="caret"/>BODY<t t-if="body.required"> (required)</t></span></summary>
271
+ <p t-if="body.description" class="param-desc" t-esc="body.description"/>
272
+ <p class="param-desc">content type: <code t-esc="body.contentType || 'application/json'"/></p>
273
+ <t t-if="body.encoding">
274
+ <p class="param-desc">encoding:</p>
275
+ <ul class="schema-tree">
276
+ <li t-foreach="encodingEntries(body.encoding)" t-as="entry" t-key="entry[0]">
277
+ <span class="param-name" t-esc="entry[0]"/>
278
+ <span class="param-type"><t t-esc="entry[1].contentType || ''"/><t t-if="entry[1].style"> style=<t t-esc="entry[1].style"/></t></span>
279
+ </li>
280
+ </ul>
281
+ </t>
282
+ <p class="param-desc">example<t t-if="body.exampleIsAuto"> (auto)</t><t t-elif="body.exampleLabel"> (<t t-esc="body.exampleLabel"/>)</t>:</p>
283
+ <pre><code t-esc="formatExample(body.example)"/></pre>
284
+ <t t-call="baseui.ApiDocSchema"><t t-set="schemaNode" t-value="body.schema"/></t>
285
+ </details>
286
+ </section>
287
+
288
+ <section class="doc-section">
289
+ <div class="response-tabs-head"><h2>Responses</h2></div>
290
+ <p t-if="!op.responses.length" class="apidoc-muted">No responses documented.</p>
291
+ <Tabs t-else="" t-key="op.id" value="'' + state.respTab" onChange="(v) => this.selectResponse(v)">
292
+ <div class="response-tabs-head">
293
+ <TabsList className="'response-tablist'" variant="'line'">
294
+ <TabTrigger t-foreach="op.responses" t-as="r" t-key="r.status" value="'' + r_index" className="'tab-status tab-' + statusClass(r.status)"><t t-esc="r.status"/></TabTrigger>
295
+ </TabsList>
296
+ </div>
297
+ <TabsContainer className="'response-card-shell'">
298
+ <TabContent t-foreach="op.responses" t-as="r" t-key="r.status" value="'' + r_index">
299
+ <p t-if="r.summary" class="response-desc"><strong t-esc="r.summary"/></p>
300
+ <p class="response-desc" t-esc="r.description"/>
301
+ <t t-call="baseui.ApiDocParams"><t t-set="paramTitle" t-value="'Response Headers'"/><t t-set="paramList" t-value="r.headers"/></t>
302
+ <div t-if="r.schema" class="response-body">
303
+ <div class="response-body-head">
304
+ <Badge variant="'secondary'" label="r.contentType || 'application/json'"/>
305
+ <div class="view-toggle" role="group">
306
+ <Button size="'sm'" variant="state.respView[r_index] !== 'example' ? 'default' : 'outline'" ariaPressed="state.respView[r_index] !== 'example' ? 'true' : 'false'" label="'Schema'" onClick="() => this.setResponseView(r_index, 'schema')"/>
307
+ <Button size="'sm'" variant="state.respView[r_index] === 'example' ? 'default' : 'outline'" ariaPressed="state.respView[r_index] === 'example' ? 'true' : 'false'" label="r.exampleIsAuto ? 'Example (auto)' : 'Example'" onClick="() => this.setResponseView(r_index, 'example')"/>
308
+ </div>
309
+ </div>
310
+ <pre t-if="state.respView[r_index] === 'example'"><code t-esc="formatExample(r.example)"/></pre>
311
+ <div t-else=""><t t-call="baseui.ApiDocSchema"><t t-set="schemaNode" t-value="r.schema"/></t></div>
312
+ </div>
313
+ <p t-else="" class="apidoc-muted">No response body.</p>
314
+ <details t-if="r.links.length" class="param-group">
315
+ <summary><span class="param-group-head"><span class="caret"/>LINKS</span></summary>
316
+ <ul>
317
+ <li t-foreach="r.links" t-as="l" t-key="l.name" class="param-row">
318
+ <div>
319
+ <div class="param-row-head"><span class="param-name" t-esc="l.name"/></div>
320
+ <p t-if="l.description" class="param-desc" t-esc="l.description"/>
321
+ <p class="param-desc"><t t-if="l.operationId">operationId: <code t-esc="l.operationId"/></t><t t-else="">operationRef: <code t-esc="l.operationRef"/></t></p>
322
+ </div>
323
+ </li>
324
+ </ul>
325
+ </details>
326
+ </TabContent>
327
+ </TabsContainer>
328
+ </Tabs>
329
+ </section>
330
+
331
+ <section t-if="op.callbacks.length" class="doc-section">
332
+ <h2>Callbacks</h2>
333
+ <ul class="schema-tree">
334
+ <li t-foreach="op.callbacks" t-as="cb" t-key="cb_index">
335
+ <span class="method-badge" t-att-data-method="cb.method" t-esc="methodLabel(cb.method)"/>
336
+ <span class="param-name" t-esc="cb.name"/>
337
+ <code class="param-type" t-esc="cb.expression"/>
338
+ <p t-if="cb.summary" class="param-desc" t-esc="cb.summary"/>
339
+ <p t-if="cb.description" class="param-desc" t-esc="cb.description"/>
340
+ </li>
341
+ </ul>
342
+ </section>
343
+ </div>
344
+ </t>
345
+
346
+ <t t-name="baseui.ApiDocResultRequest">
347
+ <div class="result-section">
348
+ <h4 class="result-section-title">Request</h4>
349
+ <details class="param-group" open="open">
350
+ <summary><span class="param-group-head"><span class="caret"/>URL</span></summary>
351
+ <t t-call="baseui.ApiDocCode"><t t-set="codeText" t-value="state.result.sent.url"/></t>
352
+ </details>
353
+ <details class="param-group" open="open">
354
+ <summary><span class="param-group-head"><span class="caret"/>Headers</span></summary>
355
+ <t t-call="baseui.ApiDocCode"><t t-set="codeText" t-value="state.result.sent.headerLines || '(none)'"/></t>
356
+ </details>
357
+ <details t-if="state.result.sent.bodyText" class="param-group" open="open">
358
+ <summary><span class="param-group-head"><span class="caret"/>Body</span></summary>
359
+ <t t-call="baseui.ApiDocCode"><t t-set="codeText" t-value="state.result.sent.bodyText"/></t>
360
+ </details>
361
+ </div>
362
+ </t>
363
+
364
+ <t t-name="baseui.ApiDocTryIt">
365
+ <div class="tryit-stack">
366
+ <section class="sdk-card">
367
+ <div class="sdk-strip" role="tablist" aria-label="Code language">
368
+ <Button t-foreach="languages" t-as="l" t-key="l.id" unstyled="true" className="'lang-chip' + (l.id === currentLanguage.id ? ' is-active' : '')" onClick="() => this.setLanguage(l.id)">
369
+ <span t-attf-class="lang-chip-icon lang-#{languageTone(l.id)}">
370
+ <span class="lang-chip-icon-image" t-att-style="iconUrl(l) ? 'background-image:url(' + iconUrl(l) + ')' : ''"/>
371
+ </span>
372
+ <span class="lang-chip-label" t-esc="l.label"/>
373
+ </Button>
374
+ </div>
375
+ <Badge variant="'secondary'" className="'sdk-current'" label="currentLanguage.label"/>
376
+ <div class="snippet-shell"><t t-call="baseui.ApiDocCode"><t t-set="codeText" t-value="state.snippet"/></t></div>
377
+ </section>
378
+
379
+ <section class="tryit-card">
380
+ <header class="tryit-card-head">
381
+ <h3>Request</h3>
382
+ <Button variant="'ghost'" size="'sm'" className="'head-action'" label="'Collapse All'" onClick="() => this.collapseRequest()"/>
383
+ </header>
384
+ <div class="tryit-card-body">
385
+ <details open="open">
386
+ <summary><span class="param-group-head"><span class="caret"/>Base URL</span></summary>
387
+ <div class="tryit-fields">
388
+ <NativeSelect value="draft.baseUrl" onChange="(v) => this.onBaseUrl(v)">
389
+ <option t-foreach="serverOptions" t-as="server" t-key="server.url" t-att-value="server.url"><t t-esc="serverLabel(server)"/></option>
390
+ </NativeSelect>
391
+ </div>
392
+ </details>
393
+
394
+ <details t-if="tryItParams.length" open="open">
395
+ <summary><span class="param-group-head"><span class="caret"/>Parameters</span></summary>
396
+ <div class="tryit-fields">
397
+ <label t-foreach="tryItParams" t-as="p" t-key="p.group + p.name" class="tryit-field">
398
+ <span><t t-esc="p.name"/> <span class="apidoc-muted">— <t t-esc="p.group"/><t t-if="p.required"> REQUIRED</t></span></span>
399
+ <NativeSelect t-if="p.enum" value="draft[p.group][p.name]" onChange="(v) => this.onParam(v, p.group, p.name)">
400
+ <option t-foreach="p.enum" t-as="v" t-key="v_index" t-att-value="v" t-esc="v"/>
401
+ </NativeSelect>
402
+ <Input t-else="" value="draft[p.group][p.name] ?? ''" placeholder="p.description || (p.required ? 'required' : 'optional')" onInput="(v) => this.onParam(v, p.group, p.name)"/>
403
+ </label>
404
+ </div>
405
+ </details>
406
+
407
+ <details t-if="op.parameters.body" open="open">
408
+ <summary><span class="param-group-head"><span class="caret"/>Body (<t t-esc="draft.mimeType"/>)</span></summary>
409
+ <div class="tryit-fields">
410
+ <Textarea rows="8" value="draft.bodyText" onInput="(v) => this.onBody(v)"/>
411
+ </div>
412
+ </details>
413
+
414
+ <Button className="'apidoc-btn'" disabled="!!(state.result and state.result.phase === 'sending')" label="'Send API Request'" onClick="() => this.send()"/>
415
+ </div>
416
+ </section>
417
+
418
+ <section class="tryit-card">
419
+ <header class="tryit-card-head">
420
+ <h3>Response</h3>
421
+ <Button variant="'ghost'" size="'sm'" className="'response-clear'" label="'Clear'" onClick="() => this.clearResult()"/>
422
+ </header>
423
+ <div class="tryit-card-body">
424
+ <div t-if="!state.result" class="response-placeholder">Click the <code>Send API Request</code> button above and see the response here!</div>
425
+ <t t-elif="state.result.phase === 'sending'">
426
+ <div class="tryit-result-head">
427
+ <Badge variant="'muted'" label="state.result.sent.method"/>
428
+ <span class="apidoc-muted">Sending…</span>
429
+ </div>
430
+ <t t-call="baseui.ApiDocResultRequest"/>
431
+ </t>
432
+ <t t-else="">
433
+ <t t-call="baseui.ApiDocResultRequest"/>
434
+ <div class="result-section">
435
+ <h4 class="result-section-title">Response</h4>
436
+ <t t-if="state.result.phase === 'done'">
437
+ <div class="tryit-result-head">
438
+ <span class="status-badge" t-att-data-status="statusClass(state.result.res.status)"><t t-esc="state.result.res.status"/><t t-if="state.result.res.statusText"> <t t-esc="state.result.res.statusText"/></t></span>
439
+ <span class="apidoc-muted"><t t-esc="state.result.res.ms"/> ms</span>
440
+ </div>
441
+ <details class="param-group">
442
+ <summary><span class="param-group-head"><span class="caret"/>Headers</span></summary>
443
+ <t t-call="baseui.ApiDocCode"><t t-set="codeText" t-value="state.result.res.headerLines || '(none)'"/></t>
444
+ </details>
445
+ <t t-call="baseui.ApiDocCode"><t t-set="codeText" t-value="state.result.res.bodyText"/></t>
446
+ </t>
447
+ <t t-else="">
448
+ <div class="tryit-result-head"><Badge variant="'danger'" label="'Request failed'"/></div>
449
+ <p class="param-desc"><t t-esc="state.result.message"/>. This is usually a CORS restriction or network error — check the browser console for details.</p>
450
+ </t>
451
+ </div>
452
+ </t>
453
+ </div>
454
+ </section>
455
+ </div>
456
+ </t>
457
+
458
+ <t t-name="baseui.ApiDocStoredField">
459
+ <div class="auth-field">
460
+ <label class="tryit-field auth-field">
461
+ <span class="auth-field-label" t-esc="fieldLabel"/>
462
+ <div class="auth-input-row">
463
+ <Input value="state.inputs[fieldName]" disabled="!!state.stored[fieldName]" placeholder="fieldPlaceholder" onInput="(v) => this.setAuthInput(fieldName, v)"/>
464
+ <Button variant="'outline'" size="'sm'" className="'auth-clean-button' + (state.stored[fieldName] ? '' : ' is-hidden')" label="'Clean'" onClick="() => this.clearAuth(fieldName)"/>
465
+ </div>
466
+ </label>
467
+ <p class="param-desc auth-field-help"><t t-esc="fieldHelp"/></p>
468
+ </div>
469
+ </t>
470
+
471
+ <t t-name="baseui.ApiDocAuthBody">
472
+ <div class="auth-global-fields">
473
+ <t t-call="baseui.ApiDocStoredField">
474
+ <t t-set="fieldName" t-value="'apikey'"/>
475
+ <t t-set="fieldLabel" t-value="'API Key'"/>
476
+ <t t-set="fieldPlaceholder" t-value="'Enter API key'"/>
477
+ <t t-set="fieldHelp" t-value="'Stored in this tab\'s session storage as apikey. Used for apiKey-type schemes and as the Basic auth credential (Authorization: Basic &lt;API Key&gt;).'"/>
478
+ </t>
479
+ <t t-call="baseui.ApiDocStoredField">
480
+ <t t-set="fieldName" t-value="'oauthkey'"/>
481
+ <t t-set="fieldLabel" t-value="'Bearer / OAuth token'"/>
482
+ <t t-set="fieldPlaceholder" t-value="'Bearer token (auto-filled by Authorize/Get token below)'"/>
483
+ <t t-set="fieldHelp" t-value="'Stored in this tab\'s session storage as oauthkey. Used for bearer, openIdConnect and oauth2 schemes as Authorization: Bearer &lt;token&gt;.'"/>
484
+ </t>
485
+ </div>
486
+ <p t-if="!authSchemes.length" class="apidoc-muted auth-empty-state">No authorization schemes defined by this spec.</p>
487
+ <div t-else="" class="auth-schemes">
488
+ <details t-foreach="authSchemes" t-as="scheme" t-key="scheme.key" class="param-group auth-scheme" open="open">
489
+ <summary>
490
+ <span t-esc="scheme.key"/>
491
+ <Badge variant="'secondary'" label="scheme.def.type"/>
492
+ <Badge t-if="scheme.def.deprecated" variant="'warning'" label="'Deprecated'"/>
493
+ </summary>
494
+ <p t-if="scheme.def.description" class="param-desc" t-esc="scheme.def.description"/>
495
+ <p t-if="scheme.def.oauth2MetadataUrl" class="param-desc">Metadata: <a t-att-href="scheme.def.oauth2MetadataUrl" target="_blank" rel="noopener noreferrer" t-esc="scheme.def.oauth2MetadataUrl"/></p>
496
+ <p t-if="scheme.def.type === 'apiKey'" class="param-desc">Sent as <t t-esc="scheme.def.in === 'query' ? 'query parameter' : 'header'"/> <code t-esc="scheme.def.name"/> using the <strong>API Key</strong> above.</p>
497
+ <p t-elif="scheme.def.type === 'basic'" class="param-desc">Sent as <code>Authorization: Basic &lt;API Key&gt;</code> using the <strong>API Key</strong> above.</p>
498
+ <p t-elif="scheme.def.type === 'bearer'" class="param-desc">Sent as <code>Authorization: Bearer &lt;token&gt;</code> using the <strong>Bearer / OAuth token</strong> above.</p>
499
+ <p t-elif="scheme.def.type === 'mutualTLS'" class="param-desc">This scheme authenticates via a client TLS certificate negotiated at the transport layer — there is nothing to fill in here.</p>
500
+ <t t-elif="scheme.def.type === 'oauth2'">
501
+ <t t-set="fields" t-value="state.oauth[scheme.key]"/>
502
+ <t t-set="flow" t-value="scheme.def.flow"/>
503
+ <p class="param-desc">Flow: <code t-esc="oauthFlowLabel(flow)"/></p>
504
+ <div t-if="scheme.scopes.length" class="tryit-field auth-field">
505
+ <span class="auth-field-label">Scopes</span>
506
+ <ul class="auth-scope-list">
507
+ <li t-foreach="scheme.scopes" t-as="scope" t-key="scope[0]">
508
+ <Checkbox checked="fields.scopes.includes(scope[0])" onChange="() => this.toggleScope(scheme.key, scope[0])"><code t-esc="scope[0]"/> — <t t-esc="scope[1]"/></Checkbox>
509
+ </li>
510
+ </ul>
511
+ </div>
512
+ <label class="tryit-field auth-field">
513
+ <span class="auth-field-label">Client ID</span>
514
+ <Input type="'text'" value="fields.clientId" onInput="(v) => this.setField(fields, 'clientId', v)"/>
515
+ </label>
516
+ <t t-if="flow === 'accessCode' or flow === 'application' or flow === 'password'">
517
+ <label class="tryit-field auth-field">
518
+ <span class="auth-field-label">Client secret</span>
519
+ <Input type="'password'" value="fields.clientSecret" onInput="(v) => this.setField(fields, 'clientSecret', v)"/>
520
+ </label>
521
+ <p class="param-desc auth-field-help">Client secret is sent from the browser — only use a test/dev OAuth client here.</p>
522
+ </t>
523
+ <t t-if="flow === 'password'">
524
+ <label class="tryit-field auth-field">
525
+ <span class="auth-field-label">Username</span>
526
+ <Input type="'text'" value="fields.username" onInput="(v) => this.setField(fields, 'username', v)"/>
527
+ </label>
528
+ <label class="tryit-field auth-field">
529
+ <span class="auth-field-label">Password</span>
530
+ <Input type="'password'" value="fields.password" onInput="(v) => this.setField(fields, 'password', v)"/>
531
+ </label>
532
+ </t>
533
+ <div t-if="flow" class="tryit-field auth-field auth-action-row">
534
+ <Button variant="'outline'" size="'sm'" label="flow === 'implicit' or flow === 'accessCode' ? 'Authorize' : 'Get token'" onClick="() => this.startOAuth2Flow(scheme.key)"/>
535
+ <p class="param-desc auth-field-help" t-att-data-state="state.oauthStatus[scheme.key] ? (state.oauthStatus[scheme.key].isError ? 'error' : 'ok') : null" t-esc="state.oauthStatus[scheme.key] ? state.oauthStatus[scheme.key].message : ''"/>
536
+ </div>
537
+ </t>
538
+ <p t-else="" class="param-desc">Unsupported scheme type: <t t-esc="scheme.def.type"/></p>
539
+ </details>
540
+ </div>
541
+ </t>
92
542
  <t t-name="baseui.accordion">
93
543
  <div b-ui="accordion"><t t-slot="default"/></div>
94
544
  </t>
@@ -1312,18 +1762,36 @@
1312
1762
  </t>
1313
1763
  <t t-name="Icon" t-call="baseui.Icon"/>
1314
1764
  <t t-name="baseui.Input">
1765
+ <div t-if="isOutlined" t-att-class="outlinedClassNames">
1766
+ <div class="bu-input-box">
1767
+ <Icon t-if="props.leadingIcon" name="props.leadingIcon" className="'bu-input-icon bu-input-leading'"/>
1768
+ <t t-call="baseui.Input.field"/>
1769
+ <span class="bu-input-label" t-esc="props.label"/>
1770
+ <Icon t-if="trailingIcon" name="trailingIcon" className="'bu-input-icon bu-input-trailing'"/>
1771
+ </div>
1772
+ <div t-if="supportingText or props.maxLength" class="bu-input-support">
1773
+ <span t-att-id="supportId" t-esc="supportingText"/>
1774
+ <span t-if="props.maxLength" class="bu-input-counter" t-esc="counter"/>
1775
+ </div>
1776
+ </div>
1777
+ <t t-else="" t-call="baseui.Input.field"/>
1778
+ </t>
1779
+ <t t-name="baseui.Input.field">
1315
1780
  <input
1316
1781
  t-att-class="classNames"
1317
1782
  t-att-style="props.style"
1318
1783
  t-att-type="props.type || 'text'"
1319
- t-att-placeholder="props.placeholder"
1784
+ t-att-placeholder="fieldPlaceholder"
1320
1785
  t-att-value="props.value"
1321
1786
  t-att-disabled="props.disabled"
1322
1787
  t-att-readonly="props.readonly"
1323
1788
  t-att-id="props.id"
1324
1789
  t-att-list="props.list"
1325
1790
  t-att-autocomplete="props.autocomplete"
1791
+ t-att-maxlength="props.maxLength"
1326
1792
  t-att-aria-label="ariaLabel"
1793
+ t-att-aria-invalid="isInvalid ? 'true' : undefined"
1794
+ t-att-aria-describedby="supportingText ? supportId : undefined"
1327
1795
  t-att-min="props.min"
1328
1796
  t-att-max="props.max"
1329
1797
  t-att-step="props.step"
Binary file
Binary file
Binary file
Binary file
Binary file