@blotch/dolan 22.2.4 → 22.2.6
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.
- package/dist/commands/add.test.js +2 -0
- package/dist/commands/add.test.js.map +1 -1
- package/dist/commands/doctor.js +78 -21
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/doctor.test.js +167 -26
- package/dist/commands/doctor.test.js.map +1 -1
- package/dist/commands/init.test.js +4 -7
- package/dist/commands/init.test.js.map +1 -1
- package/dist/commands/update.d.ts +3 -1
- package/dist/commands/update.js +78 -15
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/update.test.js +257 -2
- package/dist/commands/update.test.js.map +1 -1
- package/dist/lockfile.d.ts +10 -0
- package/dist/lockfile.js +20 -1
- package/dist/lockfile.js.map +1 -1
- package/dist/lockfile.test.js +43 -3
- package/dist/lockfile.test.js.map +1 -1
- package/dist/registry.js +73 -11
- package/dist/registry.js.map +1 -1
- package/dist/registry.test.js +16 -3
- package/dist/registry.test.js.map +1 -1
- package/dist/test-utils.d.ts +24 -2
- package/dist/test-utils.js +41 -2
- package/dist/test-utils.js.map +1 -1
- package/dist/ui/boolean-inputs.test.ts +76 -0
- package/dist/ui/components/action-sheet/action-sheet.test.ts +370 -1
- package/dist/ui/components/action-sheet/action-sheet.ts +306 -28
- package/dist/ui/components/action-sheet/index.ts +1 -0
- package/dist/ui/components/alert-dialog/alert-dialog.test.ts +552 -8
- package/dist/ui/components/alert-dialog/alert-dialog.ts +644 -36
- package/dist/ui/components/alert-dialog/index.ts +5 -0
- package/dist/ui/components/area-chart/area-chart.ts +3 -2
- package/dist/ui/components/badge/badge.ts +2 -1
- package/dist/ui/components/bottom-sheet/bottom-sheet.test.ts +968 -3
- package/dist/ui/components/bottom-sheet/bottom-sheet.ts +1021 -95
- package/dist/ui/components/bottom-sheet/index.ts +7 -1
- package/dist/ui/components/button/button.test.ts +36 -1
- package/dist/ui/components/button/button.ts +17 -7
- package/dist/ui/components/button-group/button-group.test.ts +53 -9
- package/dist/ui/components/button-group/button-group.ts +67 -21
- package/dist/ui/components/card/card.test.ts +16 -0
- package/dist/ui/components/card/card.ts +2 -1
- package/dist/ui/components/chart/chart-presets.ts +6 -6
- package/dist/ui/components/chart/chart.test.ts +14 -0
- package/dist/ui/components/chart/chart.ts +10 -3
- package/dist/ui/components/checkbox/checkbox.ts +2 -1
- package/dist/ui/components/collapsible/collapsible.ts +2 -1
- package/dist/ui/components/dialog/dialog.ts +6 -6
- package/dist/ui/components/dock/dock-scroll.ts +9 -0
- package/dist/ui/components/dock/dock-search.test.ts +594 -0
- package/dist/ui/components/dock/dock.test.ts +4 -1
- package/dist/ui/components/dock/dock.ts +529 -51
- package/dist/ui/components/dynamic-color/dynamic-color.test.ts +144 -0
- package/dist/ui/components/dynamic-color/dynamic-color.ts +146 -0
- package/dist/ui/components/dynamic-color/index.ts +5 -0
- package/dist/ui/components/glass/glass.test.ts +39 -1
- package/dist/ui/components/glass/glass.ts +87 -17
- package/dist/ui/components/glass/index.ts +1 -0
- package/dist/ui/components/icon/icon-theme.test.ts +28 -0
- package/dist/ui/components/icon/icon-theme.ts +38 -7
- package/dist/ui/components/input/input.ts +2 -1
- package/dist/ui/components/label/label.ts +8 -2
- package/dist/ui/components/line-chart/line-chart.ts +3 -2
- package/dist/ui/components/list/list.ts +5 -4
- package/dist/ui/components/menu/menu-popup.test.ts +18 -14
- package/dist/ui/components/menu/menu-popup.ts +24 -3
- package/dist/ui/components/menu/menu.test.ts +99 -5
- package/dist/ui/components/menu/menu.ts +74 -34
- package/dist/ui/components/nav-drawer/nav-drawer.ts +3 -2
- package/dist/ui/components/navigation-bar/index.ts +1 -0
- package/dist/ui/components/navigation-bar/navigation-bar.test.ts +425 -0
- package/dist/ui/components/navigation-bar/navigation-bar.ts +411 -0
- package/dist/ui/components/popover/index.ts +9 -0
- package/dist/ui/components/popover/popover-panel.test.ts +735 -0
- package/dist/ui/components/popover/popover-panel.ts +603 -0
- package/dist/ui/components/popover/popover.test.ts +286 -0
- package/dist/ui/components/popover/popover.ts +207 -0
- package/dist/ui/components/progress/progress.ts +2 -1
- package/dist/ui/components/radio-group/radio-group.ts +2 -1
- package/dist/ui/components/scroll-area/scroll-area.ts +4 -3
- package/dist/ui/components/search-field/index.ts +15 -0
- package/dist/ui/components/search-field/keyboard.test.ts +147 -0
- package/dist/ui/components/search-field/keyboard.ts +123 -0
- package/dist/ui/components/search-field/search-field.test.ts +549 -0
- package/dist/ui/components/search-field/search-field.ts +628 -0
- package/dist/ui/components/segmented-control/index.ts +8 -0
- package/dist/ui/components/segmented-control/segmented-control.test.ts +861 -0
- package/dist/ui/components/segmented-control/segmented-control.ts +884 -0
- package/dist/ui/components/select/select.ts +2 -1
- package/dist/ui/components/sheet/sheet.test.ts +90 -3
- package/dist/ui/components/sheet/sheet.ts +59 -4
- package/dist/ui/components/slider/index.ts +1 -0
- package/dist/ui/components/slider/slider.test.ts +695 -0
- package/dist/ui/components/slider/slider.ts +901 -0
- package/dist/ui/components/switch/index.ts +1 -1
- package/dist/ui/components/switch/switch.test.ts +464 -48
- package/dist/ui/components/switch/switch.ts +613 -43
- package/dist/ui/components/tabs/index.ts +7 -1
- package/dist/ui/components/tabs/tabs.test.ts +278 -2
- package/dist/ui/components/tabs/tabs.ts +233 -30
- package/dist/ui/components/textarea/textarea.ts +2 -1
- package/dist/ui/components/toggle/index.ts +1 -0
- package/dist/ui/components/toggle/toggle.test.ts +310 -4
- package/dist/ui/components/toggle/toggle.ts +241 -26
- package/dist/ui/components/toolbar/bar-scroll.test.ts +310 -0
- package/dist/ui/components/toolbar/bar-scroll.ts +372 -0
- package/dist/ui/components/toolbar/index.ts +12 -0
- package/dist/ui/components/toolbar/toolbar.test.ts +438 -0
- package/dist/ui/components/toolbar/toolbar.ts +535 -0
- package/dist/utils/analyze.d.ts +10 -5
- package/dist/utils/analyze.js +3 -2
- package/dist/utils/analyze.js.map +1 -1
- package/dist/utils/installed.d.ts +13 -1
- package/dist/utils/installed.js +17 -0
- package/dist/utils/installed.js.map +1 -1
- package/dist/utils/installed.test.js +39 -1
- package/dist/utils/installed.test.js.map +1 -1
- package/dist/utils/tailwind.d.ts +25 -5
- package/dist/utils/tailwind.js +53 -14
- package/dist/utils/tailwind.js.map +1 -1
- package/dist/utils/tailwind.test.js +85 -22
- package/dist/utils/tailwind.test.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
import { NgTemplateOutlet } from '@angular/common';
|
|
2
|
+
import {
|
|
3
|
+
Component,
|
|
4
|
+
DestroyRef,
|
|
5
|
+
ElementRef,
|
|
6
|
+
Injector,
|
|
7
|
+
ViewEncapsulation,
|
|
8
|
+
booleanAttribute,
|
|
9
|
+
computed,
|
|
10
|
+
effect,
|
|
11
|
+
inject,
|
|
12
|
+
input,
|
|
13
|
+
output,
|
|
14
|
+
signal,
|
|
15
|
+
} from '@angular/core';
|
|
16
|
+
import {
|
|
17
|
+
type BaseLynxElement,
|
|
18
|
+
LYNX_ELEMENTS,
|
|
19
|
+
LynxAccessibilitySettings,
|
|
20
|
+
} from '@blotch/angular-lynx';
|
|
21
|
+
|
|
22
|
+
import { cn } from '../../utils/cn';
|
|
23
|
+
import { UiGlass } from '../glass/glass';
|
|
24
|
+
import {
|
|
25
|
+
type GlassDesign,
|
|
26
|
+
injectGlassDesign,
|
|
27
|
+
injectSystemDrawsGlass,
|
|
28
|
+
} from '../glass/glass-design';
|
|
29
|
+
import { injectIconColor } from '../icon/icon-theme';
|
|
30
|
+
import { type IconName, ICONS } from '../icon/icons';
|
|
31
|
+
import { edgeFadeOverlay, platformOf, UiBarState } from './bar-scroll';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The toolbar's height above the home indicator, in px, in each design:
|
|
35
|
+
* iOS 26's floating 44pt glass with 8pt under it, the 44pt toolbar of iOS 15
|
|
36
|
+
* to 25, and Material 3 Expressive's 64dp docked toolbar.
|
|
37
|
+
*/
|
|
38
|
+
const TOOLBAR_HEIGHT: Readonly<Record<GlassDesign, number>> = {
|
|
39
|
+
glass: 52,
|
|
40
|
+
frosted: 44,
|
|
41
|
+
material: 64,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* An icon's markup, drawn in `color`.
|
|
46
|
+
*/
|
|
47
|
+
const iconSvg = (icon: IconName, color: string): string =>
|
|
48
|
+
// split/join, not replaceAll(), which PrimJS's ES2019 runtime lacks.
|
|
49
|
+
ICONS[icon].split('currentColor').join(color);
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Items that belong together, sharing one background: on iOS 26 and later,
|
|
53
|
+
* one Liquid Glass capsule with the items in it, as iOS groups a toolbar's
|
|
54
|
+
* related items. Put it in a `<ui-navigation-bar>` or a `<ui-toolbar>`:
|
|
55
|
+
*
|
|
56
|
+
* ```html
|
|
57
|
+
* <ui-toolbar-group>
|
|
58
|
+
* <ui-toolbar-item icon="share-2" label="Share" (pressed)="share()" />
|
|
59
|
+
* <ui-toolbar-item icon="heart" label="Favorite" (pressed)="favorite()" />
|
|
60
|
+
* </ui-toolbar-group>
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* The group is one glass shape with plain items in it, rather than a shape
|
|
64
|
+
* per item merged in a glass group: merged circles join with pinched waists,
|
|
65
|
+
* where iOS's grouped items sit on a straight-edged capsule, and glass isn't
|
|
66
|
+
* layered on glass. Before iOS 26 the items are plain bar buttons in the
|
|
67
|
+
* tint color, and on Android Material's icon buttons, so the group only
|
|
68
|
+
* keeps them together.
|
|
69
|
+
*
|
|
70
|
+
* Keep items with a label apart from items with an icon, in groups of their
|
|
71
|
+
* own: side by side, they read as one button.
|
|
72
|
+
*/
|
|
73
|
+
@Component({
|
|
74
|
+
selector: 'ui-toolbar-group',
|
|
75
|
+
imports: [LYNX_ELEMENTS, NgTemplateOutlet, UiGlass],
|
|
76
|
+
encapsulation: ViewEncapsulation.None,
|
|
77
|
+
host: { '[class]': 'hostClass()' },
|
|
78
|
+
template: `
|
|
79
|
+
@if (design() === 'glass') {
|
|
80
|
+
<blur-view
|
|
81
|
+
ui-glass
|
|
82
|
+
class="h-[44px] flex-row items-center flex"
|
|
83
|
+
[interactive]="interactive()"
|
|
84
|
+
>
|
|
85
|
+
<ng-container [ngTemplateOutlet]="items" />
|
|
86
|
+
</blur-view>
|
|
87
|
+
} @else {
|
|
88
|
+
<ng-container [ngTemplateOutlet]="items" />
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
<!-- One copy of the items, for whichever form the group takes: Angular
|
|
92
|
+
projects content once. -->
|
|
93
|
+
<ng-template #items><ng-content /></ng-template>
|
|
94
|
+
`,
|
|
95
|
+
})
|
|
96
|
+
export class UiToolbarGroup {
|
|
97
|
+
readonly userClass = input<string>('', { alias: 'class' });
|
|
98
|
+
|
|
99
|
+
readonly #settings = inject(LynxAccessibilitySettings);
|
|
100
|
+
readonly #systemDrawsGlass = injectSystemDrawsGlass();
|
|
101
|
+
|
|
102
|
+
/** The design on this platform (see GlassDesign), which the items follow. */
|
|
103
|
+
readonly design = injectGlassDesign();
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* The glass swells under a finger, as a group of bar items does on iOS 26.
|
|
107
|
+
* Where the runtime draws it (the web preview), that's motion Reduce Motion
|
|
108
|
+
* asks to leave out; iOS tones its own glass down under the setting.
|
|
109
|
+
*/
|
|
110
|
+
protected readonly interactive = computed(
|
|
111
|
+
() => this.#systemDrawsGlass || !this.#settings.reduceMotion(),
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
protected readonly hostClass = computed(() =>
|
|
115
|
+
// It keeps its items' width: a bar shortens its title, not its controls.
|
|
116
|
+
cn('shrink-0 flex-row items-center flex', this.userClass()),
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* A button in a navigation bar or a toolbar: an icon, or a short label for an
|
|
122
|
+
* action no symbol shows well, such as Edit.
|
|
123
|
+
*
|
|
124
|
+
* ```html
|
|
125
|
+
* <ui-toolbar-item icon="plus" label="New note" (pressed)="add()" />
|
|
126
|
+
* <ui-toolbar-item label="Done" prominent (pressed)="save()" />
|
|
127
|
+
* ```
|
|
128
|
+
*
|
|
129
|
+
* In a `<ui-toolbar-group>` it's a plain item on the group's glass. On its
|
|
130
|
+
* own it draws its own: a glass circle for an icon and a capsule for a label.
|
|
131
|
+
*
|
|
132
|
+
* - `prominent` is the screen's key action, such as Done: tinted glass on iOS
|
|
133
|
+
* 26 and later, a bold label before, and a filled button on Android. Give
|
|
134
|
+
* it its own place, at the trailing end, rather than a group.
|
|
135
|
+
* - `back` draws the platform's Back button: a chevron, and before iOS 26
|
|
136
|
+
* the `label` beside it, the previous screen's title. The navigation bar
|
|
137
|
+
* shows one by itself (see UiNavigationBar); use this for your own.
|
|
138
|
+
*
|
|
139
|
+
* The `label` is also what a screen reader says, so an icon-only item needs
|
|
140
|
+
* one too.
|
|
141
|
+
*/
|
|
142
|
+
@Component({
|
|
143
|
+
selector: 'ui-toolbar-item',
|
|
144
|
+
imports: [LYNX_ELEMENTS, NgTemplateOutlet, UiGlass],
|
|
145
|
+
encapsulation: ViewEncapsulation.None,
|
|
146
|
+
host: {
|
|
147
|
+
class: 'flex-row items-center flex shrink-0',
|
|
148
|
+
// The host is the button: one accessibility element named by its label,
|
|
149
|
+
// and the tap target, whose touches start on the glass inside it. The
|
|
150
|
+
// label is an attribute binding: a property binding on a component's
|
|
151
|
+
// host is checked against the DOM schema, which has no
|
|
152
|
+
// `accessibility-label`.
|
|
153
|
+
'accessibility-element': 'true',
|
|
154
|
+
'accessibility-traits': 'button',
|
|
155
|
+
'[attr.accessibility-label]': 'label()',
|
|
156
|
+
// Press feedback binds touch and mouse events: native fires only touch,
|
|
157
|
+
// and the web preview, in a browser, only mouse. A canceled touch (a
|
|
158
|
+
// scroll taking over) and a cursor leaving release too.
|
|
159
|
+
'(bindtouchstart)': 'press(true)',
|
|
160
|
+
'(bindmousedown)': 'press(true)',
|
|
161
|
+
'(bindtouchend)': 'press(false)',
|
|
162
|
+
'(bindtouchcancel)': 'press(false)',
|
|
163
|
+
'(bindmouseup)': 'press(false)',
|
|
164
|
+
'(bindmouseleave)': 'press(false)',
|
|
165
|
+
'(bindtap)': 'tap()',
|
|
166
|
+
},
|
|
167
|
+
// On its own on glass, the item is a glass shape, with its content inside
|
|
168
|
+
// the glass, where iOS's interactive glass reacts to touches on it. The
|
|
169
|
+
// content is what dims when disabled: glass under a faded parent turns
|
|
170
|
+
// into a washed-out patch on iOS.
|
|
171
|
+
template: `
|
|
172
|
+
@if (ownGlass()) {
|
|
173
|
+
<blur-view
|
|
174
|
+
ui-glass
|
|
175
|
+
[shape]="iconOnly() ? 'circle' : 'capsule'"
|
|
176
|
+
[tint]="prominent() ? 'var(--glass-tint)' : undefined"
|
|
177
|
+
[interactive]="interactive()"
|
|
178
|
+
class="h-[44px] flex-row items-center flex"
|
|
179
|
+
>
|
|
180
|
+
<view [class]="buttonClass()">
|
|
181
|
+
<ng-container [ngTemplateOutlet]="content" />
|
|
182
|
+
</view>
|
|
183
|
+
</blur-view>
|
|
184
|
+
} @else {
|
|
185
|
+
<view [class]="buttonClass()">
|
|
186
|
+
<ng-container [ngTemplateOutlet]="content" />
|
|
187
|
+
</view>
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
<!-- One copy of the content, for whichever form the item takes. -->
|
|
191
|
+
<ng-template #content>
|
|
192
|
+
@if (highlightClass(); as highlight) {
|
|
193
|
+
<view [class]="highlight"></view>
|
|
194
|
+
}
|
|
195
|
+
@if (iconMarkup(); as markup) {
|
|
196
|
+
<svg [attr.content]="markup" [style]="iconStyle()" />
|
|
197
|
+
}
|
|
198
|
+
@if (labelShown()) {
|
|
199
|
+
<text [class]="labelClass()">{{ label() }}</text>
|
|
200
|
+
}
|
|
201
|
+
</ng-template>
|
|
202
|
+
`,
|
|
203
|
+
})
|
|
204
|
+
export class UiToolbarItem {
|
|
205
|
+
/** What the item does, shown without an icon and read by screen readers. */
|
|
206
|
+
readonly label = input.required<string>();
|
|
207
|
+
/** The [Icon](/dolan/icon) the item shows instead of its label. */
|
|
208
|
+
readonly icon = input<IconName | undefined>(undefined);
|
|
209
|
+
/** The screen's key action, such as Done: tinted, on its own. */
|
|
210
|
+
readonly prominent = input(false, { transform: booleanAttribute });
|
|
211
|
+
/** The platform's Back button, with `label` as the previous screen's title. */
|
|
212
|
+
readonly back = input(false, { transform: booleanAttribute });
|
|
213
|
+
readonly disabled = input(false, { transform: booleanAttribute });
|
|
214
|
+
|
|
215
|
+
/** Emits on a tap, unless the item is disabled. */
|
|
216
|
+
readonly pressed = output<void>();
|
|
217
|
+
|
|
218
|
+
readonly #group = inject(UiToolbarGroup, { optional: true });
|
|
219
|
+
readonly #settings = inject(LynxAccessibilitySettings);
|
|
220
|
+
readonly #systemDrawsGlass = injectSystemDrawsGlass();
|
|
221
|
+
readonly #platform = platformOf(inject(Injector));
|
|
222
|
+
readonly #resolveColor = injectIconColor();
|
|
223
|
+
|
|
224
|
+
readonly #host = inject<ElementRef<BaseLynxElement>>(ElementRef);
|
|
225
|
+
|
|
226
|
+
/** The design on this platform (see GlassDesign). */
|
|
227
|
+
readonly design = injectGlassDesign();
|
|
228
|
+
|
|
229
|
+
/** A finger (or the mouse) is down on the item. */
|
|
230
|
+
protected readonly pressing = signal(false);
|
|
231
|
+
|
|
232
|
+
readonly #glass = computed(() => this.design() === 'glass');
|
|
233
|
+
readonly #material = computed(() => this.design() === 'material');
|
|
234
|
+
|
|
235
|
+
/** On glass, an item on its own is a glass shape of its own. */
|
|
236
|
+
protected readonly ownGlass = computed(() => this.#glass() && !this.#group);
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* The icon the item shows: a Back button's chevron (Material's arrow), or
|
|
240
|
+
* the item's own.
|
|
241
|
+
*/
|
|
242
|
+
readonly #icon = computed<IconName | undefined>(() => {
|
|
243
|
+
if (!this.back()) return this.icon();
|
|
244
|
+
return this.#material() ? 'arrow-left' : 'chevron-left';
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* An item shows its icon alone, except a Back button before iOS 26, which
|
|
249
|
+
* shows the previous screen's title beside its chevron.
|
|
250
|
+
*/
|
|
251
|
+
protected readonly iconOnly = computed(
|
|
252
|
+
() => !!this.#icon() && !(this.back() && this.design() === 'frosted'),
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
protected readonly labelShown = computed(
|
|
256
|
+
() => !this.#icon() || !this.iconOnly(),
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
/** A prominent item stands on its own: in a group, it's a plain one. */
|
|
260
|
+
readonly #prominent = computed(() => this.prominent() && !this.#group);
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* As UiToolbarGroup's: the glass reacts to touch, unless Reduce Motion is
|
|
264
|
+
* on where the runtime draws it.
|
|
265
|
+
*/
|
|
266
|
+
protected readonly interactive = computed(
|
|
267
|
+
() =>
|
|
268
|
+
!this.disabled() &&
|
|
269
|
+
(this.#systemDrawsGlass || !this.#settings.reduceMotion()),
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* The color of the item's icon, as a theme token (labelClass colors the
|
|
274
|
+
* label). Glass items take the glass's foreground, and a prominent one its
|
|
275
|
+
* tint's. Before iOS 26 bar buttons were the app's tint color. Material's
|
|
276
|
+
* top app bar draws its navigation icon in the surface's foreground, and
|
|
277
|
+
* its actions in the quieter variant; a prominent action is filled.
|
|
278
|
+
*/
|
|
279
|
+
readonly #iconColor = computed(() => {
|
|
280
|
+
const prominent = this.#prominent();
|
|
281
|
+
switch (this.design()) {
|
|
282
|
+
case 'glass':
|
|
283
|
+
return prominent
|
|
284
|
+
? 'var(--glass-tint-foreground)'
|
|
285
|
+
: 'var(--glass-foreground)';
|
|
286
|
+
case 'material':
|
|
287
|
+
if (prominent) return 'var(--primary-foreground)';
|
|
288
|
+
return this.back() ? 'var(--foreground)' : 'var(--muted-foreground)';
|
|
289
|
+
default:
|
|
290
|
+
return 'var(--primary)';
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// The icon is drawn here, as the dock draws its own, rather than by a
|
|
295
|
+
// nested <ui-icon>: an SVG can't read a text class, so the token is
|
|
296
|
+
// resolved to its value and written into the markup (see icon-theme.ts).
|
|
297
|
+
protected readonly iconMarkup = computed(() => {
|
|
298
|
+
const icon = this.#icon();
|
|
299
|
+
return icon ? iconSvg(icon, this.#resolveColor(this.#iconColor())) : null;
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
protected readonly iconStyle = computed(() => {
|
|
303
|
+
const size = this.design() === 'glass' ? 22 : 24;
|
|
304
|
+
return { width: `${size}px`, height: `${size}px` };
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
protected readonly buttonClass = computed(() => {
|
|
308
|
+
const iconOnly = this.iconOnly();
|
|
309
|
+
const prominent = this.#prominent();
|
|
310
|
+
let size: string;
|
|
311
|
+
switch (this.design()) {
|
|
312
|
+
case 'glass':
|
|
313
|
+
size = cn('h-[44px]', iconOnly ? 'w-[44px]' : 'px-[16px]');
|
|
314
|
+
break;
|
|
315
|
+
case 'material':
|
|
316
|
+
// A 48dp target for an icon, filled in a 40dp circle when it's
|
|
317
|
+
// prominent; a labeled action is a 40dp text or filled button.
|
|
318
|
+
if (iconOnly) {
|
|
319
|
+
size = prominent
|
|
320
|
+
? 'h-[40px] w-[40px] m-[4px] rounded-full bg-primary overflow-hidden'
|
|
321
|
+
: 'h-[48px] w-[48px]';
|
|
322
|
+
} else {
|
|
323
|
+
// 4dp in from its 48dp target, as Material insets its buttons.
|
|
324
|
+
size = cn(
|
|
325
|
+
'mx-[4px] h-[40px] rounded-full px-[12px] overflow-hidden',
|
|
326
|
+
prominent && 'bg-primary px-[16px]',
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
break;
|
|
330
|
+
default:
|
|
331
|
+
// A 44pt target. The Back button's chevron sits close to its title.
|
|
332
|
+
size = cn(
|
|
333
|
+
'h-[44px]',
|
|
334
|
+
iconOnly ? 'min-w-[44px] px-[8px]' : 'gap-[4px] pl-[4px] pr-[8px]',
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
return cn(
|
|
338
|
+
'flex-row items-center flex relative justify-center',
|
|
339
|
+
size,
|
|
340
|
+
// Before iOS 26 a pressed bar button fades, rather than moving.
|
|
341
|
+
this.pressing() && this.design() === 'frosted' && 'opacity-30',
|
|
342
|
+
this.disabled() && 'opacity-50',
|
|
343
|
+
);
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* The press's highlight: Material's state layer, and on glass a shade
|
|
348
|
+
* under the finger where the glass doesn't react by itself (Android's
|
|
349
|
+
* glass, or the web's under Reduce Motion). It comes and goes with the
|
|
350
|
+
* finger, with no motion.
|
|
351
|
+
*/
|
|
352
|
+
protected readonly highlightClass = computed(() => {
|
|
353
|
+
if (!this.pressing() || this.design() === 'frosted') return null;
|
|
354
|
+
if (this.#material()) {
|
|
355
|
+
// An icon button's layer is a 40dp circle in its 48dp target; a
|
|
356
|
+
// filled or text button's fills it.
|
|
357
|
+
const prominent = this.#prominent();
|
|
358
|
+
return cn(
|
|
359
|
+
'opacity-10 absolute',
|
|
360
|
+
this.iconOnly() && !prominent
|
|
361
|
+
? 'h-[40px] w-[40px] rounded-full bg-foreground left-[4px] top-[4px]'
|
|
362
|
+
: 'h-full w-full left-0 top-0',
|
|
363
|
+
prominent ? 'bg-primary-foreground' : !this.iconOnly() && 'bg-primary',
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
// The glass swells under the finger by itself: an item's own, or the
|
|
367
|
+
// group's capsule around a grouped item, as iOS 26's does. Except on
|
|
368
|
+
// Android, whose glass doesn't react, and where Reduce Motion stills it.
|
|
369
|
+
const glassReacts = this.interactive() && this.#platform !== 'Android';
|
|
370
|
+
return glassReacts
|
|
371
|
+
? null
|
|
372
|
+
: 'h-full w-full rounded-full bg-glass-foreground opacity-15 absolute left-0 top-0';
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
protected readonly labelClass = computed(() => {
|
|
376
|
+
const prominent = this.#prominent();
|
|
377
|
+
switch (this.design()) {
|
|
378
|
+
case 'glass':
|
|
379
|
+
return cn(
|
|
380
|
+
'text-[17px] font-medium leading-[22px]',
|
|
381
|
+
prominent ? 'text-glass-tint-foreground' : 'text-glass-foreground',
|
|
382
|
+
);
|
|
383
|
+
case 'material':
|
|
384
|
+
return cn(
|
|
385
|
+
'text-[14px] font-medium leading-[20px]',
|
|
386
|
+
prominent ? 'text-primary-foreground' : 'text-primary',
|
|
387
|
+
);
|
|
388
|
+
default:
|
|
389
|
+
return cn(
|
|
390
|
+
'text-[17px] leading-[22px] text-primary',
|
|
391
|
+
prominent && 'font-semibold',
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
// Screen readers say a disabled item is dimmed. A trait of its own, merged
|
|
397
|
+
// into `button` by the element (see LynxElement.setAccessibilityTrait). Set
|
|
398
|
+
// here rather than with lynxAccessibilityTraits, which the template can't
|
|
399
|
+
// put on the item's own host.
|
|
400
|
+
protected readonly disabledTrait = effect(() => {
|
|
401
|
+
this.#host.nativeElement.setAccessibilityTrait('disabled', this.disabled());
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
protected press(down: boolean): void {
|
|
405
|
+
this.pressing.set(down && !this.disabled());
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
protected tap(): void {
|
|
409
|
+
if (!this.disabled()) this.pressed.emit();
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Flexible space between a toolbar's items, which pushes what follows it to
|
|
415
|
+
* the end, as SwiftUI's `ToolbarSpacer(.flexible)` does:
|
|
416
|
+
*
|
|
417
|
+
* ```html
|
|
418
|
+
* <ui-toolbar>
|
|
419
|
+
* <ui-toolbar-item icon="filter" label="Filter" />
|
|
420
|
+
* <ui-toolbar-spacer />
|
|
421
|
+
* <ui-toolbar-item icon="pencil" label="Compose" />
|
|
422
|
+
* </ui-toolbar>
|
|
423
|
+
* ```
|
|
424
|
+
*/
|
|
425
|
+
@Component({
|
|
426
|
+
selector: 'ui-toolbar-spacer',
|
|
427
|
+
encapsulation: ViewEncapsulation.None,
|
|
428
|
+
// The host is the space: a component's host is a plain view on Lynx.
|
|
429
|
+
host: { class: 'flex-1' },
|
|
430
|
+
template: '',
|
|
431
|
+
})
|
|
432
|
+
export class UiToolbarSpacer {}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* A bar of actions along the bottom of the screen, over the content, which
|
|
436
|
+
* scrolls beneath it: iOS's toolbar, and Material 3 Expressive's docked
|
|
437
|
+
* toolbar. Put items, groups and spacers in it:
|
|
438
|
+
*
|
|
439
|
+
* ```html
|
|
440
|
+
* <ui-toolbar>
|
|
441
|
+
* <ui-toolbar-item icon="list-checks" label="Checklist" />
|
|
442
|
+
* <ui-toolbar-item icon="camera" label="Camera" />
|
|
443
|
+
* <ui-toolbar-item icon="pencil" label="New note" />
|
|
444
|
+
* </ui-toolbar>
|
|
445
|
+
* ```
|
|
446
|
+
*
|
|
447
|
+
* Items spread across it, or sit at either end around a
|
|
448
|
+
* `<ui-toolbar-spacer>`. Put `ui-bar-scroll` on the screen's scroll view and
|
|
449
|
+
* its content ends above the toolbar.
|
|
450
|
+
*
|
|
451
|
+
* Its form follows the platform (see GlassDesign):
|
|
452
|
+
*
|
|
453
|
+
* - `'glass'`: no bar at all. The items float in glass above the home
|
|
454
|
+
* indicator, and content fades out as it passes beneath them.
|
|
455
|
+
* - `'frosted'`: the toolbar of iOS 15 to 25, edge to edge, on the bars'
|
|
456
|
+
* chrome material with a hairline on top while content continues beneath
|
|
457
|
+
* it, and clear once it's scrolled to the end.
|
|
458
|
+
* - `'material'`: a docked toolbar on the surface container color.
|
|
459
|
+
*/
|
|
460
|
+
@Component({
|
|
461
|
+
selector: 'ui-toolbar',
|
|
462
|
+
imports: [LYNX_ELEMENTS, UiGlass],
|
|
463
|
+
encapsulation: ViewEncapsulation.None,
|
|
464
|
+
host: {
|
|
465
|
+
'[class]': 'hostClass()',
|
|
466
|
+
'[style.background-image]': 'edgeFade()',
|
|
467
|
+
},
|
|
468
|
+
template: `
|
|
469
|
+
@if (frostedBackground()) {
|
|
470
|
+
<blur-view
|
|
471
|
+
ui-glass
|
|
472
|
+
shape="rounded"
|
|
473
|
+
[radius]="0"
|
|
474
|
+
material="chrome"
|
|
475
|
+
class="absolute bottom-0 left-0 right-0 top-0"
|
|
476
|
+
></blur-view>
|
|
477
|
+
<view class="h-[0.5px] bg-border absolute left-0 right-0 top-0"></view>
|
|
478
|
+
}
|
|
479
|
+
<view [class]="rowClass()"><ng-content /></view>
|
|
480
|
+
`,
|
|
481
|
+
})
|
|
482
|
+
export class UiToolbar {
|
|
483
|
+
readonly userClass = input<string>('', { alias: 'class' });
|
|
484
|
+
|
|
485
|
+
readonly #state = inject(UiBarState);
|
|
486
|
+
readonly #platform = platformOf(inject(Injector));
|
|
487
|
+
|
|
488
|
+
/** The design on this platform (see GlassDesign), which the items follow. */
|
|
489
|
+
readonly design = injectGlassDesign();
|
|
490
|
+
|
|
491
|
+
/** The toolbar's height above the home indicator, for the scroll view. */
|
|
492
|
+
readonly height = computed(() => TOOLBAR_HEIGHT[this.design()]);
|
|
493
|
+
|
|
494
|
+
protected readonly attachment = this.#state.attach(
|
|
495
|
+
this.#state.bottomBar,
|
|
496
|
+
this,
|
|
497
|
+
inject(DestroyRef),
|
|
498
|
+
);
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* The frosted toolbar's material shows while content continues beneath
|
|
502
|
+
* it, as iOS 15 to 25 draw it, and goes once it's scrolled to the end.
|
|
503
|
+
*/
|
|
504
|
+
protected readonly frostedBackground = computed(
|
|
505
|
+
() => this.design() === 'frosted' && this.#state.scrolledUnderBottom(),
|
|
506
|
+
);
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* On Android, glass bars fade the content beneath them themselves, since
|
|
510
|
+
* its scroll view takes no mask (see edgeFadeOverlay).
|
|
511
|
+
*/
|
|
512
|
+
protected readonly edgeFade = computed(() =>
|
|
513
|
+
this.design() === 'glass' && this.#platform === 'Android'
|
|
514
|
+
? edgeFadeOverlay('top')
|
|
515
|
+
: null,
|
|
516
|
+
);
|
|
517
|
+
|
|
518
|
+
protected readonly hostClass = computed(() => {
|
|
519
|
+
const design = this.design();
|
|
520
|
+
return cn(
|
|
521
|
+
'flex-col flex absolute bottom-0 left-0 right-0',
|
|
522
|
+
design === 'glass' ? 'px-[16px] pb-safe-[8px]' : 'pb-safe',
|
|
523
|
+
design === 'material' && 'bg-secondary',
|
|
524
|
+
this.userClass(),
|
|
525
|
+
);
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
protected readonly rowClass = computed(() =>
|
|
529
|
+
cn(
|
|
530
|
+
'flex-row items-center flex relative justify-between',
|
|
531
|
+
this.design() === 'material' ? 'h-[64px] px-[16px]' : 'h-[44px]',
|
|
532
|
+
this.design() === 'frosted' ? 'px-[8px]' : 'gap-[8px]',
|
|
533
|
+
),
|
|
534
|
+
);
|
|
535
|
+
}
|
package/dist/utils/analyze.d.ts
CHANGED
|
@@ -6,10 +6,14 @@ export type FileAnalysis = {
|
|
|
6
6
|
currentContent: string;
|
|
7
7
|
newContent: string;
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
* it
|
|
9
|
+
* Why upstream's version of the file is no file, when it is: upstream
|
|
10
|
+
* stopped shipping it, or it's a theme file for the Tailwind version the
|
|
11
|
+
* app doesn't use. Taking upstream's version deletes it (see
|
|
12
|
+
* analyzeRemovedFile), and its `newContent` is empty. The summary and the
|
|
13
|
+
* question about it give this reason, since "upstream changed" would hide
|
|
14
|
+
* that the file goes.
|
|
11
15
|
*/
|
|
12
|
-
removed?:
|
|
16
|
+
removed?: string;
|
|
13
17
|
};
|
|
14
18
|
export type ComponentAnalysis = {
|
|
15
19
|
name: string;
|
|
@@ -31,8 +35,9 @@ export declare const STATUS_LABELS: Record<FileStatus, string>;
|
|
|
31
35
|
export declare const analyzeFile: (currentContent: string | null, newContent: string, storedHash: string | null, hashContent: (content: string) => string) => FileStatus;
|
|
32
36
|
/**
|
|
33
37
|
* The status of a file dolan tracks but upstream no longer ships, such as the
|
|
34
|
-
* `*-render.test.ts` files that 22.2.3 stopped copying
|
|
35
|
-
*
|
|
38
|
+
* `*-render.test.ts` files that 22.2.3 stopped copying, or the Tailwind 3
|
|
39
|
+
* plugin of an app that moved to Tailwind 4. Upstream's version of it is "no
|
|
40
|
+
* file", so the same three-way rules apply as for a changed file:
|
|
36
41
|
* - gone from disk too → up-to-date (only its lockfile entry is left)
|
|
37
42
|
* - current == base → auto-update (nobody edited it: delete it)
|
|
38
43
|
* - current != base → conflict (git's modify/delete: ask before deleting)
|
package/dist/utils/analyze.js
CHANGED
|
@@ -48,8 +48,9 @@ export const analyzeFile = (currentContent, newContent, storedHash, hashContent)
|
|
|
48
48
|
};
|
|
49
49
|
/**
|
|
50
50
|
* The status of a file dolan tracks but upstream no longer ships, such as the
|
|
51
|
-
* `*-render.test.ts` files that 22.2.3 stopped copying
|
|
52
|
-
*
|
|
51
|
+
* `*-render.test.ts` files that 22.2.3 stopped copying, or the Tailwind 3
|
|
52
|
+
* plugin of an app that moved to Tailwind 4. Upstream's version of it is "no
|
|
53
|
+
* file", so the same three-way rules apply as for a changed file:
|
|
53
54
|
* - gone from disk too → up-to-date (only its lockfile entry is left)
|
|
54
55
|
* - current == base → auto-update (nobody edited it: delete it)
|
|
55
56
|
* - current != base → conflict (git's modify/delete: ask before deleting)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyze.js","sourceRoot":"","sources":["../../src/utils/analyze.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EACL,SAAS,EACT,eAAe,EACf,UAAU,GAGX,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"analyze.js","sourceRoot":"","sources":["../../src/utils/analyze.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Lockfile } from '../lockfile.js';
|
|
2
2
|
/**
|
|
3
3
|
* The registry components with a folder in the components directory. Other
|
|
4
4
|
* folders (the app's own code next to the components) aren't dolan's.
|
|
@@ -27,3 +27,15 @@ export declare const untrackedComponents: (componentsDir: string, lockfile: Lock
|
|
|
27
27
|
* (`doctor` warns about those instead).
|
|
28
28
|
*/
|
|
29
29
|
export declare const droppedFiles: (name: string, lockfile: Lockfile) => string[];
|
|
30
|
+
/**
|
|
31
|
+
* The theme files dolan tracks (see trackedThemeFiles) that the project no
|
|
32
|
+
* longer takes: the Tailwind 3 plugin and its test once it's on Tailwind 4,
|
|
33
|
+
* or `tailwind.css` once it's back on Tailwind 3. `update` used to look only
|
|
34
|
+
* at the files the project takes, so the plugin stayed, on disk and in the
|
|
35
|
+
* lockfile, as dead code and an uncovered file in the app's coverage report.
|
|
36
|
+
*
|
|
37
|
+
* As for droppedFiles, a test counts as taken whether or not the project has
|
|
38
|
+
* the testing library: the plugin's test goes with the plugin, not with the
|
|
39
|
+
* library, so a Tailwind 3 project that drops the library keeps its copy.
|
|
40
|
+
*/
|
|
41
|
+
export declare const droppedThemeFiles: (cwd: string, themeDir: string, lockfile: Lockfile) => string[];
|
package/dist/utils/installed.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { existsSync, readdirSync } from 'node:fs';
|
|
2
2
|
import { getComponentNames } from '../registry.js';
|
|
3
|
+
import { trackedThemeFiles } from '../lockfile.js';
|
|
3
4
|
import { getComponentFiles } from './resolve-paths.js';
|
|
5
|
+
import { themeFilesFor } from './tailwind.js';
|
|
4
6
|
/**
|
|
5
7
|
* The registry components with a folder in the components directory. Other
|
|
6
8
|
* folders (the app's own code next to the components) aren't dolan's.
|
|
@@ -40,4 +42,19 @@ export const droppedFiles = (name, lockfile) => {
|
|
|
40
42
|
const shipped = new Set(getComponentFiles(name, { tests: true }));
|
|
41
43
|
return Object.keys(lockfile.components[name] ?? {}).filter((file) => !shipped.has(file));
|
|
42
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* The theme files dolan tracks (see trackedThemeFiles) that the project no
|
|
47
|
+
* longer takes: the Tailwind 3 plugin and its test once it's on Tailwind 4,
|
|
48
|
+
* or `tailwind.css` once it's back on Tailwind 3. `update` used to look only
|
|
49
|
+
* at the files the project takes, so the plugin stayed, on disk and in the
|
|
50
|
+
* lockfile, as dead code and an uncovered file in the app's coverage report.
|
|
51
|
+
*
|
|
52
|
+
* As for droppedFiles, a test counts as taken whether or not the project has
|
|
53
|
+
* the testing library: the plugin's test goes with the plugin, not with the
|
|
54
|
+
* library, so a Tailwind 3 project that drops the library keeps its copy.
|
|
55
|
+
*/
|
|
56
|
+
export const droppedThemeFiles = (cwd, themeDir, lockfile) => {
|
|
57
|
+
const takes = new Set(themeFilesFor(cwd, { tests: true }));
|
|
58
|
+
return trackedThemeFiles(lockfile, themeDir).filter((file) => !takes.has(file));
|
|
59
|
+
};
|
|
43
60
|
//# sourceMappingURL=installed.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installed.js","sourceRoot":"","sources":["../../src/utils/installed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"installed.js","sourceRoot":"","sources":["../../src/utils/installed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAiB,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,aAAqB,EAAY,EAAE;IAClE,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC;QAAE,OAAO,EAAE,CAAC;IAC1C,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,iBAAiB,EAAE,CAAC,CAAC;IAC3C,OAAO,WAAW,CAAC,aAAa,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;SACvD,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;SAC/D,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;SAC1B,IAAI,EAAE,CAAC;AACZ,CAAC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,aAAqB,EACrB,QAAkB,EACR,EAAE,CACZ,gBAAgB,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;AAE9E,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,aAAqB,EACrB,QAAkB,EACR,EAAE,CACZ,gBAAgB,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;AAE/E;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,IAAY,EAAE,QAAkB,EAAY,EAAE;IACzE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,iBAAiB,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAClE,OAAO,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CACxD,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAC7B,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,GAAW,EACX,QAAgB,EAChB,QAAkB,EACR,EAAE;IACZ,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC3D,OAAO,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,MAAM,CACjD,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAC3B,CAAC;AACJ,CAAC,CAAC"}
|