
A specialized, statically typed markup, styling, and state management language created exclusively for Eclipse DE
## About Glint Unlike traditional configuration files (JSON, TOML, YAML) or heavyweight embedded languages (Lua, JS), Glint is designed as the live source code of the graphical shell itself. It parses in nanoseconds, compiles to compact bytecode on the fly, and allows for rebuilding the DE interface in real time without a system restart. ## Philosophy "Source-as-Configuration" Eclipse doesn't have a concept of "default settings" in the form of hidden binary code. Everything you see on the screen, from the tray corner radius to the desktop layout logic, is written in Glint files. (`.gltm`, `.glts` and bytecode - `.glbc`) - **Hot-Reload**: Any change in the `.gltm`/`.glts` file is instantly picked up, compiled into '.glbc' and rendered at runtime. - **Crash resistance**: If the user makes a syntax or logic error, Glint will automatically roll back to the last working state from the bytecode cache. The system fundamentally cannot crash due to an incorrect configuration. Eclipse itself is a framework for writing Glint code to create a DE, although it has its own base source code, allowing you to enjoy the DE and use your PC like GNOME or KDE Plasma. - **Who is Glint for?**: Knowing Glint is necessary for advanced users who want full control over their DE, however, no one forbids the use of the unmodified Eclipse DE ## Architecture The language is designed with a focus on maximum predictability, determinism, and performance of the native application layer on 'iced' (Rust). ### Sigil system | Symbol | Type | Description | | -------------- | --------- | ---------------------------------------------------------- | | `@` | Directive | Single symbol → instant dispatch | | `$` | Varialbe | Distinguishes from item names and strings | | `!rhei` | call Rhei | '!' does not occur in names; The body is transmitted as-is | | `//` | Comment | Two-character check | | `"…"` | String | An unambiguous beginning and end; escape via '\' | | `#RRGGBB` | Color | Only in the value position; parsed for 7 bytes | | `fs:/…` | FS-path | URI scheme; Recognized in 3 characters (f,s,:) | | Capital letter | Element | Distinguishes from directives and keywords | | `0-9 / -` | Integer | The first character uniquely indicates the numeric type | The parser spends **zero time** determining the type of token. It doesn't need to symbolically compare strings like `if`, `let`, or `function`. ### 2. Separation of scopes '()' and '{}' - Parentheses '(...)' contain **only** the properties of the element in the format 'key=value'. - Curly braces '{...}' contain **only** child elements and directives. ### 3. Native protocols Instead of calling heavy functions like 'readFile("/path")' that require argument parsing in runtime, Glint uses built-in prefixes: ```gltm Image(src=fs:/usr/share/backgrounds/wallpaper.png) ``` ## Bytecode Glint does not interpret pure text while DE is running. It compiles it into a compact binary bytecode. Each UI element is translated into `ELEM_PUSH`/`ELEM_POP` instructions. Each property is converted into a typed opcode of a fixed width (`PROP_INT`, `PROP_STR`, `PROP_COLOR`). This allows the Render Hot-Path to run without memory allocations at all ## Syntax example ```gltm @version 1 @style "desktop.glts" @global $username = !rhei: os.env("USER") @global $hostname = !rhei: os.hostname() @global $locale = "ru_RU" @global $scaleFactor = 1.0 @singleton Config { wallpaper = fs:/home/$username/.config/de/wallpaper.jpg wallpaperFit = "cover" iconTheme = "papirus-dark" fontMain = "Inter" fontMono = "JetBrains Mono" fontSize = 13 workspaces = 4 animEnabled = true accentColor = #cba6f7 taskbarPos = "bottom" } @singleton SystemState { battery = !rhei: sys.battery() volume = !rhei: sys.volume.get() brightness = !rhei: sys.brightness.get() network = !rhei: sys.network.status() timezone = !rhei: os.timezone() } @component DesktopIcon(label: str, icon: fspath, exec: str) { Button(class="desktop-icon") { @on click { !rhei: process.spawn($exec) } @on dblclick { !rhei { process.spawn($exec) wm.raise(process.lastPid()) } } Image(src=$icon, class="icon-img") {} Label(class="icon-label") $label } } @component BatteryWidget { @if $SystemState.battery.present { @let $lvl = $SystemState.battery.level @let $ico = !rhei: icons.battery($lvl) Panel(class="tray-item") { Icon(src=$ico, class="tray-icon") {} @if $SystemState.battery.charging { Icon(src=fs:/usr/share/eclipse/icons/charging.svg, class="tray-icon tray-icon--accent") {} } } } } @component ClockWidget { @let $time = !rhei: sys.time.format("%H:%M", $SystemState.timezone) @let $date = !rhei: sys.time.format("%d %b", $SystemState.timezone) Panel(class="clock-widget") { @on click { !rhei: ui.toggle("calendar-popup") } Label(class="clock-time") $time Label(class="clock-date") $date } } @component SystemTray { Panel(id="system-tray", class="tray") { BatteryWidget {} @if $SystemState.network.connected { Icon(class="tray-icon", src=!rhei: icons.network($SystemState.network.type)) {} } @else { Icon(class="tray-icon tray-icon--warn", src=fs:/usr/share/eclipse/icons/net-offline.svg) {} } Slider(id="vol-slider", class="tray-slider", value=$SystemState.volume, min=0, max=100) { @on change { !rhei: sys.volume.set($self.value) } } ClockWidget {} } } Screen(id="root", width=1920, height=1080, scale=$scaleFactor) { Wallpaper(id="bg", src=$Config.wallpaper, fallback=fs:/usr/share/eclipse/wallpapers/default.jpg, fit=$Config.wallpaperFit, watch=true) {} Panel(id="desktop", class="desktop") { @let $desktopFiles = !rhei: fs.glob("/home/$username/Desktop/*.desktop") @each $f in $desktopFiles { @let $entry = !rhei: xdg.parseDesktop($f) DesktopIcon( label=$entry.name, icon=!rhei: icons.resolve($entry.icon, $Config.iconTheme), exec=$entry.exec ) {} } @on contextmenu { !rhei: ui.show("desktop-ctx") } } ContextMenu(id="desktop-ctx", class="ctx-menu", visible=false) { MenuItem(class="ctx-item") { @on click { !rhei: ui.show("settings") } "Settings" } MenuItem(class="ctx-item") { @on click { !rhei: wallpaper.showPicker() } "Change wallpaper" } MenuDivider {} MenuItem(class="ctx-item ctx-item--danger") { @on click { !rhei: session.logout() } "Logout" } } Panel(id="taskbar", class="taskbar") { Button(id="launcher-btn", class="launcher-button") { @on click { !rhei: ui.toggle("app-launcher") } Image(src=fs:/usr/share/eclipse/logo.svg) {} } Panel(id="window-list", class="window-list") { @let $wins = !rhei: wm.getWindows() @each $w in $wins { Button(class="taskbar-win", data-wid=$w.id, active=$w.focused) { @on click { !rhei: wm.focus($w.id) } @on middleclick { !rhei: wm.close($w.id) } Icon(src=$w.icon, class="win-icon") {} Label(class="win-title") $w.title } } } SystemTray {} } Overlay(id="app-launcher", class="launcher", visible=false) { @on keydown(key="Escape") { !rhei: ui.hide("app-launcher") } Input(id="launcher-search", class="launcher-search", placeholder="Search...", autofocus=true) { @on input { !rhei: launcher.filter($self.value) } } Grid(id="app-grid", class="launcher-grid", columns=6) { @let $apps = !rhei: apps.listAll() @each $app in $apps { Button(class="launcher-app") { @on click { !rhei { process.spawn($app.exec) ui.hide("app-launcher") } } Image(src=$app.icon, class="app-icon") {} Label(class="app-name") $app.name } } } } Overlay(id="notif-layer", class="notif-layer") { @let $notifs = !rhei: notifications.active() @each $n in $notifs { Panel(class="notif-card", data-id=$n.id) { Panel(class="notif-header") { Icon(src=$n.appIcon, class="notif-icon") {} Label(class="notif-app") $n.appName Button(class="notif-close") { @on click { !rhei: notifications.dismiss($n.id) } "×" } } Label(class="notif-title") $n.summary @if $n.body { Label(class="notif-body") $n.body } } } } @if !rhei: os.env("GLINT_DEV") { !rhei { debug.overlay.show() debug.log("DE is init. User: " + $username) debug.log("Singletones: Config, SystemState") } } } ``` ```glts $bg = #1e1e2e $bg-s0 = #313244 $bg-s1 = #45475a $text = #cdd6f4 $text-dim = #a6adc8 $accent = #cba6f7 $accent-h = #b4befe $red = #f38ba8 $warn = #fab387 $green = #a6e3a1 $r = 8px $r-sm = 4px $taskbar-h = 36px $t-fast = 120ms $t-mid = 240ms @mixin flex-row { display: flex flex-direction: row align-items: center } @mixin flex-col { display: flex flex-direction: column align-items: flex-start } @mixin glass { background: rgba(30, 30, 46, 0.88) backdrop-filter: blur(14px) border: 1px solid rgba(203, 166, 247, 0.15) } @mixin shadow-sm { box-shadow: 0 2px 8px rgba(0,0,0,0.45) } @mixin shadow-lg { box-shadow: 0 6px 24px rgba(0,0,0,0.65) } @mixin btn-reset { border: none background: transparent cursor: pointer padding: 0 } @anim fade-in { from { opacity: 0; transform: scale(0.95) } to { opacity: 1; transform: scale(1.0) } } @anim slide-up { from { opacity: 0; transform: translateY(10px) } to { opacity: 1; transform: translateY(0) } } @anim slide-down { from { opacity: 0; transform: translateY(-10px) } to { opacity: 1; transform: translateY(0) } } @anim pulse-warn { 0% { opacity: 1.0 } 50% { opacity: 0.4 } 100% { opacity: 1.0 } } Screen { font-family: $Config.fontMain font-size: $Config.fontSizepx color: $text background: transparent } Wallpaper { position: absolute inset: 0 z-index: 0 object-fit: cover } Panel.desktop { position: absolute inset: 0 0 $taskbar-h 0 z-index: 1 padding: 16px display: grid grid-template-columns: repeat(auto-fill, 80px) grid-template-rows: repeat(auto-fill, 88px) align-content: start gap: 8px } Button.desktop-icon { @use flex-col @use btn-reset width: 76px padding: 6px 4px border-radius: $r-sm border: 1px solid transparent align-items: center gap: 5px transition: background $t-fast, border-color $t-fast } Button.desktop-icon:hover { background: rgba(203,166,247,0.12) border-color: rgba(203,166,247,0.30) } Button.desktop-icon:active { background: rgba(203,166,247,0.22) transform: scale(0.96) } Button.desktop-icon > Image.icon-img { width: 48px height: 48px } Button.desktop-icon > Label.icon-label { font-size: 11px color: $text text-align: center text-shadow: 0 1px 4px rgba(0,0,0,0.85) max-width: 72px overflow: ellipsis } Panel.taskbar { @use flex-row @use glass @use shadow-sm position: absolute bottom: 0; left: 0 width: 100% height: $taskbar-h z-index: 100 padding: 0 8px gap: 4px } Button.launcher-button { @use flex-row @use btn-reset width: 30px height: 28px border-radius: $r-sm padding: 5px transition: background $t-fast } Button.launcher-button:hover { background: rgba(203,166,247,0.20) } Button.launcher-button:active { background: rgba(203,166,247,0.36) } Panel.window-list { @use flex-row flex: 1 gap: 2px height: 28px overflow: hidden } Button.taskbar-win { @use flex-row @use btn-reset max-width: 180px height: 28px padding: 0 8px border-radius: $r-sm background: rgba(255,255,255,0.06) color: $text-dim font-size: 12px gap: 6px overflow: hidden transition: background $t-fast, color $t-fast } Button.taskbar-win:hover { background: rgba(255,255,255,0.12) color: $text } Button.taskbar-win[active=true] { background: rgba(203,166,247,0.18) color: $accent border-bottom: 2px solid $accent } Button.taskbar-win > Icon.win-icon { width: 16px height: 16px flex-shrink: 0 } Button.taskbar-win > Label.win-title { white-space: nowrap overflow: ellipsis } Panel.tray { @use flex-row margin-left: auto gap: 4px } Panel.tray-item { @use flex-row; gap: 2px } Icon.tray-icon { width: 16px height: 16px opacity: 0.82 cursor: pointer transition: opacity $t-fast } Icon.tray-icon:hover { opacity: 1.0 } Icon.tray-icon--accent { color: $green } Icon.tray-icon--warn { color: $warn animation: pulse-warn 2s ease infinite } Slider.tray-slider { width: 68px height: 4px accent-color: $accent } Panel.clock-widget { @use flex-col padding: 0 8px cursor: pointer gap: 0 border-radius: $r-sm transition: background $t-fast } Panel.clock-widget:hover { background: rgba(255,255,255,0.07) } Label.clock-time { font-family: $Config.fontMono font-size: 13px font-weight: 500 color: $text letter-spacing: 0.4px } Label.clock-date { font-size: 10px color: $text-dim } ContextMenu.ctx-menu { @use glass @use shadow-lg border-radius: $r padding: 4px min-width: 200px animation: fade-in $t-fast ease } MenuItem.ctx-item { padding: 7px 12px border-radius: $r-sm font-size: 13px color: $text cursor: pointer transition: background $t-fast } MenuItem.ctx-item:hover { background: rgba(255,255,255,0.08) } MenuItem.ctx-item--danger { color: $red } MenuItem.ctx-item--danger:hover { background: rgba(243,139,168,0.12) } MenuDivider { height: 1px background: rgba(255,255,255,0.08) margin: 4px 0 } Overlay.launcher { @use glass @use flex-col @use shadow-lg position: absolute bottom: $taskbar-h left: 50% transform: translateX(-50%) width: 700px max-height: 540px border-radius: $r padding: 16px z-index: 200 gap: 12px animation: slide-up $t-mid ease } Input.launcher-search { width: 100% height: 40px background: rgba(255,255,255,0.06) border: 1px solid rgba(203,166,247,0.25) border-radius: $r-sm padding: 0 14px font-size: 14px color: $text outline: none transition: border-color $t-fast, background $t-fast } Input.launcher-search:focused { border-color: $accent background: rgba(255,255,255,0.09) } Grid.launcher-grid { width: 100% grid-template-columns: repeat(6, 1fr) gap: 8px overflow-y: scroll max-height: 420px padding: 4px } Button.launcher-app { @use flex-col @use btn-reset padding: 10px 6px border-radius: $r-sm border: 1px solid transparent align-items: center gap: 6px transition: background $t-fast, border-color $t-fast animation: fade-in $t-mid ease } Button.launcher-app:hover { background: rgba(203,166,247,0.12) border-color: rgba(203,166,247,0.25) } Button.launcher-app > Image.app-icon { width: 44px; height: 44px } Button.launcher-app > Label.app-name { font-size: 11px color: $text-dim text-align: center max-width: 80px overflow: ellipsis } Overlay.notif-layer { @use flex-col position: absolute top: 12px; right: 12px gap: 8px z-index: 300 max-width: 360px } Panel.notif-card { @use glass @use flex-col @use shadow-sm border-radius: $r padding: 12px gap: 4px animation: slide-down $t-mid ease } Panel.notif-header { @use flex-row gap: 6px margin-bottom: 4px } Icon.notif-icon { width: 16px; height: 16px } Label.notif-app { font-size: 11px; color: $text-dim } Label.notif-title { font-size: 13px; font-weight: 500; color: $text } Label.notif-body { font-size: 12px; color: $text-dim; line-height: 1.5 } Button.notif-close { @use btn-reset margin-left: auto color: $text-dim font-size: 16px line-height: 1 transition: color $t-fast } Button.notif-close:hover { color: $red } @if $Config.accentColor != #cba6f7 { Button.desktop-icon:hover { background: rgba($Config.accentColor, 0.12) border-color: rgba($Config.accentColor, 0.30) } Button.taskbar-win[active=true] { background: rgba($Config.accentColor, 0.18) color: $Config.accentColor border-bottom: 2px solid $Config.accentColor } Input.launcher-search:focused { border-color: $Config.accentColor } } ```