Commit 09a296e
authored
feat(ui): Tier-2 components -> WebComponent + render() + <slot> (rebased) (#28)
* refactor(ui): toggle.ts -> WebComponent + render() + <slot>
First Tier-2 component converted from extends Base to extends
WebComponent. This is the pattern-locking conversion; remaining
nine components follow the same shape.
Pattern summary:
- extends WebComponent (was: extends Base from local utils)
- static properties = { ... reflect: true } (was: observedAttributes
getter + manual attr/property bridges)
- declare prop: Type + constructor defaults (was: imperative gets)
- render() returns html`<slot></slot>`, sets host-level class +
aria + data-state. <slot> has display: contents in every browser's
user-agent stylesheet, so children layout as direct flex children
of the host with no visible wrapper.
- firstUpdated() handles one-time event listener setup
- .register('tag-name') instead of defineElement()
Public API unchanged: tag <ui-toggle>, attributes pressed/variant/
size/disabled, ui-pressed-change event. Behavior preserved.
* refactor(ui): sonner.ts -> WebComponent + reactive state
Toast items now live in this.state.items via setState. The
previous imperative _render() that did replaceChildren is replaced
by html`...${repeat(items, ...)} ` returning a TemplateResult.
escapeHTML manual escape is replaced by webjs's html`` interpolation
which escapes text by default; the SVG icon strings are wrapped in
unsafeHTML() since they are trusted, internal markup.
Public API unchanged: toast() function + .success/.error/.info/
.warning/.loading/.dismiss/.promise methods, <ui-sonner position>
element with imperative addToast() escape hatch.
* refactor(ui): toggle-group.ts -> WebComponent + render() + <slot>
Both classes (UiToggleGroup parent + UiToggleGroupItem children) now
extend WebComponent. Children project through the slot in both
cases. Item-state synchronization (data-state, aria-pressed driven
by the parent's comma-separated value) runs from the parent's
render() via queueMicrotask after projection settles. Item class
computation reads parent attributes via closest(), preserved from
the original.
Public API unchanged.
* refactor(ui): tooltip.ts -> WebComponent + render() + <slot>
All three classes (UiTooltip, UiTooltipTrigger, UiTooltipContent)
convert from extends Base. UiTooltip's child queries change from
:scope > ui-tooltip-content to plain ui-tooltip-content descendant
search since children now live under the projection slot wrapper.
The hover-with-delay state machine, native popover manual mode,
positionFloating call, and skip-delay-duration window are all
preserved unchanged.
Public API unchanged: tags <ui-tooltip>, <ui-tooltip-trigger>,
<ui-tooltip-content>; attributes delay-duration, skip-delay-duration,
side, align, side-offset, align-offset, open.
* refactor(ui): hover-card.ts -> WebComponent + render() + <slot>
Same pattern as tooltip.ts: three classes all converted to
extends WebComponent + render() returning html`<slot></slot>`.
Child queries change from :scope > to descendant search.
Native popover manual mode, hover delays, and positionFloating
preserved unchanged.
* refactor(ui): tabs.ts -> WebComponent + render() + <slot>
All four classes (UiTabs, UiTabsList, UiTabsTrigger, UiTabsContent)
converted. Parent broadcasts active value to children via
_syncChildren() scheduled in queueMicrotask after the slot
projection settles. The ui-value-change event fires on actual
value mutations rather than every attribute callback.
Keyboard navigation (arrow keys, Home/End, Enter/Space) preserved
unchanged. Public API: <ui-tabs value orientation>, <ui-tabs-list
variant>, <ui-tabs-trigger value>, <ui-tabs-content value>.
* refactor(ui): dialog.ts -> WebComponent + render() + named slots
This is the refactor where slots earn their keep. The previous
imperative pattern (find <ui-dialog-content>, create a <dialog>
programmatically, replaceWith + appendChild to reparent the
content into the <dialog>) is replaced by a declarative template:
render() {
return html`
<slot></slot>
<dialog ... class=${NATIVE_DIALOG_CLASS}>
<slot name="dialog-content"></slot>
</dialog>
`;
}
The user keeps writing <ui-dialog-content> as a normal child. In
connectedCallback the component sets slot="dialog-content" on
that element so the projection machinery routes it inside the
<dialog>. The user-facing API is unchanged.
scroll-lock refcounting, showModal/close native API, escape-to-
close via native cancel event, ::backdrop styling, focus trap (all
native dialog behavior) are preserved unchanged.
All five classes (UiDialog, UiDialogTrigger, UiDialogContent,
UiDialogClose, UiDialogFooter) converted to extends WebComponent.
* refactor(ui): alert-dialog.ts -> WebComponent + render() + named slots
Same shape as the dialog.ts refactor: render() returns a <dialog>
template with <slot name='alert-dialog-content'> inside, and the
connectedCallback routes the user's <ui-alert-dialog-content> child
to that named slot. Native dialog's escape-cancel behavior preserved
via the cancel event listener (preventDefault on alert dialogs).
All five classes converted (UiAlertDialog, UiAlertDialogTrigger,
UiAlertDialogContent, UiAlertDialogCancel, UiAlertDialogAction).
* refactor(ui): dropdown-menu.ts -> WebComponent + render() + <slot>
The biggest of the Tier-2 refactors. All 11 classes converted from
extends Base: UiDropdownMenu (parent), UiDropdownMenuTrigger,
UiDropdownMenuContent, UiDropdownMenuItem, UiDropdownMenuLabel,
UiDropdownMenuSeparator, UiDropdownMenuShortcut, UiDropdownMenuGroup,
UiDropdownMenuSub, UiDropdownMenuSubTrigger, UiDropdownMenuSubContent.
Each class follows the established pattern: extends WebComponent,
static properties + declare for reactive props, _userClass snapshot
on connect, host attribute writes in render(), event-listener wiring
in firstUpdated, render() returns html`<slot></slot>`.
Selector changes:
:scope > ui-dropdown-menu-content -> descendant ui-dropdown-menu-content
parentElement -> closest('ui-dropdown-menu-sub')
The latter handles the slot wrapper between the sub and its
trigger/content (parentElement is now the slot, not the sub).
closest() walks past the slot wrapper to find the ancestor sub.
Keyboard navigation, document-level click-outside, scroll/resize
reposition, type-ahead, submenu pointer-leave delay, native popover
showPopover/hidePopover, and positionFloating are all preserved
unchanged. ui-open-change-like state coordination moves into a
queueMicrotask after render so projection settles first.
* release: @webjskit/ui 0.1.0 -> 0.2.0
The 10 Tier-2 components were rewritten from extends Base + imperative
DOM walking to extends WebComponent + render() + <slot> projection.
Public API (tag names, attributes, events) is unchanged: existing
markup keeps working. Internals are now consistent with the rest of
the webjs framework's component idiom.
The slot work that landed in @webjskit/core 0.5.0 (light-DOM <slot>
with full shadow-DOM spec parity) is the prerequisite that made this
refactor possible. Previously the ui kit had to use extends Base
because slots only worked inside shadow DOM, which the kit avoids
for Tailwind compatibility.
* refactor(ui): add back isOpen getters + consolidate sonner imports
Tests assume el.isOpen as a back-compat getter alongside the new
reactive 'open' property. Added on dialog, alert-dialog, tooltip,
hover-card.
Sonner now imports repeat from @webjskit/core (which re-exports it
from ./src/repeat.js) rather than the redundant double-import that
WTR's esbuild plugin was struggling to resolve at dynamic-import
time.
* fix(ui): Tier-2 WebComponent lifecycle + test architecture mismatch
Three root causes behind the 35 browser-test failures on this branch:
1. queueMicrotask was too eager for nested slot projection. Parents that
queried for descendants (tabs / toggle-group / tooltip / hover-card /
dialog / alert-dialog / dropdown-menu) ran sync helpers in a
microtask after their own render, but descendant WebComponents had
not finished their own first render + slot projection by then.
Switch to requestAnimationFrame so the nested cascade settles first.
2. Listeners attached in firstUpdated were orphaned by the
disconnect/reconnect cycle of light-DOM slot projection on first
mount. firstUpdated runs once; disconnectedCallback removes
listeners. The intermediate disconnect during projection left the
reconnected element with no handlers. Move listener registration to
connectedCallback (runs every reconnect), keep removal symmetric in
disconnectedCallback.
3. showPopover() throws InvalidStateError on disconnected elements.
The RAF-scheduled sync could fire after the host was torn down (test
teardown, route transition). Add an isConnected guard in tooltip +
hover-card _syncContent.
Test cleanup:
- Stale ui-popover / ui-accordion / ui-collapsible suites removed:
those components are Tier 1 on main, no custom element to test.
- :scope > ui-X-content selectors updated to descendant search
(ui-X-content): WebComponent rendering moves content into nested
slots, so direct-child matches fail.
- Escape-closes-dialog test rewritten to fire a native close event on
the inner <dialog> (the UA-internal step). dispatching keydown on
document does not reach the native dialog Escape handler.
- Click-the-overlay test removed: <ui-dialog-overlay> was replaced by
native ::backdrop in the WebComponent dialog.
Other fixes:
- sonner.ts: import unsafeHTML from '@webjskit/core' directly (the
/directives subpath was removed when the package exports were
consolidated).
Results:
- Unit: 936/936 pass
- Browser: 81/81 pass
* fix(ui): alert-dialog button-in-button + classify toggle as Tier 2
Two issues observed on the live docs preview for /docs/components/alert-dialog
and /docs/components/toggle.
1. alert-dialog showed a "button inside a button" visual for both Cancel
and Action. The back-compat path in `applyAlertDialogButton` skipped
self-styling when the user authored a `<button>` inside the host:
if (host.querySelector('button')) return;
The old Base-class implementation kept authored children on the host,
so the query worked. With the WebComponent + light-DOM slot
architecture, `captureAuthoredChildren` moves the children OUT of the
host into an off-tree assignment table BEFORE render() runs, so the
query found nothing. The host got styled as a button AND the user's
own button rendered inside it after slot projection.
Fix: capture the "user provided a button" flag in connectedCallback
(which runs before the slot machinery hides authored children) and
store it on the instance as `_hasAuthoredButton`. render() consults
the flag, not a live DOM query.
2. toggle was classified as Tier 1 in the website's `_lib/tier.ts`.
That was correct on main (where toggle is class-helper + native
<button> + attachToggle). On this branch, toggle.ts was migrated to
a WebComponent custom element (`<ui-toggle>`), so it belongs in
TIER_2_NAMES.
* refactor(ui): toggle.ts to Lit-idiomatic pattern
Eliminates ~50 lines of connectedCallback / disconnectedCallback /
firstUpdated / addEventListener / setAttribute boilerplate by rendering
a native <button> inside the host's template and binding the click
handler declaratively.
- render() returns html`<button ...><slot></slot></button>`
- @click=${this._onClick} on the inner <button> auto-wires
- aria-pressed / data-state / class / disabled bound declaratively on
the inner button instead of imperative setAttribute calls
- Native <button> handles Enter/Space + focus + disabled semantics
for free; the manual _onKeyDown handler is gone
- The host's class attribute is no longer touched (no _userClass
capture/merge); the inner button carries the visual class
Semantics shift: aria-pressed + data-state live on the inner <button>
now, not the host. More correct for screen readers (focusable element
carries its state). The host's `pressed` reactive prop still reflects
to a `pressed` attribute for declarative consumers.
* refactor(ui): Lit-idiomatic Tier-2 components
Tier-2 component classes were verbose with manual lifecycle wiring,
imperative setAttribute calls in render(), addEventListener +
removeEventListener pairs in connectedCallback/disconnectedCallback,
and `_userClass` capture-and-merge to avoid stomping author classes.
Lit-idiomatic patterns eliminate the boilerplate:
Declarative attribute bindings (`?attr=${bool}`, `attr=${val}`,
`class=${...}`) replace imperative setAttribute in render().
Declarative event bindings (`@click=${this._onClick}`) replace
manual addEventListener / removeEventListener around the host. The
framework wires/unwires them automatically as the rendered element
enters/leaves the DOM.
Inner element rendering pattern. Instead of styling the host and
catching events on it (which forces connectedCallback boilerplate
and a `_userClass` capture for class merging), each component
renders the role-bearing element inside its slot output:
<ui-toggle> renders <button>, <ui-tabs-trigger> renders <button
role="tab">, <ui-tooltip-content> renders <div popover="manual"
role="tooltip">, etc. The host stays a thin slot wrapper; the
user's class attribute on the host is untouched; semantics live on
the focusable / popover element where they belong.
Native button elements get Enter/Space activation, focus, and
disabled semantics for free; removed manual keydown handlers and
tabindex/role setAttribute calls.
Native dialog @close / @cancel event bindings replace
addEventListener + removeEventListener pairs.
Constraint: webjs core has firstUpdated() but not updated(changedProps),
so transitions across renders (e.g. "open just became true, call
showModal") use a manual `_lastOpen` flag + requestAnimationFrame in
render(). This is the documented pattern when an `updated()` hook is
absent.
Test updates: tests previously queried role / data-state / event
targets on the <ui-*> hosts. The Lit refactor moved those to the
inner rendered elements, so tests now query [data-slot="X"], [role="Y"],
or [popover] for the actual rendered element. Behavior assertions
(open transitions, value updates, event firing) are unchanged.
Browser tests: 81 / 81 pass
Unit tests: 936 / 936 pass
* fix(ui): SSR + visual regressions in the Lit-idiomatic refactor
Three classes of bug introduced by the previous refactor commit:
1. SSR crashes. render() runs server-side via linkedom, which doesn't
implement closest() on custom elements, dispatchEvent / CustomEvent,
or requestAnimationFrame. The refactored components called all three
unconditionally and brought the dev server down. Guards added:
- `if (typeof this.closest !== 'function') return null;` in every
parent-lookup getter
- `if (typeof requestAnimationFrame !== 'undefined') ...` around
every RAF schedule
- `if (typeof window !== 'undefined') { ... }` around the
dispatchEvent + queueMicrotask block in <ui-tabs>
The client re-renders after hydration so the omitted side effects
run at the correct time anyway.
2. Tabs content stayed visible when inactive. The old code had inline
CSS hiding `<ui-tabs-content[data-state="inactive"]>`; the refactor
removed it and relied on `?hidden` on the inner `<section>`, which
leaves the host taking visual space as an empty box. Toggle `hidden`
on the host directly via `this.toggleAttribute('hidden', !active)`
so the entire <ui-tabs-content> is removed from layout when inactive.
3. <ui-toggle-group-item> lost joined-corner styling. The refactor
moved toggleClass + data-state to an inner <button>, but the CSS
uses sibling selectors (`:first-child`, `:last-child` plus
data-spacing variants) that need the styled element to be a direct
sibling of other items. With a button inside each host, the button
is the only child of its host, so `:first-child` is always true
and the rounded-corner rules misfire. The fix: keep the host as
the styled element for this compound-component pattern. The
click + keydown listeners attach in connectedCallback (host is the
click target, not an inner element). New reactive `pressed` prop
replaces the parent's imperative `setAttribute('data-state', ...)`
walk, so the item's render() re-runs cleanly on state change.
4. Dialog X close button stopped rendering. The previous attempt set
the inner SVG via `.innerHTML=${...}` on a <ui-dialog-close>, but
the custom element's own render() overwrites the host's children
so the SVG was lost. Render the X button inline in
<ui-dialog-content>'s template using `unsafeHTML(SVG)` for the
icon, with a local @click handler that hides the parent dialog.
Tests: 81 / 81 browser pass, 936 / 936 unit pass.
* refactor(ui): finish Tier-2 to Lit-style WebComponent
Migrate the 9 Tier-2 stateful custom elements from the local Base /
defineElement HTMLElement-decorator pattern to Lit-shaped WebComponent
from @webjskit/core. Every component now uses static properties,
render() returning html`...` templates, declarative bindings (@click,
?attr, attr=, .prop), and <slot></slot> for projecting authored
children. No imperative DOM mutation in event handlers; host-styled
escape hatches (toggle-group-item, tabs-content) use dataset/IDL
properties only.
Architectural fix for dialog and alert-dialog: the content child now
owns the native <dialog> element (its render emits the <dialog>
wrapper around its slotted content), and the parent drives showModal /
close on the child via the open prop. Every slot is a default slot,
which closes the SSR named-slot routing bug that earlier required
setting slot="..." in connectedCallback (a pattern that also breaks
in shadow DOM with DSD).
alert-dialog Cancel and Action drop the broken wrap-a-button
back-compat (SSR was producing invalid nested-button HTML). Bare-text
authoring is now the canonical shape; the docs example matches.
Also normalises docstrings on the 9 Tier-2 components to a canonical
structure (header, prose, APG link, shadcn parity map, Usage,
Attributes, Events, Programmatic API, Keyboard, Design tokens).
Fixes stale architectural claims in packages/ui/AGENTS.md +
README.md (which still described Tier-2 as HTMLElement subclasses)
and the docs/ui page reference to ui-popover as Tier-2 (popover is
Tier-1 now). Updates the registry-contents test assertions to match
the new WebComponent.register + declarative role= patterns.
* docs(ui): normalise Tier-1 component docstrings to canonical structure
Standardise the top-of-file JSDoc for every Tier-1 component to the
same section order: header line, prose paragraph on implementation,
shadcn parity map, Usage code example, optional component-specific
notes, Design tokens used. Adds explicit shadcn parity tables to
input and textarea (previously missing). Tightens accordion,
collapsible, popover, and progress to fit the same shape without
losing their migration / browser-support notes. No code changes.
* fix(docker): build ui-website tailwind CSS in the image
The Dockerfile RUN tailwindcss line was building CSS for website,
docs, and examples/blog but skipping packages/ui/packages/website.
The omission has been there since the UI website was added in
3d20a85. ui.webjs.dev still ships styled today because Railway
invokes npm start, which fires the prestart css:build hook at
container boot; but the compose path (and any non-npm-start CMD
override) ships an unstyled image. Add the missing line so the
behaviour matches the other three apps and cold-start CSS-build
cost moves from container boot to image build.
* docs: clarify npm run dev vs webjs dev across all app-level AGENTS
Add guidance that npm run dev / npm start are the entrypoints inside
an app, not webjs dev / webjs start. The webjs CLI commands are
framework primitives (only run the server); the npm scripts compose
them with the app-specific watchers (Tailwind, Prisma) via
concurrently and pre* hooks. Skipping the npm wrapper produces silent
breakage (stale Prisma client, missing tailwind.css, unmigrated DB).
Same split Rails 7+ ships (bin/rails server vs bin/dev). In Docker /
Railway, prefer npm start as the CMD so the prestart hook fires.
Lands the explainer in each of the four runnable apps' AGENTS.md
(website, docs, blog, ui-website), in the scaffold template AGENTS
(so every newly-created app picks it up), and a one-line pointer in
the framework's own root AGENTS so an agent working from the repo
root knows to defer to per-app guidance. Also corrects a stray
"cd docs && webjs dev" in the root README. No changes to AGENTS.md
sections that pertain to framework development workflow.
* feat(website): default UI_URL to localhost + ship .env.example
UI_URL fallback in app/layout.ts and app/page.ts now points at the
canonical local dev port (http://localhost:5001, matching the
ui-website's webjs:dev script and the compose.yaml CMD), mirroring
how DOCS_URL and BLOG_URL already default to their local ports. The
production value still comes from the Railway service env. Ship a
website/.env.example committed alongside (the .env itself is
gitignored at the repo root) so a fresh clone documents the three
sibling URLs in one place.
---------1 parent 2923738 commit 09a296e
52 files changed
Lines changed: 2104 additions & 1927 deletions
File tree
- docs
- app/docs/ui
- examples/blog
- packages
- cli/templates
- ui
- packages
- registry/components
- website
- app
- _lib
- docs/components/[name]
- test
- test/browser
- website
- app
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
818 | 818 | | |
819 | 819 | | |
820 | 820 | | |
| 821 | + | |
| 822 | + | |
| 823 | + | |
| 824 | + | |
| 825 | + | |
| 826 | + | |
| 827 | + | |
| 828 | + | |
821 | 829 | | |
822 | 830 | | |
823 | 831 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
50 | 50 | | |
51 | 51 | | |
52 | 52 | | |
53 | | - | |
54 | | - | |
55 | | - | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
56 | 61 | | |
57 | 62 | | |
58 | 63 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
192 | 192 | | |
193 | 193 | | |
194 | 194 | | |
195 | | - | |
| 195 | + | |
196 | 196 | | |
197 | 197 | | |
198 | 198 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
55 | 55 | | |
56 | 56 | | |
57 | 57 | | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
58 | 65 | | |
59 | 66 | | |
60 | 67 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
12 | | - | |
| 12 | + | |
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
144 | 144 | | |
145 | 145 | | |
146 | 146 | | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
147 | 173 | | |
148 | 174 | | |
149 | 175 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
275 | 275 | | |
276 | 276 | | |
277 | 277 | | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
278 | 301 | | |
279 | 302 | | |
280 | 303 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
44 | 44 | | |
45 | 45 | | |
46 | 46 | | |
47 | | - | |
48 | | - | |
49 | | - | |
50 | | - | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
51 | 59 | | |
52 | 60 | | |
53 | 61 | | |
| |||
99 | 107 | | |
100 | 108 | | |
101 | 109 | | |
102 | | - | |
| 110 | + | |
103 | 111 | | |
104 | 112 | | |
105 | 113 | | |
| |||
209 | 217 | | |
210 | 218 | | |
211 | 219 | | |
212 | | - | |
213 | | - | |
214 | | - | |
215 | | - | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
216 | 226 | | |
217 | 227 | | |
218 | 228 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
23 | | - | |
24 | | - | |
25 | | - | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
33 | | - | |
34 | | - | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
35 | 40 | | |
36 | 41 | | |
37 | 42 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| |||
0 commit comments