diff --git a/.sync/log/3bf1a92a9d3aa47ee7f02c1b2363363bc906f313.md b/.sync/log/3bf1a92a9d3aa47ee7f02c1b2363363bc906f313.md new file mode 100644 index 000000000..6cbe313b6 --- /dev/null +++ b/.sync/log/3bf1a92a9d3aa47ee7f02c1b2363363bc906f313.md @@ -0,0 +1,29 @@ +# Port: perf(components): drop the redundant inner in component extend (#6647) + +**Upstream:** `3bf1a92a9d3aa47ee7f02c1b2363363bc906f313` (nuxt/ui) +**Decision:** port + +## Upstream change +In every component the theme is composed as +`tv({ extend: tv(theme), … })(…)`. The inner `tv(theme)` wrapper is redundant — +`tv`'s `extend` accepts a raw theme object directly — so it is dropped: +`extend: tv(theme)` → `extend: theme`. The outer `tv({ … })(…)` call is +unchanged, so `tv` stays imported and used. + +## b24ui port — direct 1:1 (mechanical) +b24ui uses the identical pattern (`tv({ extend: tv(theme), ...(appConfig.b24ui?. || {}) })(…)`). +Replaced `extend: tv(theme)` → `extend: theme` across all **162** occurrences +in `src/runtime/{components,components/prose,components/content,vue/overrides/*}` +(one line per file, 162 files, +162/−162). Upstream touched 163 files; the +1-file delta is just the diverging component set (no behavioural meaning). + +No renames (no `ui`→`b24ui`/icon/color tokens in the diff). The only b24ui +surface difference is the `appConfig.b24ui?.` spread (vs upstream +`appConfig.ui?.`), which is pre-existing and untouched. + +## Tests +Behaviour-preserving (same resolved theme) → no runtime/markup change, no +snapshot churn. Full suite unchanged (225 files, 5143 passed / 6 skipped). + +## Verify (CI=true) +`dev:prepare` · `lint` · `typecheck` · `test` · `build` — all green. diff --git a/.sync/nuxt-ui.json b/.sync/nuxt-ui.json index f1f46b8c2..11b9ea2ba 100644 --- a/.sync/nuxt-ui.json +++ b/.sync/nuxt-ui.json @@ -2,7 +2,7 @@ "upstream": "nuxt/ui", "branch": "v4", "sync_enabled": false, - "cursor": "7eab4bcb78f17b9eed365d617aa3ef3874c21bac", + "cursor": "3bf1a92a9d3aa47ee7f02c1b2363363bc906f313", "_cursor_note": "cursor = last upstream commit ported into b24ui (oldest-first, manual cadence). sync_enabled stays false until Phase 2 (porter workflow #67 + CLAUDE_CODE_OAUTH_TOKEN) is wired and trusted. `processed` is maintained per port from now on (backfilled #68-#72 on 2026-06-09).", "stats": { "queue_depth": 0, @@ -779,10 +779,16 @@ "summary": "fix(Separator): forward fall-through attributes to root (#6641) — Separator.vue: add defineOptions({ inheritAttrs: false }) + change root binding v-bind=rootProps -> v-bind={...rootProps, ...$attrs} so fall-through attrs land on the actual root. Direct 1:1 (b24ui matched pre-change; useForwardProps rootProps + reka root unchanged; no ui/icon/color renames). +attr-forwarding spec mirroring #218 (d3b5f1d3) (targets [data-slot=root] since Separator is multi-root). 2 snapshots updated (Separator + -vue): the existing 'with color success' renderEach case passes `color` which is NOT a SeparatorProps member = a fall-through attr; pre-fix multi-root dropped it, post-fix $attrs forwards it to root -> snapshot now carries color=air-primary-success (the fix demonstrating itself)" }, "7eab4bcb78f17b9eed365d617aa3ef3874c21bac": { + "pr": 227, + "b24ui_sha": "575c8dee", + "decision": "port", + "summary": "perf(types): import cross-component types from source, not the barrel (#6646) — replace `import type {...} from '../types'` barrel imports with per-source imports (owning component .vue, e.g. ButtonProps from ./Button.vue; or specific ../types/* module, e.g. Messages from ../types/locale) across 119 consumer files in src/runtime/{components,composables,utils,dictionary,locale,vue/...}; +eslint no-restricted-imports guard on ../types & ../../types for components/**/*.vue + composables/**/*.ts, with useComponentProps.ts exempted (keeps `import type * as ComponentTypes from '../types'`). Adapted, not 1:1: resolved each type->source against b24ui's OWN exports (IconComponent->../types/icons since b24ui has no Icon.vue; own component set; curated 20 locale files vs upstream 62 per §2). 0 unresolved / 0 ambiguous names. Rule appended via existing .append chain (bitrix24-ui/ namespace). Types-only, no snapshot churn. 120 files, +284/-120. Rebased onto main after v2.9.0 release (#226) landed mid-CI" + }, + "3bf1a92a9d3aa47ee7f02c1b2363363bc906f313": { "pr": null, "b24ui_sha": "pending-merge", "decision": "port", - "summary": "perf(types): import cross-component types from source, not the barrel (#6646) — replace `import type {...} from '../types'` barrel imports with per-source imports (owning component .vue, e.g. ButtonProps from ./Button.vue; or specific ../types/* module, e.g. Messages from ../types/locale) across 119 consumer files in src/runtime/{components,composables,utils,dictionary,locale,vue/...}; +eslint no-restricted-imports guard on ../types & ../../types for components/**/*.vue + composables/**/*.ts, with useComponentProps.ts exempted (keeps `import type * as ComponentTypes from '../types'`). Adapted, not 1:1: resolved each type->source against b24ui's OWN exports (IconComponent->../types/icons since b24ui has no Icon.vue; own component set; curated 20 locale files vs upstream 62 per §2). 0 unresolved / 0 ambiguous names. Rule appended via existing .append chain (bitrix24-ui/ namespace). Types-only, no snapshot churn. 120 files, +284/-120" + "summary": "perf(components): drop the redundant inner in component extend (#6647) — replace `extend: tv(theme)` -> `extend: theme` across all 162 occurrences in src/runtime/{components,components/prose,components/content,vue/overrides/*} (tv's extend takes a raw theme object; inner tv() wrapper redundant). Outer tv({...})(...) unchanged so tv stays imported. Direct 1:1 mechanical (1 line/file, 162 files, +162/-162; upstream 163, delta = diverging component set). appConfig.b24ui?. spread pre-existing/untouched. Behaviour-preserving, no snapshot churn" } } } diff --git a/src/runtime/components/Accordion.vue b/src/runtime/components/Accordion.vue index 23998a28d..d29826d71 100644 --- a/src/runtime/components/Accordion.vue +++ b/src/runtime/components/Accordion.vue @@ -104,7 +104,7 @@ const appConfig = useAppConfig() as Accordion['AppConfig'] const rootProps = useForwardProps(reactivePick(props, 'as', 'collapsible', 'defaultValue', 'disabled', 'modelValue', 'unmountOnHide'), emits) // eslint-disable-next-line vue/no-dupe-keys -const b24ui = computed(() => tv({ extend: tv(theme), ...(appConfig.b24ui?.accordion || {}) })({ +const b24ui = computed(() => tv({ extend: theme, ...(appConfig.b24ui?.accordion || {}) })({ disabled: props.disabled })) diff --git a/src/runtime/components/Advice.vue b/src/runtime/components/Advice.vue index 59f411a7f..d8a87c9aa 100644 --- a/src/runtime/components/Advice.vue +++ b/src/runtime/components/Advice.vue @@ -55,7 +55,7 @@ const { isLeading, leadingIconName } = useComponentIcons( ) // eslint-disable-next-line vue/no-dupe-keys -const b24ui = computed(() => tv({ extend: tv(theme), ...(appConfig.b24ui?.advice || {}) })({ +const b24ui = computed(() => tv({ extend: theme, ...(appConfig.b24ui?.advice || {}) })({ angle: props.angle })) diff --git a/src/runtime/components/Alert.vue b/src/runtime/components/Alert.vue index 901714255..1b0676aad 100644 --- a/src/runtime/components/Alert.vue +++ b/src/runtime/components/Alert.vue @@ -104,7 +104,7 @@ const { t } = useLocale() const appConfig = useAppConfig() as Alert['AppConfig'] // eslint-disable-next-line vue/no-dupe-keys -const b24ui = computed(() => tv({ extend: tv(theme), ...(appConfig.b24ui?.alert || {}) })({ +const b24ui = computed(() => tv({ extend: theme, ...(appConfig.b24ui?.alert || {}) })({ color: props.color, inverted: Boolean(props.inverted), size: props.size, diff --git a/src/runtime/components/Avatar.vue b/src/runtime/components/Avatar.vue index 503413e5d..3bf70b80d 100644 --- a/src/runtime/components/Avatar.vue +++ b/src/runtime/components/Avatar.vue @@ -81,7 +81,7 @@ const appConfig = useAppConfig() as Avatar['AppConfig'] const { size, color } = useAvatarGroup(_props) // eslint-disable-next-line vue/no-dupe-keys -const b24ui = computed(() => tv({ extend: tv(theme), ...(appConfig.b24ui?.avatar || {}) })({ +const b24ui = computed(() => tv({ extend: theme, ...(appConfig.b24ui?.avatar || {}) })({ size: size.value ?? props.size, color: color.value ?? props.color })) diff --git a/src/runtime/components/AvatarGroup.vue b/src/runtime/components/AvatarGroup.vue index 7726c4a3f..2510fd25e 100644 --- a/src/runtime/components/AvatarGroup.vue +++ b/src/runtime/components/AvatarGroup.vue @@ -50,7 +50,7 @@ const props = useComponentProps('avatarGroup', _props) const appConfig = useAppConfig() as AvatarGroup['AppConfig'] // eslint-disable-next-line vue/no-dupe-keys -const b24ui = computed(() => tv({ extend: tv(theme), ...(appConfig.b24ui?.avatarGroup || {}) })({ +const b24ui = computed(() => tv({ extend: theme, ...(appConfig.b24ui?.avatarGroup || {}) })({ size: props.size, color: props.color })) diff --git a/src/runtime/components/Badge.vue b/src/runtime/components/Badge.vue index 7f89bdb7c..5092971e8 100644 --- a/src/runtime/components/Badge.vue +++ b/src/runtime/components/Badge.vue @@ -86,7 +86,7 @@ const { orientation, size: fieldGroupSize } = useFieldGroup(_props) const { isLeading, leadingIconName } = useComponentIcons(props) // eslint-disable-next-line vue/no-dupe-keys -const b24ui = computed(() => tv({ extend: tv(theme), ...(appConfig.b24ui?.badge || {}) })({ +const b24ui = computed(() => tv({ extend: theme, ...(appConfig.b24ui?.badge || {}) })({ color: props.color, inverted: Boolean(props.inverted), size: fieldGroupSize.value ?? props.size, diff --git a/src/runtime/components/Banner.vue b/src/runtime/components/Banner.vue index 16beb49f2..38f0855f2 100644 --- a/src/runtime/components/Banner.vue +++ b/src/runtime/components/Banner.vue @@ -92,7 +92,7 @@ const appConfig = useAppConfig() as Banner['AppConfig'] const prefix = usePrefix() // eslint-disable-next-line vue/no-dupe-keys -const b24ui = computed(() => tv({ extend: tv(theme), ...(appConfig.b24ui?.banner || {}) })({ +const b24ui = computed(() => tv({ extend: theme, ...(appConfig.b24ui?.banner || {}) })({ color: props.color, to: !!props.to })) diff --git a/src/runtime/components/Breadcrumb.vue b/src/runtime/components/Breadcrumb.vue index d8d9616df..226c1e1c0 100644 --- a/src/runtime/components/Breadcrumb.vue +++ b/src/runtime/components/Breadcrumb.vue @@ -93,7 +93,7 @@ const appConfig = useAppConfig() as Breadcrumb['AppConfig'] const separatorIcon = computed(() => props.separatorIcon || (dir.value === 'rtl' ? icons.chevronLeft : icons.chevronRight)) // eslint-disable-next-line vue/no-dupe-keys -const b24ui = computed(() => tv({ extend: tv(theme), ...(appConfig.b24ui?.breadcrumb || {}) })({ +const b24ui = computed(() => tv({ extend: theme, ...(appConfig.b24ui?.breadcrumb || {}) })({ color: props.color })) diff --git a/src/runtime/components/Button.vue b/src/runtime/components/Button.vue index f4b1e71ec..f310eb364 100644 --- a/src/runtime/components/Button.vue +++ b/src/runtime/components/Button.vue @@ -155,7 +155,7 @@ const isLabel = computed(() => { // eslint-disable-next-line vue/no-dupe-keys const b24ui = computed(() => tv({ - extend: tv(theme), + extend: theme, ...defu({ variants: { active: { diff --git a/src/runtime/components/Calendar.vue b/src/runtime/components/Calendar.vue index 0dea78639..5b2973a96 100644 --- a/src/runtime/components/Calendar.vue +++ b/src/runtime/components/Calendar.vue @@ -154,7 +154,7 @@ const prevYearIcon = computed(() => props.prevYearIcon || (dir.value === 'rtl' ? const prevMonthIcon = computed(() => props.prevMonthIcon || (dir.value === 'rtl' ? icons.chevronRight : icons.chevronLeft)) // eslint-disable-next-line vue/no-dupe-keys -const b24ui = computed(() => tv({ extend: tv(theme), ...(appConfig.b24ui?.calendar || {}) })({ +const b24ui = computed(() => tv({ extend: theme, ...(appConfig.b24ui?.calendar || {}) })({ color: props.color, size: props.size, weekNumbers: props.weekNumbers diff --git a/src/runtime/components/Card.vue b/src/runtime/components/Card.vue index c6e485357..d78c06771 100644 --- a/src/runtime/components/Card.vue +++ b/src/runtime/components/Card.vue @@ -46,7 +46,7 @@ const props = useComponentProps('card', _props) const appConfig = useAppConfig() as Card['AppConfig'] // eslint-disable-next-line vue/no-dupe-keys -const b24ui = computed(() => tv({ extend: tv(theme), ...(appConfig.b24ui?.card || {}) })({ +const b24ui = computed(() => tv({ extend: theme, ...(appConfig.b24ui?.card || {}) })({ variant: props.variant })) diff --git a/src/runtime/components/ChatMessage.vue b/src/runtime/components/ChatMessage.vue index 772e7eeda..402ecb4c3 100644 --- a/src/runtime/components/ChatMessage.vue +++ b/src/runtime/components/ChatMessage.vue @@ -88,7 +88,7 @@ const textParts = computed(() => props.parts?.filter((part): part is TextUIPart const messageProps = computed(() => omit(props, ['as', 'icon', 'avatar', 'variant', 'color', 'side', 'actions', 'compact', 'class', 'b24ui'])) // eslint-disable-next-line vue/no-dupe-keys -const b24ui = computed(() => tv({ extend: tv(theme), ...(appConfig.b24ui?.chatMessage || {}) })({ +const b24ui = computed(() => tv({ extend: theme, ...(appConfig.b24ui?.chatMessage || {}) })({ variant: props.variant, color: props.color, side: props.side, diff --git a/src/runtime/components/ChatMessages.vue b/src/runtime/components/ChatMessages.vue index f368d23b0..b251870e4 100644 --- a/src/runtime/components/ChatMessages.vue +++ b/src/runtime/components/ChatMessages.vue @@ -123,7 +123,7 @@ const userProps = toRef(() => defu(props.user, { side: 'right' as const, variant const assistantProps = toRef(() => defu(props.assistant, { side: 'left' as const, variant: 'message' as const })) // eslint-disable-next-line vue/no-dupe-keys -const b24ui = computed(() => tv({ extend: tv(theme), ...(appConfig.b24ui?.chatMessages || {}) })({ +const b24ui = computed(() => tv({ extend: theme, ...(appConfig.b24ui?.chatMessages || {}) })({ compact: props.compact })) diff --git a/src/runtime/components/ChatPalette.vue b/src/runtime/components/ChatPalette.vue index e356a3212..070c1c6f1 100644 --- a/src/runtime/components/ChatPalette.vue +++ b/src/runtime/components/ChatPalette.vue @@ -37,7 +37,7 @@ const props = useComponentProps('chatPalette', _props) const appConfig = useAppConfig() as ChatPalette['AppConfig'] // eslint-disable-next-line vue/no-dupe-keys -const b24ui = computed(() => tv({ extend: tv(theme), ...(appConfig.b24ui?.chatPalette || {}) })()) +const b24ui = computed(() => tv({ extend: theme, ...(appConfig.b24ui?.chatPalette || {}) })())