eQuantic.UIeQuantic.UI
Docs
Playground
GitHub
START HERE
Getting Started
WRITE-ONCE
Write-Once Components
Declarative Surface
Photon Engine
Design System
Capabilities
Storage
Forms
Code Editor
Markdown
Mermaid
Email Rendering
ARCHITECTURE
Architecture Overview
Package Architecture
Components
Styling
Localization
Analytics & GTM
COMPILATION
Compiler
Compile-Time Evaluation
Supported C# Features
External Type Resolution
Build Flow
Diagnostics
RUNTIME
Runtime (TypeScript)
Performance
SERVER
Server Integration
Assets
BunPackage
Security
ECOSYSTEM
Image
Charts
Icons
Lottie
DEVELOPMENT
Visual Editor
Debug
Roadmap
PT-BR
Analytics-pt-BR
Architecture-pt-BR
Assets-pt-BR
BuildFlow-pt-BR
BunPackage-pt-BR
Capabilities-pt-BR
Charts-pt-BR
CodeEditor-pt-BR
Compiler-pt-BR
CompileTimeEvaluation-pt-BR
Components-pt-BR
Debug-pt-BR
DeclarativeSurface-pt-BR
DesignSystem-pt-BR
Diagnostics-pt-BR
EmailRealizer-pt-BR
ExternalTypeResolution-pt-BR
Forms-pt-BR
GettingStarted-pt-BR
Home-pt-BR
Icons-pt-BR
Image-pt-BR
Localization-pt-BR
Lottie-pt-BR
Markdown-pt-BR
Mermaid-pt-BR
PackageArchitecture-pt-BR
Performance-pt-BR
Photon-pt-BR
Roadmap-pt-BR
Runtime-pt-BR
Security-pt-BR
ServerIntegration-pt-BR
Storage-pt-BR
Styling-pt-BR
SupportedFeatures-pt-BR
VisualEditor-pt-BR
WriteOnceComponents-pt-BR
ACTIONS & INPUTS
Button
IconButton
TextInput
Select
Checkbox
Switch
RadioGroup
SegmentedControl
Slider
Stepper
SearchField
SURFACES & DISPLAY
Card
Badge
Chip
Avatar
Banner
ProgressBar
EmptyState
Divider
NAVIGATION
Tabs
AppBar
BottomNavigation
Breadcrumb
Pagination
PageIndicator
Menu
Drawer
OVERLAYS
Dialog
BottomSheet
Toast
Popover
Tooltip
LISTS & DATA
List
ListView
Table
Accordion
CodeBlock
TOUCH INTERACTION
PullToRefresh
SwipeableRow
DocsCompilation
eQuantic.UI Build Flow
Edit this page
3 min read
🌐 This page in: English · Português
This document describes the eQuantic.UI build flow, demonstrating how the framework maintains zero external dependencies for the consumer.
Visual Flow
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
┌─────────────────────────────────────────────────────────────────────────────┐
│ DEVELOPMENT (source tree) │
└─────────────────────────────────────────────────────────────────────────────┘
reconciler.ts, component.ts, etc.
┌──────────────────────────────────┐
│ npm run build │ (only during development)
│ (eQuantic.UI.Runtime) │
└──────────────────────────────────┘
dist/index.js (compiled runtime)
boot.ts ──────imports────────┘
┌──────────────────────────────────┐
│ dotnet build │
│ (eQuantic.UI.Server) │
│ │
│ ResolveBunForServer target: │
│ ├─ Looks for Bun in: │
│ │ Runtime.Osx64/tools/bun/ │
│ │ Runtime.Win64/tools/bun/ │
│ │ Runtime.Linux64/tools/bun/ │
│ ├─ Extracts from .zip if needed │
│ └─ chmod +x (Unix) │
│ │
│ BundleRuntime target: │
│ └─ "$(_BunPath)" build boot.ts │
└──────────────────────────────────┘
wwwroot/runtime.js (embedded in Server.dll)
┌──────────────────────────────────┐
│ dotnet pack │
│ (eQuantic.UI.Server) │
└──────────────────────────────────┘
artifacts/packages/eQuantic.UI.Server.0.1.1.nupkg
┌─────────────────────────────────────────────────────────────────────────────┐
│ CONSUMER (client project) │
└─────────────────────────────────────────────────────────────────────────────┘
MyApp.csproj
├─ Sdk="eQuantic.UI.Sdk/0.1.1"
└─ PackageReference: eQuantic.UI.Server, eQuantic.UI.Runtime
┌──────────────────────────────────┐
│ dotnet restore │
│ │
│ NuGet installs packages: │
│ ├─ eQuantic.UI.Sdk │
│ ├─ eQuantic.UI.Server │
│ ├─ eQuantic.UI.Runtime │
│ │ └─ (meta-package) │
│ └─ eQuantic.UI.Runtime.Osx64 │ ◄── Bun embedded here!
│ └─ tools/bun/bun-darwin.zip │
└──────────────────────────────────┘
> **Which Bun?** `src/eQuantic.UI.Runtime/bun-toolchain.json` holds the version and a SHA-256 per
> platform, checked on every test run. The digests prove the committed bytes are the ones the
> release published; the version is verified by extracting the host's binary and running it, because
> a manifest nobody compares against the thing it describes drifts from it.
>
> All six platform packages (`Osx64`, `OsxArm64`, `Win64`, `WinArm64`, `Linux64`, `LinuxArm64`) carry
> the same build; a different Bun per architecture is how you get a bundle that only fails on one
> person's machine.
>
> Updating it: replace the `.zip` files, run the tests once with `EQ_UPDATE_BUN_MANIFEST=1`, and read
> the diff. A version that moved with digests that did not is a mistake, and so is the reverse.
┌──────────────────────────────────┐
│ dotnet build │
│ │
│ SDK.targets executes: │
│ │
│ 1. ResolveBunZipPath │
│ └─ $(PkgeQuantic_UI_Runtime_ │
│ Osx64)/tools/bun/*.zip │
│ │
│ 2. EnsureBunExtracted │
│ ├─ Unzip if needed │
│ └─ chmod +x (Unix) │
│ │
│ 3. ResolveBunPath │
│ └─ Defines $(BunPath) │
│ │
│ 4. InstallBunPackages │
│ ├─ <BunPackage> → bun add │
│ └─ Symlink node_modules │
│ │
│ 5. CompileEQuanticUI │
│ └─ dotnet eqc.dll ... --bun │
│ "$(BunPath)" │
│ │
│ 6. CopyEQuanticRuntime │ ◄── Runtime.js deployment
│ └─ Copy from Runtime package │
│ to wwwroot/_equantic/ │
│ │
│ └─ "$(BunPath)" x │
└──────────────────────────────────┘
wwwroot/_equantic/
├─ runtime.js (from Runtime package)
└─ *.js (compiled components)
┌──────────────────────────────────┐
│ dotnet run │
│ │
│ Server serves: │
│ ├─ runtime.js (from Server.dll) │
│ └─ *.js (from wwwroot/_equantic)│
└──────────────────────────────────┘
Browser loads application
Bun Source by Component
Component
Bun Source
Server (package build)
eQuantic.UI.Runtime.{OS}/tools/bun/ (source tree)
SDK (consumer)
$(PkgeQuantic_UI_Runtime_{OS})/tools/bun/ (NuGet cache)
Consumer Requirements
The consumer only needs:
.NET SDK 10.0
dotnet restore + dotnet build
No Node.js, npm, or global Bun installation required.
Consuming an unreleased SDK from local packages
Since 0.2.0-preview.45
Validate a framework change in a REAL app — packages, global.json, restore, the whole consumer path — without cutting a version or running a pipeline. The first Track W consumer proved its migration this way: byte-identical frames from the package build and from the source build.
In the framework checkout:
1
dotnet pack -c Release -p:PackageVersion=1.0.0-dev -o artifacts/local-feed
1.0.0-dev never collides with a published version, Sdk.Native derives the version of every sibling package from its own, and samples do not pack — the feed is all product. In the app:
global.json: { "msbuild-sdks": { "eQuantic.UI.Sdk.Native": "1.0.0-dev" } } (or eQuantic.UI.Sdk)
nuget.config: <clear />, then the local feed FIRST, nuget.org after
the project file: Sdk="eQuantic.UI.Sdk.Native", no version attribute
After every repack, clear the cached copy and restore again — NuGet never re-downloads a version it already has. ClearEQuanticCache lives in the framework's Directory.Build.targets, so run it from that checkout; it removes every equantic.* package from the NuGet cache reactively (a hand-kept list once missed all the native packages):
1
dotnet msbuild -t:ClearEQuanticCache src/eQuantic.UI.Core/eQuantic.UI.Core.csproj
1
dotnet restore --force
Headless proof for a native app, no window needed: --Photon:ScreenshotPath out.png.
Key Files
File
Responsibility
Sdk/Sdk.targets
Resolves Bun, installs <BunPackage> items, compiles components
Server.csproj
Resolves Bun from source tree, bundles runtime.js
Runtime.{OS}.csproj
Packages Bun executable for each platform
MSBuild Targets (Execution Order)
In SDK (consumer)
1.
ResolveBunZipPath - Finds the Bun .zip in NuGet cache
2.
EnsureBunExtracted - Extracts the executable if needed
3.
ResolveBunPath - Defines $(BunPath) for later use
4.
InstallBunPackages - Installs <BunPackage> items via bun add (see BunPackage)
5.
CompileEQuanticUI - Transpiles C# → TypeScript → JavaScript
6.
CopyEQuanticRuntime - Copies runtime.js from Runtime package to wwwroot/\_equantic/
In Server (development)
1.
ResolveBunForServer - Finds Bun in source tree
2.
BundleRuntime - Compiles boot.ts → runtime.js
Package Architecture & Self-Containment
eQuantic.UI follows a self-contained package architecture where each package manages its own artifacts. The SDK acts as an orchestrator, referencing other packages via NuGet's $(Pkg*) properties.
Architecture Principles
Before (Problematic):
1
2
3
SDK Package ❌
├─ Embedded runtime.js (copied from Runtime)
└─ Embedded *.cs files (copied from Components)
Problems: tight coupling, version conflicts, artifact duplication
After (Correct):
1
2
3
4
5
6
7
8
Runtime Package ✅
└─ tools/runtime/runtime.js (self-contained)
Components Package ✅
└─ tools/source/*.cs (self-contained)
SDK Package ✅
└─ References other packages via $(PkgeQuantic_UI_*)
Benefits: decoupling, correct versioning, no duplication
Runtime.js Deployment
1. Packaging (Development)
During dotnet pack of eQuantic.UI.Runtime:
1
2
3
4
5
<!-- eQuantic.UI.Runtime.csproj -->
<ItemGroup>
<Content Include="dist\index.js" PackagePath="tools\runtime\runtime.js"
Condition="Exists('dist\index.js')" />
</ItemGroup>
The Runtime package embeds its own compiled JavaScript artifact.
2. Deployment (Consumer Build)
During dotnet build, the SDK's CopyEQuanticRuntime target executes:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- Sdk/Sdk.targets -->
<Target Name="CopyEQuanticRuntime" AfterTargets="CompileEQuanticUI"
Condition="'$(EnableEQuanticUICompilation)' == 'true'">
<PropertyGroup>
<!-- Resolve from Runtime package via NuGet property -->
<_RuntimeSourcePath Condition="'$(PkgeQuantic_UI_Runtime)' != ''">
$(PkgeQuantic_UI_Runtime)/tools/runtime/runtime.js
</_RuntimeSourcePath>
<!-- Fallback to source tree (development only) -->
<_RuntimeSourcePath Condition="'$(_RuntimeSourcePath)' == ''">
$(MSBuildThisFileDirectory)../../eQuantic.UI.Runtime/dist/index.js
</_RuntimeSourcePath>
<_RuntimeDestPath>$(MSBuildProjectDirectory)/$(EQuanticOutputPath)runtime.js</_RuntimeDestPath>
</PropertyGroup>
<Error Text="eQuantic.UI: Runtime not found. Ensure eQuantic.UI.Runtime package is installed."
Condition="'$(_RuntimeSourcePath)' == '' Or !Exists('$(_RuntimeSourcePath)')" />
<Copy SourceFiles="$(_RuntimeSourcePath)" DestinationFiles="$(_RuntimeDestPath)" />
</Target>
Components Source Deployment
1. Packaging (Development)
During dotnet pack of eQuantic.UI.Components:
1
2
3
4
<!-- eQuantic.UI.Components.csproj -->
<ItemGroup>
<Content Include="**\*.cs" Exclude="obj\**;bin\**" PackagePath="tools\source\" />
</ItemGroup>
The Components package embeds its own C# source files for compiler type resolution.
2. Compilation (Consumer Build)
During dotnet build, the SDK's CompileEQuanticUI target executes:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- Sdk/Sdk.targets -->
<Target Name="CompileEQuanticUI" BeforeTargets="Build">
<PropertyGroup>
<!-- Resolve from Components package via NuGet property -->
<_StandardComponentsDir Condition="'$(PkgeQuantic_UI_Components)' != ''">
$(PkgeQuantic_UI_Components)/tools/source
</_StandardComponentsDir>
<!-- Fallback to source tree (development only) -->
<_StandardComponentsDir Condition="'$(_StandardComponentsDir)' == ''">
$(MSBuildThisFileDirectory)../../eQuantic.UI.Components
</_StandardComponentsDir>
</PropertyGroup>
<Error Text="eQuantic.UI: Standard components not found. Ensure eQuantic.UI.Components package is installed."
Condition="'$(_StandardComponentsDir)' == '' Or !Exists('$(_StandardComponentsDir)')" />
<Exec Command="dotnet $(EqcCliPath) &quot;$(MSBuildProjectDirectory);$(_StandardComponentsDir)&quot; ..." />
</Target>
Key Architectural Benefits
Decoupling: SDK doesn't embed artifacts from other packages
Correct Versioning: Consumer can use Runtime 0.1.3 + SDK 0.1.2 independently
No Duplication: Each artifact exists only in its source package
Flexibility: Packages evolve independently without tight coupling
Clear Interface: SDK references packages via well-defined NuGet properties ($(Pkg*))
Development Fallback: Source tree paths work for framework development
Runtime Single Bundle Strategy
The Runtime uses Vite's inlineDynamicImports: true configuration to create a single bundle:
1
2
3
4
5
6
7
8
9
10
// eQuantic.UI.Runtime/vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
output: {
inlineDynamicImports: true, // ← Creates single bundle
},
},
},
});
Why Single Bundle?
Simplified Deployment: Only one file to copy (runtime.js)
No Chunk Management: Avoids issues with separate logger-_.js, error-overlay-_.js chunks
Reliable Distribution: Guaranteed that all runtime features (logger, error overlay) are included
Small Size: ~49KB minified with all features included
Without this, Vite would create separate chunks for dynamic imports, and the SDK would need to copy multiple files with hash-based names.