Skip to content

fix(extractor): compile or reject function-local style objects, keep ClassNames cx calls, read style functions of props - #745

Open
owjs3901 wants to merge 59 commits into
mainfrom
fix/local-style-objects
Open

owjs3901 wants to merge 59 commits into
mainfrom
fix/local-style-objects

Conversation

@owjs3901

@owjs3901 owjs3901 commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Refs #688, #690, #695 (함수 안의 스타일 객체, ClassNames cx, Emotion 스타일 함수). #740(fix/emotion-rule-numbers-and-mixins) 위에 쌓인 브랜치이며, #740 및 그 선행 composition 체인이 먼저 병합되어야 합니다.

요약

다른 작업에서 발견된 다섯 가지를 고칩니다.

  1. 조용한 오동작. 함수 안에서 const inner = { color: 'red' }로 선언한 객체를 css(inner)에 넘기면 클래스도 오류도 없이 return inner로 컴파일되었습니다. 인라인되지 않은 식별자가 "알 수 없는 클래스"로 읽혀 그대로 남았기 때문입니다.
  2. 보류: <ClassNames>의 opaque cx 호출 결과 허용은 부모의 정적 조합 계약과 충돌하므로 이번 갱신에서 제외합니다. 나머지 기능은 모두 유지합니다.
  3. css(cond && [{ ... }, { ... }])처럼 조건 아래의 규칙 객체 배열이 Cannot compose 오류였습니다.
  4. styled.div({ ... }).attrs({ ... })가 런타임 호출로 남아 던졌습니다.
  5. styled.div((props) => ({ ... })), styled.div({ ... }, (props) => ({ ... })) 스타일 함수가 Cannot compose 오류였습니다.

동작

1. 함수 안의 스타일 객체. 함수 안 const가 전부 리터럴로 쓰인 객체/배열이면, 최상위 상수와 똑같이 스타일 위치에서 인라인합니다(css(), styled(), globalCss(), keyframes(), 스타일 prop과 펼침, Emotion·styled-components·vanilla-extract 별칭, Global styles, ClassNames의 css). 그 바인딩이 스타일에서만 읽힐 때만 컴파일하고, 다른 곳에서도 읽히면(바뀔 수 있으므로) 바인딩 위치에 오류를 냅니다. 리터럴이 아닌 값을 가진 객체({ color: c })는 호출 위치에 LOCAL_STYLES 오류를 냅니다. 객체를 그대로 돌려주는 경우는 없습니다.

export function f() { const inner = { color: 'red' }; return css(inner) }
export function f() {
	const inner = { color: "red" };
	return "a";
}
.a{color:red}
export function f() { const inner = { color: 'red' }; console.log(inner); return css(inner) }
loc2.tsx:2:29: Cannot read `inner` as styles at build time: it is also read elsewhere, where code may change it, so use it only as styles, or declare it with `const` at the top level of the module

2. ClassNames의 cx. 알 수 없는 함수·멤버 호출은 Devup 스타일 클래스를 반환할 수 있으므로, 배열·조건·숫자 혼합 및 단독 호출 모두 부모의 위치가 표시되는 빌드 오류를 유지합니다. 해당 값이 Devup 스타일 클래스를 포함할 수 없다는 증명이 있을 때까지 opaque 호출 허용은 보류합니다. @emotion/css의 별도 계약은 변경하지 않습니다.

3. 조건 아래의 배열. css()는 known_parts 조합 경로로, styled()는 조건을 각 요소에 분배해(cond && [a, b] → [cond && a, cond && b], 삼항도 같은 방식, 조건이 같은 값을 내야 하므로 순수한 조건만) 조합합니다. 순수하지 않은 조건(get() && [...])은 이전처럼 Cannot compose 오류입니다.

4. .attrs 뒤에 붙이기. 위치가 표시되는 빌드 오류입니다.

at.tsx:2:18: `styled()` cannot use `styled.div({ color: "red" }).attrs({ id: "x" })` at build time: chain `.attrs` before the styles, as `styled.div.attrs({ ... })({ ... })`, which neither Emotion nor styled-components take after them

5. props의 함수. 규칙 객체 하나를 한 번에 돌려주는 callback은 원래 invocation scope와 인수 평가를 유지한 채 한 번 호출하고, authored dynamic value를 순서대로 고정 vector에 캡처합니다. 컴포넌트는 그 값을 CSS 변수로 전달하며, nonzero 숫자는 기존 px 변환을 유지하고 unitless 값은 그대로 둡니다. 리터럴과 증명된 theme 경로는 정적 CSS에 남습니다. ordinary property callback과 상속·ref·attrs·slot 경로는 기존 계약을 유지합니다. 아래의 최신 실제 public WASM 출력과 Callback Scope 보완을 함께 보십시오.

export const A = styled.div((p) => ({ color: p.color, padding: 8, '&:hover': { color: p.hover } }))
export const B = styled.div(({ theme }) => ({ color: theme.colors.primary, margin: theme.space[2] }))

최신 생성 코드와 CSS 원문은 아래 실제 Public WASM 출력에 기록했습니다.

새로 생기는 오류

  • 함수 안에서 선언한 스타일 객체가 스타일 밖에서도 읽힐 때: Cannot read inner as styles at build time: ... (바인딩 위치).
  • 함수 안에서 선언한 객체의 값이 빌드 시점에 알려지지 않을 때: ``css()cannot useinner` at build time: a style object it composes must be written in it, or declared with `const` at the top level of the module, where the build reads it`.
  • styled(...)(...) 뒤에 .attrs(...): 위의 chain .attrs before the styles 오류.
  • 규칙 객체 하나를 한 번에 돌려주지 않는 props 스타일 함수(본문에 문장이 있거나, 펼침·계산된 키·async가 있을 때): ``styled()cannot use<함수>` at build time: a style function of the props must give one rule object at once, with every entry written out, ...`.
  • 기존의 ClassNames cx(getClass()) 및 숫자·배열·조건 내부 unknown-call 오류는 유지합니다. 알려진 규칙 배열의 일부 중복 Cannot compose 오류만 해소합니다.

남는 한계

  • props 스타일 함수가 p.on && { ... }처럼 조건으로 규칙 객체 자체를 고르면 오류입니다(값마다 조건은 지원).
  • 최상위 const 객체와 달리, 함수 안 객체는 값이 전부 리터럴이어야 컴파일됩니다.
  • 순수하지 않은 조건(get() && [...])이 고르는 배열은 오류입니다.
  • {...inner}처럼 런타임 값이 있는 객체를 Box에 펼치는 동작은 이 PR의 범위 밖입니다(이전과 같음).

검증

최신 공유 Rust stable과 새 WASM/root build의 전체 검증 결과는 아래 W62g 실제 재검증에 기록했습니다. 이전 head의 역사적 성공을 이번 변경의 검증으로 계산하지 않습니다.

2026-10-08 main 11790bef를 병합하여 업데이트했습니다. 충돌 없이 갱신된 부모 PR의 변경과 main의 변경을 모두 보존했습니다.

2026-10-18 W62g 갱신: 갱신된 #740 93fe24a4877d11a14f30a7a1fd914eefe6f3b8e4를 실제 두 부모 merge commit으로 반영하고 main a6ecc125 및 정적 fallback 26개 단언·capture/slot/marker·부모 오류 가드를 보존했습니다. ClassNames 호출 허용에 관한 자체 테스트 test_emotion_class_names_keeps_calls_as_classes는 located error를 명시적으로 검증하도록 바꾸고 test_emotion_class_names_reports_what_it_cannot_compile에는 부모의 단독 cx(getClass()) 거절을 복구했습니다. ClassNames cx의 opaque 호출은 Devup 스타일 클래스를 포함할 수 없다는 증명이 있기 전까지 위치가 표시되는 오류로 유지하며, 해당 허용 하나만 보류하고 다른 #745 기능은 유지합니다. test_style_objects_declared_in_functions 스냅샷은 부모 globalCss/keyframes의 인수 위치·오류 순서만 반영하고 test_styled_functions_of_the_props_and_arrays_under_conditions 스냅샷의 규칙 객체가 아닌 반환은 부모의 호출 위치 Cannot compose 거절을 보존합니다. test_styled_props_rules_keep_unmapped_theme_guards와 test_styled_function_leaves_map_literal_theme_paths는 정규화 전 theme 거절·일반 함수 리터럴 경로를 검증하고, conditional_rule_arrays_keep_one_getter_read_and_consistent_branches는 기존 capture lowering으로 getter를 한 번만 읽어 분기가 섞이지 않음을 두 CSS 모드에서 검증합니다. 동일 배열 입력은 정확한 main a6ecc125와 부모 93fe24a4에서는 기존 located Cannot compose 오류였고, 이 PR에서 새로 허용한 경로의 중복 읽기를 수정한 것입니다.

Theme 추가 회귀 test_styled_renamed_theme_roots_keep_located_errors는 별칭 root 우회를 독립 RED로 재현한 뒤 검증합니다. 승인된 기존 theme 경로 정책을 재사용하여 p['theme'] 및 ({theme: themeValue})의 리터럴 경로는 같은 CSS 변수로 매핑하고, 동적 키·호출은 기존 located error를 유지합니다. 정확한 main a6ecc125와 부모 93fe24a4는 해당 whole-rule/value-callback 네 입력을 모두 기존 오류로 거절했으며 런타임 theme 접근을 내보내지 않았습니다.

별도 수정 예정인 기존 오류: 일반 class-name 경로에서 literal-table 키 객체의 Symbol.toPrimitive와 뒤쪽 opaque 클래스 getter의 순서가 뒤바뀔 수 있습니다. 정확한 main a6ecc125와 부모 #740 93fe24a4의 public WASM에서 두 CSS 모드 모두 on,key,after,coerce 및 blue를 재현했습니다(올바른 순서는 on,key,coerce,after 및 red). coordinator 결정 msg_fb5ec7b4a02b에 따라 이 PR은 해당 부모 동작과 동등하게 유지하고 일반 경로 수정은 별도 PR로 분리합니다. 실험 회귀 lookup_key_coercion_precedes_later_opaque_reads의 올바른 red 기대값과 실제 RED 결과·30개 origin probe는 W62g 별도 재현 자료에 변경 없이 보존했으며 기존 부모·원래 #745 테스트는 제거하거나 완화하지 않았습니다.

W62g 실제 재검증

2026-10-18 현재 부모 검증은 로컬 실제 merge ffc8ff6a의 두 부모 d846365d + 93fe24a4 위에서 수행했습니다. 원격 검증은 마지막 CI 기록의 실제 head/run만 근거로 삼으며, 이전 head의 성공 run을 새 수정의 검증으로 사용하지 않습니다.

  • 공유 최신 Rust stable 1.99.1, CARGO_BUILD_JOBS=2, 기본 Rust stack으로 fmt check / workspace·all-target strict Clippy / 전체 Rust 테스트 4,990 pass, 0 fail, 0 ignored. INSTA_FORCE_PASS와 snapshot update를 해제한 최종 재실행도 성공했습니다.
  • snapshot 후보는 하나뿐이며 48개 row 전체를 비교했습니다. 10개 row는 compiler-owned render IIFE 제거와 이미 캡처한 값에 대한 동일 숫자 정규화 이동이고, 나머지 38개 row와 기존 거절은 그대로입니다. 승인 후 .snap.new는 남기지 않았습니다.
  • 새 bun install → bindings WASM build → root build 성공, Bun 5,922 pass, 0 fail, 함수·라인 100%, lint 0 errors / 기존 warnings 9개. 로컬 Bun 1.4.3 / Node 24.19.0 / wasm-pack 0.15.0을 관찰했으며 Rust나 도구 버전을 낮추거나 고정하지 않았습니다.
  • 실제 public WASM에서 원래 callback 회귀 14 GREEN, 추가 계약 22 PASS, 암묵적 theme root 10 GREEN, catch/loop/switch/var 및 named self-reference 추가 84 PASS, member-fold 거절 16 GREEN, 14개 API의 member guard 84 PASS, 기존 SDK 182 PASS. 실제 Chromium의 desktop/mobile 계산 스타일 100 PASS는 기존 88개와 추가 12개를 모두 포함합니다.
  • 파서 stack 오류는 native Boa Script::parse에서 재현했습니다. 실행하지 않은 callback과 catch를 없앤 control도 넘쳤고, 단순 catch는 성공했습니다. 숫자 변환을 캡처한 고정 slot으로 옮기고 중복된 compiler-owned IIFE만 제거한 뒤, 원본 catch/더 깊은 loop 입력을 바꾸지 않고 두 CSS 모드의 기본 stack에서 성공했습니다. stack/profile 조정, authored callback body wrapper 변경, 런타임 helper·프로토콜·규칙 해석기는 추가하지 않았습니다.
  • 생성물을 읽는 최종 public WASM SHA256: 207443f07750a5a6a57267783510177c8508dca6de051bd039f4b8cc597ce148. 빌드·테스트 job은 모두 owned process를 0까지 정리했습니다. Rust LSP daemon은 timeout이므로 LSP clean이라고 주장하지 않으며 실제 컴파일·Clippy·테스트 결과로 검증했습니다.

Local Member 정적 가드 보완

최종 리뷰가 const holder={rules:{color:'red'}}; holder.rules.color='blue'; css(holder.rules)의 stale red 컴파일을 발견했습니다. 기존 가드가 bare identifier만 세고 member/fold/조건 선택으로 지워진 참조를 놓친 원인입니다. 두 모드에서 native 16 FAIL / safe 12 PASS, public 16 RED, 14개 API baseline의 safe 28 PASS / unsafe 56 RED를 먼저 실행한 후 수정했습니다. 원래 RED 기대값과 부모 negative는 그대로입니다.

성공한 AST 치환에서 지워지는 semantic reference ID를 원래 symbol에 한 번만 계산합니다. 조건의 test/버려진 branch는 계산하되, 선택되어 런타임에 남는 subtree와 실패한 fold probe를 미리 계산하지 않습니다. 원래 reference 총수·binding 위치·Cannot read ... as styles ... it is also read elsewhere 오류와 safe style-only member 동작은 유지합니다. 같은 참조를 다시 방문해도 중복 가산하지 않으며 numeric/fallback/ClassNames 경로와 별도 generic key-coercion 정책은 바꾸지 않았습니다. 새 native accounting 90개를 포함한 최종 4,990개와 public 16+84개가 모두 성공했습니다.

Callback Scope 보완

Whole-rule callback은 원래 arrow/일반 함수, 이름, 인수·중첩 destructuring·default·rest, strict directive, this/arguments 소유권을 유지하며 실제 props를 한 번만 받습니다. 모든 authored dynamic leaf는 사라진 선언까지 순서대로 한 번 캡처하고, empty/static-only callback의 인수 평가도 render마다 보존합니다. function-valued leaf는 (0, slot)(rest)로 호출하여 배열을 receiver로 주지 않습니다. 상속·attrs·ref·children·caller style·CSS slot·marker·retargeting 및 부모 static fallback 26개 단언을 유지했습니다.

암묵적인 실제 props theme 접근은 own arguments[0] 또는 첫 번째 rest formal의 [0]를 검사하되, catch/loop/switch lexical binding은 해당 scope 안에만 적용하고 nested block의 var는 함수 scope로 처리합니다. 기존 정적 theme 경로 매핑을 확대하지 않았습니다.

새로 생기는 오류 (이번 보완)

이름 있는 whole-rule callback이 자신의 실제 함수 binding을 참조하면 위치가 표시되는 build error가 나며 callback 이름과 recursion is not compiled 이유를 제공합니다. .name/.length, default와 nested capture도 포함하며, 같은 철자의 다른 lexical binding과 property 이름 또는 일반 property callback은 이 거절에 포함하지 않습니다. 정확한 main 3ca6182f와 부모 #740 93fe24a4도 원래 재귀 입력을 두 모드 모두 located error로 거절했으므로, 이 PR의 새 경로가 만든 runtime ReferenceError/undefined를 방지하는 승인된 수정입니다.

자체 신규 draft normal_callback_identity_and_direct_scope_survive_when_it_is_named의 metadata-positive 기대는 승인된 정책에 맞는 명시적 거절과 normal_callback_direct_scope_survives_when_named_without_self_references로 분리했습니다. strict this / own arguments / 실제 인수 한 개의 positive 검증과 원래 부모 negative는 유지했습니다. 추가 인수나 arguments/rest로 우회한 unprovable theme read도 기존 located error를 유지합니다.

남는 한계는 승인된 기존 범위와 같습니다. ClassNames cx의 opaque 호출 허용 하나만 보류하며, 일반 object key coercion 순서 오류는 기존 main/부모 origin과 현재 head 관찰 30건을 남겨 별도 수정합니다. 재귀적으로 반환한 rule object의 런타임 해석은 지원하지 않으며 스타일링 런타임을 추가하지 않습니다. Linux Rust 100%는 새 head의 실제 CI 결과를 확인한 후 별도로 기록합니다.

실제 Public WASM 출력

입력:

import styled from '@emotion/styled';export const A=styled.div(function(p){return{color:arguments[0].color,margin:arguments[0].m};});

생성 코드:

import "@devup-ui/react/devup-ui.css";
import { jsx as __devupJsx0 } from "react/jsx-runtime";
import { forwardRef as __devupForwardRef } from "react";
export const A = __devupForwardRef((__devupRefProps, __devupRef) => (({ style, className, as: DevupAs = "div", forwardedAs, ...rest }) => {
	const __devupRecord0 = (0, function(p) {
		return [arguments[0].color, arguments[0].m];
	}(rest));
	return __devupJsx0(DevupAs, {
		...(({ "theme": __devupOmit0, ...__devupDom }) => __devupDom)(rest),
		className: ["a c", className].filter(Boolean).join(" "),
		style: {
			...{
				"--b": (__devupValue=>typeof __devupValue===`number`&&__devupValue!==0?__devupValue+`px`:__devupValue)(__devupRecord0[`0`]),
				"--d": (__devupValue=>typeof __devupValue===`number`&&__devupValue!==0?__devupValue+`px`:__devupValue)(__devupRecord0[`1`])
			},
			...style
		},
		as: forwardedAs
	});
})({
	...__devupRefProps,
	ref: __devupRef
}));

생성 CSS (singleCss의 base CSS 원문):

/*! devup-ui v1.0.83, | Apache License 2.0 | https://devup-ui.com */.a{color:var(--b)}.c{margin:var(--d)}

이름 있는 callback의 실제 오류:

/src/props.ts:2:27: `styled()` cannot use `(function rules(p) { return { color: p.done ? "red" : rules({ done: true }).color }; })` at build time: callback `rules` refers to itself; recursion is not compiled

Member mutation의 실제 입력과 오류:

import {css} from '@devup-ui/react';
export function f(){const holder={rules:{color:'red'}};holder.rules.color='blue';return css(holder.rules);}
/src/props.ts:2:27: Cannot read `holder` as styles at build time: it is also read elsewhere, where code may change it, so use it only as styles, or declare it with `const` at the top level of the module

owjs3901 and others added 20 commits October 1, 2026 21:16
…rations

css(a, b) composing classes whose styles the build knows, bound to css() in the file or exported by another module, merges their atoms per property, selector, breakpoint and layer, conditions included, instead of joining classes whose winner the stylesheet order picked. vanilla-extract style([...]) passes each composed style as its own argument, keeping a style composed again later.

Refs #688

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #688

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #688

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #688

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…asurable

Refs #688

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…JSX spreads win

Refs #688

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…led order and JSX element className

Refs #688

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #689

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…uate shouldForwardProp at build time

Refs #689

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…ring

Refs #689

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #691

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
… and conditional styles

Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…h Emotion

Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…ed parameters

Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…ins and inline css() by styles

Refs #688, #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…ClassNames cx calls, read style functions of props

Refs #690, #695

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #690, #695

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@github-actions

github-actions Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Changepacks

@devup-ui/wasm@1.0.83 → 1.0.84 - bindings/devup-ui-wasm/package.json

Patch

  • css(a, b) composing classes whose styles the build knows (a const bound to css() in the file, or a css() result another module exports with a rule object every value of which is known) merges their styles: a later part's declaration replaces an earlier one's for the same property, selector, breakpoint and layer, also under conditions (css(base, cond && danger)) and for ||/?? parts, where the classes used to be joined and the stylesheet order picked the winner. vanilla-extract style([a, b]) passes each composed style as its own argument, so the later one wins, and a style composed again later (style([a, b, a])) is kept. Classes the build does not know (strings, props, CSS Modules) are kept as they are
  • styled(Base) extending a styled component the file binds to a const renders Base's tag directly with Base's styles composed under its own, so the extension's declarations replace Base's for the same property, selector, breakpoint and layer instead of losing to them by stylesheet order; Base's attrs apply before the extension's. attrs merge as styled-components merges them: className is joined with the caller's, style is merged and other props replace earlier ones. On an element, a className or style spread after the explicit prop now wins, as React merges props in the order they are written
  • Emotion's now compiles at build time with no runtime: the element becomes what its child function ({ css, cx, theme }) => ... renders, each css and cx call becomes the classes it composes (later styles replace earlier ones; css reads strings as CSS text and its tagged templates split around mixins; cx keeps unknown classes, reads { name: condition } objects as conditional classes and composes nested css and cx calls), and theme.a.b in their values becomes var(--a-b). Numbers in css rules are px lengths, as Emotion reads them, and top-level constants are inlined. New build errors, each at file:line:col: a without exactly one child function taking { css, cx, theme } that gives what it renders at once (attributes, other children, rest or default parameters, async or generator functions, several statements); reading the theme other than as theme.a.b in a css value; using css or cx other than by calling them; a cx object with a spread, getter or method; parts css and cx cannot compose (runtime calls, spreads, a condition choosing between styles and classes). @devup-ui/react/compat exports ClassNames with the ClassNamesContent and ClassNamesArg types, and the @emotion/react declarations include them
  • Emotion and styled-components component selectors now compile at build time: a styled component the file defines that another style selects (${Child} { ... } in CSS text, [Child] or [&:hover ${Child}] as a rule key) gets a short marker class, which only such components get, and the selector reads it as .marker; a component extending it, or an element inlining it for its css prop, keeps the marker. Selectors written without & now nest under the component as stylis nests them (ul { ... } is & ul, a selector list nests each selector, :hover applies to the component itself) instead of becoming global rules, and rule-object keys starting with ., #, [, *, >, + or ~ are read as nested selectors instead of properties
  • Emotion's css prop compiles at build time while @emotion/react is aliased: on tags and Devup UI components always, and on every element once the file imports @emotion/react or @emotion/styled or names @emotion/react in a @jsxImportSource pragma; jsx, jsxs and jsxDEV from @emotion/react/jsx-runtime (or jsx-dev-runtime) and jsx from @emotion/react compile it too, so libraries built with Emotion's JSX runtime do as well. The prop becomes the element's className, with CSS variables in its style for values only the runtime gives: arrays and conditions compose with a later part replacing what an earlier one sets, css() classes the file knows compose by their styles, strings and templates are CSS text, css`` mixins split the text around them, a function of the theme reads theme.a.b as var(--a-b), unitless numbers are px as in Emotion (constants included), and a className holding known css() classes overrides the prop as Emotion's registered classes do. A styled component the file defines renders its tag in the element's place when the prop overrides its styles and it renders a tag with no attrs or props read and the element has no spread, as or forwardedAs. Emotion's JSX runtime imports become react/jsx-runtime, the pragma names react, and jsx comes from @devup-ui/react/compat (React's createElement); @devup-ui/react/compat/css-prop types the prop on React.Attributes. New build errors, each with file:line:column and the code: a css prop part the build cannot read (a call, an element), a style object declared inside a function or with let, a binding only running the module gives or code changes, a theme function that does more than return rules or reads the theme other than as theme.a.b in a value, an interpolation CSS text cannot place or a mixin inside a nested rule, and a css prop overriding a styled component's styles where its tag cannot be rendered in place. Composing css() with a part that reads a known binding (such as a keyframes name) now reads its value
  • A project whose tsconfig.json (or jsconfig.json) builds JSX with Emotion (compilerOptions.jsxImportSource of @emotion/react, through extends and project references too) now compiles the css prop on every element of every file, and its .tsx and .jsx files are built with React's JSX runtime through a /** @jsxImportSource react */ pragma, which a file's own JSX pragma overrides, so the bundler no longer imports Emotion's runtime. The plugins read the setting into a new @emotion/react/jsx-runtime alias (importAliases can turn it off with false, and it is left out when @emotion/react is not aliased); @devup-ui/react/compat/emotion-jsx-runtime declares Emotion's JSX runtimes as React's so the project type-checks without Emotion installed. plugin-utils exports readJsxImportSource, and mergeImportAliases takes the JSX import source
  • Numbers in rules written against Emotion, styled-components or vanilla-extract now read as px lengths when a constant stands in for the literal, as they do for a literal written in place: css({ padding: gap }) with const gap = 8, styled.div({ margin: SIZE }), <Global styles={{ body: { margin: GAP } }} /> and the same with imported constants and constant style objects give 8px, while unitless properties keep bare numbers, 0 stays 0 and Devup shorthands such as p keep the spacing scale. css${mixin}; color: red; outside the css prop composes the mixin by styles, so a later declaration replaces the mixin's, and a css... template is a known style a later css() composes. Inline css() calls inside css() arguments (css(css({ ... }), { ... }), css(cond && css({ ... }), base)) are composed with the other parts, the later winning, instead of being compiled to a class of their own. No new build errors
  • style-order-range only checks styleOrder where the build reads it: a prop of a Devup UI component and a top-level key of a style object passed to css/globalCss/keyframes. A styleOrder key in nested data, in a prop value such as an element passed as a prop, or in a call of a function sharing a name with an Object.prototype member is no longer reported. The array, typography and media rules already skip non-style data, and css-utils-literal-only accepts a css()/keyframes() result held in a const inside a function. No new build errors.
  • A style object declared with const inside a function and passed to css(), styled(), globalCss(), keyframes(), a style prop or a spread (also through the Emotion, styled-components and vanilla-extract aliases) now compiles when every entry is written out and the object is only read as styles, instead of leaving the object where the class belongs; the build errors, at the binding, when the object is also read elsewhere (where code may change it), and at the call when it holds values only the runtime gives. Emotion's ClassNames cx keeps a function call's result as a class that stays as written, as @emotion/css cx does. css() and styled() compose an array of rule objects under a condition. A style function of the props (styled.div((props) => ({ ... })), and as a later argument) compiles: each value reading the props becomes a CSS variable the component sets (a number is a px length unless the property is unitless), theme reads (including theme.a[2]) become var(--a-2), and a condition chooses per value. New build errors: styled(...).attrs(...) chained after the styles (chain .attrs before them), a style function of the props that does not give one rule object at once with every entry written out, and a style object declared in a function that is also read elsewhere
  • Styled components the build generates are wrapped in React's forwardRef, so a ref passed to them reaches the element they render on React 18 as well as React 19; the file imports forwardRef from react when it defines one
  • Styled components no longer pass every prop to the tag they render: $ props, theme and the props their style functions or attrs read are kept away from a tag unless it takes them as attributes, and shouldForwardProp (Emotion options or styled-components withConfig) is evaluated at build time. A shouldForwardProp the build cannot evaluate is a build error naming the file, line and code, with the forms it accepts. An element using a styled component the file defines drops props the component neither reads nor passes on; spread props are passed as written
  • Styled components render what their as prop names, defaulting to the tag or component they were defined with, and pass forwardedAs on as as, as styled-components and Emotion do; as used to reach the rendered tag as an attribute. Component.withComponent(target) on a styled component the file binds to a const builds a component rendering the same styles and attrs as target (a tag name or a component JSX can name), where it used to call a method the generated function does not have and throw
  • Tailwind classes in a className: each class compiles on its own and every class Tailwind does not define (custom, plugin and analytics classes) stays as written, where any recognized class used to rebuild the whole className without them and could rewrite part of another class; a class touching a template interpolation (icon-${name}) is left as written, and template classNames no longer get spaces inserted around interpolations. A class whose variant is not understood stays as written instead of applying unconditionally; variants follow Tailwind v4: stacked variants compose (hover:focus: matches the element itself), hover is guarded by @media (hover: hover), group-/peer- use the group/peer marker classes (and group-hover/name), and data-, aria-, has-, nth-, , **, supports-[…] and [&…]/[@media …] variants compile. Utilities emit all their declarations (size-, rounded-t-, truncate, sr-only, not-sr-only, text-sm with its line height, text-sm/6, leading-), translate/rotate/scale/skew compose through Tailwind's individual properties and registered variables (@Property) with negative values kept negative, and arbitrary values follow Tailwind's grammar: spaces for underscores except in url() and var() names, _ for an underscore, spaces around math operators, type hints, [property:value] and (--variable) shorthands. Words that only share a utility's prefix (select-wrapper, order-summary, cursor-foo) are no longer compiled

@devup-ui/bun-plugin@1.0.22 → 1.0.23 - packages/bun-plugin/package.json

Patch

  • Bun plugin works with Bun.build: DevupUI() (from @devup-ui/bun-plugin/register) serves the generated stylesheet to the bundler's CSS loader once every other module is loaded, so the build emits a CSS output with the styles of the whole bundle instead of an empty module; class names are short there and readable under the runtime (debug option). Files importing the packages Devup UI takes the place of (@emotion/react, @emotion/styled, styled-components, @vanilla-extract/css), @stylexjs/stylex, or a subpath or re-export of these or @devup-ui/react, are compiled instead of skipped
  • A project whose tsconfig.json (or jsconfig.json) builds JSX with Emotion (compilerOptions.jsxImportSource of @emotion/react, through extends and project references too) now compiles the css prop on every element of every file, and its .tsx and .jsx files are built with React's JSX runtime through a /** @jsxImportSource react */ pragma, which a file's own JSX pragma overrides, so the bundler no longer imports Emotion's runtime. The plugins read the setting into a new @emotion/react/jsx-runtime alias (importAliases can turn it off with false, and it is left out when @emotion/react is not aliased); @devup-ui/react/compat/emotion-jsx-runtime declares Emotion's JSX runtimes as React's so the project type-checks without Emotion installed. plugin-utils exports readJsxImportSource, and mergeImportAliases takes the JSX import source

@devup-ui/components@0.1.60 → 0.1.61 - packages/components/package.json

Patch

  • Auto-update: depends on '@devup-ui/react' via a local workspace dependency

@devup-ui/eslint-plugin@1.0.22 → 1.0.23 - packages/eslint-plugin/package.json

Patch

  • style-order-range only checks styleOrder where the build reads it: a prop of a Devup UI component and a top-level key of a style object passed to css/globalCss/keyframes. A styleOrder key in nested data, in a prop value such as an element passed as a prop, or in a call of a function sharing a name with an Object.prototype member is no longer reported. The array, typography and media rules already skip non-style data, and css-utils-literal-only accepts a css()/keyframes() result held in a const inside a function. No new build errors.

@devup-ui/next-plugin@1.0.90 → 1.0.91 - packages/next-plugin/package.json

Patch

  • A project whose tsconfig.json (or jsconfig.json) builds JSX with Emotion (compilerOptions.jsxImportSource of @emotion/react, through extends and project references too) now compiles the css prop on every element of every file, and its .tsx and .jsx files are built with React's JSX runtime through a /** @jsxImportSource react */ pragma, which a file's own JSX pragma overrides, so the bundler no longer imports Emotion's runtime. The plugins read the setting into a new @emotion/react/jsx-runtime alias (importAliases can turn it off with false, and it is left out when @emotion/react is not aliased); @devup-ui/react/compat/emotion-jsx-runtime declares Emotion's JSX runtimes as React's so the project type-checks without Emotion installed. plugin-utils exports readJsxImportSource, and mergeImportAliases takes the JSX import source

@devup-ui/plugin-utils@1.0.17 → 1.0.18 - packages/plugin-utils/package.json

Patch

  • Webpack and Rsbuild builds hold every style in their stylesheets: a stylesheet module is built on its first import, so styles extracted later (the global styles of files webpack or Rsbuild reached late, often outside src) were missing from the output. The files the entries reach under src are now extracted before the build, the shared base on disk is kept current, and when a stylesheet still changed during the build it is compiled once more, writing only the complete result. Rsbuild serves each stylesheet from the current sheet instead of a module-wide string kept only when it grew, which left edits that shortened the CSS and later builds in the same process stale. plugin-utils adds computeReachableFiles
  • A project whose tsconfig.json (or jsconfig.json) builds JSX with Emotion (compilerOptions.jsxImportSource of @emotion/react, through extends and project references too) now compiles the css prop on every element of every file, and its .tsx and .jsx files are built with React's JSX runtime through a /** @jsxImportSource react */ pragma, which a file's own JSX pragma overrides, so the bundler no longer imports Emotion's runtime. The plugins read the setting into a new @emotion/react/jsx-runtime alias (importAliases can turn it off with false, and it is left out when @emotion/react is not aliased); @devup-ui/react/compat/emotion-jsx-runtime declares Emotion's JSX runtimes as React's so the project type-checks without Emotion installed. plugin-utils exports readJsxImportSource, and mergeImportAliases takes the JSX import source

@devup-ui/react@1.0.44 → 1.0.45 - packages/react/package.json

Patch

  • Emotion's now compiles at build time with no runtime: the element becomes what its child function ({ css, cx, theme }) => ... renders, each css and cx call becomes the classes it composes (later styles replace earlier ones; css reads strings as CSS text and its tagged templates split around mixins; cx keeps unknown classes, reads { name: condition } objects as conditional classes and composes nested css and cx calls), and theme.a.b in their values becomes var(--a-b). Numbers in css rules are px lengths, as Emotion reads them, and top-level constants are inlined. New build errors, each at file:line:col: a without exactly one child function taking { css, cx, theme } that gives what it renders at once (attributes, other children, rest or default parameters, async or generator functions, several statements); reading the theme other than as theme.a.b in a css value; using css or cx other than by calling them; a cx object with a spread, getter or method; parts css and cx cannot compose (runtime calls, spreads, a condition choosing between styles and classes). @devup-ui/react/compat exports ClassNames with the ClassNamesContent and ClassNamesArg types, and the @emotion/react declarations include them
  • Emotion's css prop compiles at build time while @emotion/react is aliased: on tags and Devup UI components always, and on every element once the file imports @emotion/react or @emotion/styled or names @emotion/react in a @jsxImportSource pragma; jsx, jsxs and jsxDEV from @emotion/react/jsx-runtime (or jsx-dev-runtime) and jsx from @emotion/react compile it too, so libraries built with Emotion's JSX runtime do as well. The prop becomes the element's className, with CSS variables in its style for values only the runtime gives: arrays and conditions compose with a later part replacing what an earlier one sets, css() classes the file knows compose by their styles, strings and templates are CSS text, css`` mixins split the text around them, a function of the theme reads theme.a.b as var(--a-b), unitless numbers are px as in Emotion (constants included), and a className holding known css() classes overrides the prop as Emotion's registered classes do. A styled component the file defines renders its tag in the element's place when the prop overrides its styles and it renders a tag with no attrs or props read and the element has no spread, as or forwardedAs. Emotion's JSX runtime imports become react/jsx-runtime, the pragma names react, and jsx comes from @devup-ui/react/compat (React's createElement); @devup-ui/react/compat/css-prop types the prop on React.Attributes. New build errors, each with file:line:column and the code: a css prop part the build cannot read (a call, an element), a style object declared inside a function or with let, a binding only running the module gives or code changes, a theme function that does more than return rules or reads the theme other than as theme.a.b in a value, an interpolation CSS text cannot place or a mixin inside a nested rule, and a css prop overriding a styled component's styles where its tag cannot be rendered in place. Composing css() with a part that reads a known binding (such as a keyframes name) now reads its value
  • A project whose tsconfig.json (or jsconfig.json) builds JSX with Emotion (compilerOptions.jsxImportSource of @emotion/react, through extends and project references too) now compiles the css prop on every element of every file, and its .tsx and .jsx files are built with React's JSX runtime through a /** @jsxImportSource react */ pragma, which a file's own JSX pragma overrides, so the bundler no longer imports Emotion's runtime. The plugins read the setting into a new @emotion/react/jsx-runtime alias (importAliases can turn it off with false, and it is left out when @emotion/react is not aliased); @devup-ui/react/compat/emotion-jsx-runtime declares Emotion's JSX runtimes as React's so the project type-checks without Emotion installed. plugin-utils exports readJsxImportSource, and mergeImportAliases takes the JSX import source

@devup-ui/reset-css@1.0.31 → 1.0.32 - packages/reset-css/package.json

Patch

  • Auto-update: depends on '@devup-ui/react' via a local workspace dependency

@devup-ui/rsbuild-plugin@1.0.67 → 1.0.68 - packages/rsbuild-plugin/package.json

Patch

  • Webpack and Rsbuild builds hold every style in their stylesheets: a stylesheet module is built on its first import, so styles extracted later (the global styles of files webpack or Rsbuild reached late, often outside src) were missing from the output. The files the entries reach under src are now extracted before the build, the shared base on disk is kept current, and when a stylesheet still changed during the build it is compiled once more, writing only the complete result. Rsbuild serves each stylesheet from the current sheet instead of a module-wide string kept only when it grew, which left edits that shortened the CSS and later builds in the same process stale. plugin-utils adds computeReachableFiles
  • A project whose tsconfig.json (or jsconfig.json) builds JSX with Emotion (compilerOptions.jsxImportSource of @emotion/react, through extends and project references too) now compiles the css prop on every element of every file, and its .tsx and .jsx files are built with React's JSX runtime through a /** @jsxImportSource react */ pragma, which a file's own JSX pragma overrides, so the bundler no longer imports Emotion's runtime. The plugins read the setting into a new @emotion/react/jsx-runtime alias (importAliases can turn it off with false, and it is left out when @emotion/react is not aliased); @devup-ui/react/compat/emotion-jsx-runtime declares Emotion's JSX runtimes as React's so the project type-checks without Emotion installed. plugin-utils exports readJsxImportSource, and mergeImportAliases takes the JSX import source

@devup-ui/vite-plugin@1.0.73 → 1.0.74 - packages/vite-plugin/package.json

Patch

  • A project whose tsconfig.json (or jsconfig.json) builds JSX with Emotion (compilerOptions.jsxImportSource of @emotion/react, through extends and project references too) now compiles the css prop on every element of every file, and its .tsx and .jsx files are built with React's JSX runtime through a /** @jsxImportSource react */ pragma, which a file's own JSX pragma overrides, so the bundler no longer imports Emotion's runtime. The plugins read the setting into a new @emotion/react/jsx-runtime alias (importAliases can turn it off with false, and it is left out when @emotion/react is not aliased); @devup-ui/react/compat/emotion-jsx-runtime declares Emotion's JSX runtimes as React's so the project type-checks without Emotion installed. plugin-utils exports readJsxImportSource, and mergeImportAliases takes the JSX import source

@devup-ui/webpack-plugin@1.0.71 → 1.0.72 - packages/webpack-plugin/package.json

Patch

  • Webpack and Rsbuild builds hold every style in their stylesheets: a stylesheet module is built on its first import, so styles extracted later (the global styles of files webpack or Rsbuild reached late, often outside src) were missing from the output. The files the entries reach under src are now extracted before the build, the shared base on disk is kept current, and when a stylesheet still changed during the build it is compiled once more, writing only the complete result. Rsbuild serves each stylesheet from the current sheet instead of a module-wide string kept only when it grew, which left edits that shortened the CSS and later builds in the same process stale. plugin-utils adds computeReachableFiles
  • A project whose tsconfig.json (or jsconfig.json) builds JSX with Emotion (compilerOptions.jsxImportSource of @emotion/react, through extends and project references too) now compiles the css prop on every element of every file, and its .tsx and .jsx files are built with React's JSX runtime through a /** @jsxImportSource react */ pragma, which a file's own JSX pragma overrides, so the bundler no longer imports Emotion's runtime. The plugins read the setting into a new @emotion/react/jsx-runtime alias (importAliases can turn it off with false, and it is left out when @emotion/react is not aliased); @devup-ui/react/compat/emotion-jsx-runtime declares Emotion's JSX runtimes as React's so the project type-checks without Emotion installed. plugin-utils exports readJsxImportSource, and mergeImportAliases takes the JSX import source

@codecov

codecov Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
libs/css/src/lib.rs 100.00% <100.00%> (ø)
libs/extractor/src/build_time_values.rs 100.00% <100.00%> (ø)
libs/extractor/src/composition.rs 100.00% <100.00%> (ø)
libs/extractor/src/css_prop.rs 100.00% <100.00%> (ø)
libs/extractor/src/css_utils.rs 100.00% <100.00%> (ø)
...tor/src/extractor/extract_style_from_expression.rs 100.00% <ø> (ø)
...tractor/src/extractor/extract_style_from_styled.rs 100.00% <100.00%> (ø)
libs/extractor/src/import_alias_visit.rs 100.00% <100.00%> (ø)
libs/extractor/src/imported_constants.rs 100.00% <100.00%> (ø)
libs/extractor/src/lib.rs 100.00% <100.00%> (ø)
... and 15 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

owjs3901 and others added 5 commits October 8, 2026 15:11
No conflicted files; preserve composition changes and main ESLint checks.
No conflicted files; combine updated parent and PR changes.
No conflicted files; combine updated parent and PR changes.
Conflicted file: libs/extractor/src/lib.rs. Retain both composition helper and regression tests from PR 706 and Tailwind per-class regression tests from main 11790be; take rewritten main Tailwind implementation with Rust 1.99 assertions. bun.lock regenerated by bun install; no snapshots were hand-merged.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Merge updated parent PR 706 (30247ba), containing main 11790be. Conflicted file: libs/extractor/src/prop_modify_utils.rs. Preserve PR 707's last-written className/style spread semantics and no spread_props parameter with capacity 2, together with main's per-class Tailwind compiler. No snapshots were hand-merged.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
No conflicted files; retain PR changes and merge updated parent #729, which contains main 11790be.
No conflicted files; retain PR changes and merge updated parent #730, which contains main 11790be.
No conflicted files; retain PR changes and merge updated parent #740, which contains main 11790be.
owjs3901 added a commit that referenced this pull request Oct 8, 2026
No conflicted files; retain PR changes and merge updated parent #745, which contains main 11790be.
owjs3901 and others added 25 commits October 14, 2026 17:45
Refs #688 #707 #776

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #688 #707 #776 #702 #780

Includes exact patches 7960b48 and b71ca4e

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #688 #707 #777 #716 #743

Preserve inherited captures and actual TS targets; cover dynamic bases and assertion guards

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #688 #707

Keep the visitor unchanged at module scope so coverage analysis does not invent an uncovered nested declaration

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #688 #710 #707

Retain inherited captures for withComponent and as in JSX and TypeScript renderers

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #689

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #691

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #691 #695

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…sition

Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #682 #695 #688 #689

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #682 #695 #688 #689

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #682 #695 #688 #689

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #682 #695 #688 #689

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #682 #695 #688 #689

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #682 #689

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #690

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Preserve the updated parent guards and local-style features while capturing
conditional rule controllers once and retaining static theme path checks.

Refs #707

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #688

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant