zui 0.0.1
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.
- checksums.yaml +7 -0
- data/Components/Builtins/Accordion.qml +208 -0
- data/Components/Builtins/Action.qml +43 -0
- data/Components/Builtins/ActionButton.qml +19 -0
- data/Components/Builtins/ActionGroup.qml +81 -0
- data/Components/Builtins/AlertDialog.qml +398 -0
- data/Components/Builtins/AnchorAnimation.qml +3 -0
- data/Components/Builtins/AnchorChanges.qml +11 -0
- data/Components/Builtins/AnimatedImage.qml +39 -0
- data/Components/Builtins/Animation.qml +3 -0
- data/Components/Builtins/AnimationController.qml +11 -0
- data/Components/Builtins/ApplicationWindow.qml +103 -0
- data/Components/Builtins/AreaChart.qml +70 -0
- data/Components/Builtins/AspectRatio.qml +30 -0
- data/Components/Builtins/Audio.qml +85 -0
- data/Components/Builtins/AudioInput.qml +10 -0
- data/Components/Builtins/AudioOutput.qml +10 -0
- data/Components/Builtins/Avatar.qml +46 -0
- data/Components/Builtins/Badge.qml +38 -0
- data/Components/Builtins/Banner.qml +133 -0
- data/Components/Builtins/BarChart.qml +67 -0
- data/Components/Builtins/BarIconButton.qml +30 -0
- data/Components/Builtins/BarIndicator.qml +26 -0
- data/Components/Builtins/Behavior.qml +13 -0
- data/Components/Builtins/Blur.qml +14 -0
- data/Components/Builtins/BorderImage.qml +38 -0
- data/Components/Builtins/BorderOverlay.qml +25 -0
- data/Components/Builtins/BottomSheet.qml +211 -0
- data/Components/Builtins/Breadcrumb.qml +169 -0
- data/Components/Builtins/BubbleChart.qml +3 -0
- data/Components/Builtins/BusyIndicator.qml +24 -0
- data/Components/Builtins/Button.qml +55 -0
- data/Components/Builtins/ButtonGroup.qml +18 -0
- data/Components/Builtins/Calendar.qml +55 -0
- data/Components/Builtins/Camera.qml +18 -0
- data/Components/Builtins/CandlestickChart.qml +3 -0
- data/Components/Builtins/Canvas.qml +159 -0
- data/Components/Builtins/CaptureSession.qml +19 -0
- data/Components/Builtins/Card.qml +23 -0
- data/Components/Builtins/Carousel.qml +72 -0
- data/Components/Builtins/Center.qml +20 -0
- data/Components/Builtins/CheckDelegate.qml +132 -0
- data/Components/Builtins/Checkbox.qml +62 -0
- data/Components/Builtins/Chip.qml +65 -0
- data/Components/Builtins/Circle.qml +35 -0
- data/Components/Builtins/Clipboard.qml +29 -0
- data/Components/Builtins/ColorAnimation.qml +3 -0
- data/Components/Builtins/ColorPicker.qml +70 -0
- data/Components/Builtins/Colorize.qml +14 -0
- data/Components/Builtins/Column.qml +17 -0
- data/Components/Builtins/ColumnLayout.qml +13 -0
- data/Components/Builtins/ConfirmDialog.qml +20 -0
- data/Components/Builtins/ConstrainedBox.qml +28 -0
- data/Components/Builtins/Container.qml +30 -0
- data/Components/Builtins/ContextMenu.qml +140 -0
- data/Components/Builtins/CursorSurface.qml +18 -0
- data/Components/Builtins/DataTable.qml +69 -0
- data/Components/Builtins/DatePicker.qml +180 -0
- data/Components/Builtins/DayOfWeekRow.qml +11 -0
- data/Components/Builtins/DelayButton.qml +47 -0
- data/Components/Builtins/DelegateModel.qml +10 -0
- data/Components/Builtins/DelegateModelGroup.qml +10 -0
- data/Components/Builtins/Dial.qml +42 -0
- data/Components/Builtins/Dialog.qml +128 -0
- data/Components/Builtins/DialogButtonBox.qml +137 -0
- data/Components/Builtins/Divider.qml +26 -0
- data/Components/Builtins/DonutChart.qml +3 -0
- data/Components/Builtins/DoubleSpinBox.qml +58 -0
- data/Components/Builtins/DragArea.qml +43 -0
- data/Components/Builtins/Drawer.qml +107 -0
- data/Components/Builtins/DropArea.qml +19 -0
- data/Components/Builtins/DropShadow.qml +16 -0
- data/Components/Builtins/Dropdown.qml +19 -0
- data/Components/Builtins/ExpansionPanel.qml +157 -0
- data/Components/Builtins/FilePicker.qml +140 -0
- data/Components/Builtins/FittedBox.qml +40 -0
- data/Components/Builtins/Flickable.qml +40 -0
- data/Components/Builtins/Flipable.qml +45 -0
- data/Components/Builtins/Flow.qml +16 -0
- data/Components/Builtins/FocusScope.qml +29 -0
- data/Components/Builtins/FolderListModel.qml +9 -0
- data/Components/Builtins/FolderPicker.qml +93 -0
- data/Components/Builtins/FontLoader.qml +21 -0
- data/Components/Builtins/FontPicker.qml +91 -0
- data/Components/Builtins/Frame.qml +71 -0
- data/Components/Builtins/FrameAnimation.qml +13 -0
- data/Components/Builtins/Gauge.qml +3 -0
- data/Components/Builtins/Glow.qml +15 -0
- data/Components/Builtins/Gradient.qml +93 -0
- data/Components/Builtins/Grid.qml +16 -0
- data/Components/Builtins/GridLayout.qml +16 -0
- data/Components/Builtins/GridView.qml +226 -0
- data/Components/Builtins/GroupBox.qml +95 -0
- data/Components/Builtins/Heatmap.qml +3 -0
- data/Components/Builtins/Histogram.qml +3 -0
- data/Components/Builtins/HorizontalHeader.qml +23 -0
- data/Components/Builtins/HorizontalHeaderDelegate.qml +39 -0
- data/Components/Builtins/HoverArea.qml +19 -0
- data/Components/Builtins/Icon.qml +16 -0
- data/Components/Builtins/Image.qml +110 -0
- data/Components/Builtins/ImageCapture.qml +14 -0
- data/Components/Builtins/ItemDelegate.qml +152 -0
- data/Components/Builtins/KeyCatcher.qml +25 -0
- data/Components/Builtins/Label.qml +43 -0
- data/Components/Builtins/LayoutItemProxy.qml +51 -0
- data/Components/Builtins/Legend.qml +86 -0
- data/Components/Builtins/Line.qml +28 -0
- data/Components/Builtins/LineChart.qml +87 -0
- data/Components/Builtins/ListModel.qml +8 -0
- data/Components/Builtins/ListView.qml +65 -0
- data/Components/Builtins/Loader.qml +33 -0
- data/Components/Builtins/Markdown.qml +23 -0
- data/Components/Builtins/MediaDevices.qml +10 -0
- data/Components/Builtins/MediaPlayer.qml +27 -0
- data/Components/Builtins/MediaRecorder.qml +16 -0
- data/Components/Builtins/Menu.qml +119 -0
- data/Components/Builtins/MenuBar.qml +128 -0
- data/Components/Builtins/MenuItem.qml +77 -0
- data/Components/Builtins/MenuSeparator.qml +30 -0
- data/Components/Builtins/MessageDialog.qml +104 -0
- data/Components/Builtins/ModalSheet.qml +222 -0
- data/Components/Builtins/ModelView3d.qml +50 -0
- data/Components/Builtins/MonthGrid.qml +23 -0
- data/Components/Builtins/MultiEffect.qml +48 -0
- data/Components/Builtins/MultiSelect.qml +23 -0
- data/Components/Builtins/NavigationRail.qml +154 -0
- data/Components/Builtins/NumberAnimation.qml +3 -0
- data/Components/Builtins/NumberField.qml +18 -0
- data/Components/Builtins/OpacityAnimator.qml +3 -0
- data/Components/Builtins/OpacityMask.qml +21 -0
- data/Components/Builtins/OpticalGlyph.qml +16 -0
- data/Components/Builtins/Page.qml +111 -0
- data/Components/Builtins/PageIndicator.qml +63 -0
- data/Components/Builtins/Pagination.qml +158 -0
- data/Components/Builtins/Pane.qml +72 -0
- data/Components/Builtins/PanelHero.qml +15 -0
- data/Components/Builtins/ParallelAnimation.qml +3 -0
- data/Components/Builtins/ParentAnimation.qml +3 -0
- data/Components/Builtins/ParentChange.qml +10 -0
- data/Components/Builtins/ParticleSystem.qml +81 -0
- data/Components/Builtins/PasswordField.qml +58 -0
- data/Components/Builtins/Path.qml +35 -0
- data/Components/Builtins/PathAnimation.qml +3 -0
- data/Components/Builtins/PauseAnimation.qml +3 -0
- data/Components/Builtins/PieChart.qml +3 -0
- data/Components/Builtins/PinchArea.qml +23 -0
- data/Components/Builtins/Popup.qml +130 -0
- data/Components/Builtins/Progress.qml +15 -0
- data/Components/Builtins/ProgressRing.qml +120 -0
- data/Components/Builtins/PropertyAction.qml +11 -0
- data/Components/Builtins/PropertyAnimation.qml +3 -0
- data/Components/Builtins/PropertyChanges.qml +10 -0
- data/Components/Builtins/RadarChart.qml +3 -0
- data/Components/Builtins/RadialGauge.qml +3 -0
- data/Components/Builtins/RadioButton.qml +46 -0
- data/Components/Builtins/RadioDelegate.qml +115 -0
- data/Components/Builtins/RadioGroup.qml +58 -0
- data/Components/Builtins/RangeSlider.qml +39 -0
- data/Components/Builtins/Rectangle.qml +23 -0
- data/Components/Builtins/RectangularShadow.qml +23 -0
- data/Components/Builtins/ReorderableList.qml +204 -0
- data/Components/Builtins/RichText.qml +23 -0
- data/Components/Builtins/RotationAnimation.qml +3 -0
- data/Components/Builtins/RotationAnimator.qml +3 -0
- data/Components/Builtins/RoundButton.qml +42 -0
- data/Components/Builtins/Row.qml +17 -0
- data/Components/Builtins/RowLayout.qml +13 -0
- data/Components/Builtins/ScaleAnimator.qml +3 -0
- data/Components/Builtins/ScatterChart.qml +3 -0
- data/Components/Builtins/ScreenCapture.qml +78 -0
- data/Components/Builtins/ScriptAction.qml +15 -0
- data/Components/Builtins/Scroll.qml +18 -0
- data/Components/Builtins/ScrollBar.qml +18 -0
- data/Components/Builtins/ScrollIndicator.qml +15 -0
- data/Components/Builtins/SearchField.qml +178 -0
- data/Components/Builtins/SearchableDropdown.qml +21 -0
- data/Components/Builtins/SectionHeader.qml +14 -0
- data/Components/Builtins/SelectableText.qml +33 -0
- data/Components/Builtins/SelectionRectangle.qml +17 -0
- data/Components/Builtins/Separator.qml +13 -0
- data/Components/Builtins/SequentialAnimation.qml +3 -0
- data/Components/Builtins/Settings.qml +10 -0
- data/Components/Builtins/ShaderEffect.qml +115 -0
- data/Components/Builtins/ShaderEffectSource.qml +61 -0
- data/Components/Builtins/Shaders/grayscale.frag +14 -0
- data/Components/Builtins/Shaders/grayscale.frag.qsb +0 -0
- data/Components/Builtins/Shaders/passthrough.frag +11 -0
- data/Components/Builtins/Shaders/passthrough.frag.qsb +0 -0
- data/Components/Builtins/Shaders/pixelate.frag +16 -0
- data/Components/Builtins/Shaders/pixelate.frag.qsb +0 -0
- data/Components/Builtins/Shaders/vignette.frag +18 -0
- data/Components/Builtins/Shaders/vignette.frag.qsb +0 -0
- data/Components/Builtins/Shaders/wave.frag +18 -0
- data/Components/Builtins/Shaders/wave.frag.qsb +0 -0
- data/Components/Builtins/Shape.qml +53 -0
- data/Components/Builtins/Skeleton.qml +78 -0
- data/Components/Builtins/Slider.qml +21 -0
- data/Components/Builtins/SmoothedAnimation.qml +3 -0
- data/Components/Builtins/Snackbar.qml +181 -0
- data/Components/Builtins/SortFilterProxyModel.qml +16 -0
- data/Components/Builtins/SoundEffect.qml +10 -0
- data/Components/Builtins/Spacer.qml +13 -0
- data/Components/Builtins/Sparkline.qml +3 -0
- data/Components/Builtins/SpinBox.qml +44 -0
- data/Components/Builtins/SplitView.qml +31 -0
- data/Components/Builtins/SpringAnimation.qml +3 -0
- data/Components/Builtins/Stack.qml +14 -0
- data/Components/Builtins/StackLayout.qml +19 -0
- data/Components/Builtins/StackView.qml +122 -0
- data/Components/Builtins/StackedBarChart.qml +3 -0
- data/Components/Builtins/StandardPaths.qml +9 -0
- data/Components/Builtins/State.qml +13 -0
- data/Components/Builtins/StateGroup.qml +11 -0
- data/Components/Builtins/Support/AnimationDriver.qml +79 -0
- data/Components/Builtins/Support/AnimationGroupDriver.qml +32 -0
- data/Components/Builtins/Support/ChartCanvas.qml +629 -0
- data/Components/Builtins/Support/ModelView3dScene.qml +292 -0
- data/Components/Builtins/Support/OptionalModuleState.js +5 -0
- data/Components/Builtins/SwipeDelegate.qml +231 -0
- data/Components/Builtins/SwipeView.qml +69 -0
- data/Components/Builtins/SwitchDelegate.qml +140 -0
- data/Components/Builtins/TabBar.qml +117 -0
- data/Components/Builtins/TabButton.qml +105 -0
- data/Components/Builtins/TableView.qml +388 -0
- data/Components/Builtins/TableViewDelegate.qml +17 -0
- data/Components/Builtins/Tabs.qml +138 -0
- data/Components/Builtins/Text.qml +24 -0
- data/Components/Builtins/TextArea.qml +59 -0
- data/Components/Builtins/TextField.qml +21 -0
- data/Components/Builtins/TextMetrics.qml +39 -0
- data/Components/Builtins/TimePicker.qml +213 -0
- data/Components/Builtins/Timer.qml +13 -0
- data/Components/Builtins/Toast.qml +213 -0
- data/Components/Builtins/Toggle.qml +19 -0
- data/Components/Builtins/ToggleSwitch.qml +22 -0
- data/Components/Builtins/ToolBar.qml +61 -0
- data/Components/Builtins/ToolButton.qml +41 -0
- data/Components/Builtins/ToolSeparator.qml +36 -0
- data/Components/Builtins/Tooltip.qml +20 -0
- data/Components/Builtins/Transition.qml +3 -0
- data/Components/Builtins/TreeView.qml +423 -0
- data/Components/Builtins/TreeViewDelegate.qml +46 -0
- data/Components/Builtins/Tumbler.qml +18 -0
- data/Components/Builtins/UniformAnimator.qml +3 -0
- data/Components/Builtins/VectorAnimation.qml +3 -0
- data/Components/Builtins/VectorImage.qml +62 -0
- data/Components/Builtins/VerticalHeader.qml +21 -0
- data/Components/Builtins/VerticalHeaderDelegate.qml +35 -0
- data/Components/Builtins/Video.qml +54 -0
- data/Components/Builtins/VideoOutput.qml +54 -0
- data/Components/Builtins/WebView.qml +57 -0
- data/Components/Builtins/WeekNumberColumn.qml +13 -0
- data/Components/Builtins/WidgetButton.qml +26 -0
- data/Components/Builtins/Window.qml +90 -0
- data/Components/Builtins/WindowCapture.qml +11 -0
- data/Components/Builtins/Wrap.qml +18 -0
- data/Components/Builtins/XAnimator.qml +3 -0
- data/Components/Builtins/YAnimator.qml +3 -0
- data/Components/README.md +45 -0
- data/ControlNode.qml +1369 -0
- data/Controls/BarIconButton.qml +27 -0
- data/Controls/BarIndicator.qml +22 -0
- data/Controls/BorderOverlay.qml +10 -0
- data/Controls/BorderSurface.qml +10 -0
- data/Controls/Button.qml +78 -0
- data/Controls/ButtonGroup.qml +19 -0
- data/Controls/ConfirmDialog.qml +37 -0
- data/Controls/CursorSurface.qml +29 -0
- data/Controls/Dropdown.qml +59 -0
- data/Controls/MultiSelect.qml +32 -0
- data/Controls/NumberField.qml +24 -0
- data/Controls/OpticalGlyph.qml +14 -0
- data/Controls/PanelActionButton.qml +10 -0
- data/Controls/PanelHero.qml +18 -0
- data/Controls/PanelKeyCatcher.qml +25 -0
- data/Controls/PanelSectionHeader.qml +11 -0
- data/Controls/PanelSeparator.qml +10 -0
- data/Controls/PanelSlider.qml +36 -0
- data/Controls/PanelToolTip.qml +13 -0
- data/Controls/SearchableDropdown.qml +11 -0
- data/Controls/TextField.qml +25 -0
- data/Controls/Toggle.qml +22 -0
- data/Controls/ToggleSwitch.qml +26 -0
- data/Controls/WidgetButton.qml +35 -0
- data/Controls/qmldir +25 -0
- data/Desktop.qml +76 -0
- data/LICENSE +22 -0
- data/README.md +112 -0
- data/Service.qml +430 -0
- data/Theme/Border.qml +22 -0
- data/Theme/Color.qml +22 -0
- data/Theme/Style.qml +47 -0
- data/Theme/Util.qml +6 -0
- data/Theme/qmldir +5 -0
- data/bin/zui +7 -0
- data/lib/zui/animation.rb +24 -0
- data/lib/zui/application.rb +327 -0
- data/lib/zui/builder.rb +725 -0
- data/lib/zui/cli.rb +91 -0
- data/lib/zui/command.rb +101 -0
- data/lib/zui/component_registry.rb +91 -0
- data/lib/zui/components.rb +262 -0
- data/lib/zui/distribution.rb +200 -0
- data/lib/zui/generator.rb +84 -0
- data/lib/zui/host.rb +123 -0
- data/lib/zui/node.rb +30 -0
- data/lib/zui/platform.rb +40 -0
- data/lib/zui/protocol.rb +29 -0
- data/lib/zui/runner.rb +37 -0
- data/lib/zui/runtime.rb +23 -0
- data/lib/zui/scheduler.rb +136 -0
- data/lib/zui/source_bundle.rb +63 -0
- data/lib/zui/state_store.rb +100 -0
- data/lib/zui/value.rb +44 -0
- data/lib/zui.rb +33 -0
- data/native/CMakeLists.txt +45 -0
- data/native/ZuiClipboard.cpp +16 -0
- data/native/ZuiClipboard.h +16 -0
- data/native/ZuiProcess.cpp +84 -0
- data/native/ZuiProcess.h +34 -0
- data/native/main.cpp +64 -0
- data/vendor/host/linux-x86_64/PROVENANCE.md +15 -0
- data/vendor/host/linux-x86_64/zui-host +0 -0
- data/vendor/host/linux-x86_64/zui-host.sha256 +1 -0
- metadata +365 -0
data/Desktop.qml
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import QtQuick
|
|
2
|
+
import QtQuick.Controls as QQC
|
|
3
|
+
import "Theme"
|
|
4
|
+
|
|
5
|
+
QQC.ApplicationWindow {
|
|
6
|
+
id: window
|
|
7
|
+
|
|
8
|
+
readonly property string activeSurface: applicationSurface()
|
|
9
|
+
readonly property var windowOptions: service.optionsFor(activeSurface)
|
|
10
|
+
|
|
11
|
+
function option(name, fallback) {
|
|
12
|
+
var value = windowOptions ? windowOptions[name] : undefined
|
|
13
|
+
return value === undefined || value === null ? fallback : value
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function applicationSurface() {
|
|
17
|
+
if (service.rootId("main") !== "") return "main"
|
|
18
|
+
var names = Object.keys(service.surfaces)
|
|
19
|
+
return names.length > 0 ? names[0] : ""
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
visible: option("visible", true) !== false
|
|
23
|
+
title: String(option("title", zuiApplicationName))
|
|
24
|
+
width: Number(option("width", 760))
|
|
25
|
+
height: Number(option("height", 520))
|
|
26
|
+
minimumWidth: Number(option("min_width", 320))
|
|
27
|
+
minimumHeight: Number(option("min_height", 220))
|
|
28
|
+
maximumWidth: Number(option("max_width", 16777215))
|
|
29
|
+
maximumHeight: Number(option("max_height", 16777215))
|
|
30
|
+
visibility: option("fullscreen", false) === true
|
|
31
|
+
? Window.FullScreen
|
|
32
|
+
: (option("maximized", false) === true ? Window.Maximized : Window.Windowed)
|
|
33
|
+
color: option("color", Color.background)
|
|
34
|
+
|
|
35
|
+
Service {
|
|
36
|
+
id: service
|
|
37
|
+
transport: zuiProcess
|
|
38
|
+
projectDir: zuiProjectDir
|
|
39
|
+
componentDir: zuiComponentDir
|
|
40
|
+
runtimeExecutable: zuiRubyExecutable
|
|
41
|
+
program: zuiRubyProgram
|
|
42
|
+
rubyLoadPath: zuiRubyLoadPath
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
ControlNode {
|
|
46
|
+
id: renderer
|
|
47
|
+
anchors.fill: parent
|
|
48
|
+
anchors.margins: Number(window.option("padding", 20))
|
|
49
|
+
visible: window.activeSurface !== "" && service.rootId(window.activeSurface) !== ""
|
|
50
|
+
bridge: service
|
|
51
|
+
surfaceName: window.activeSurface
|
|
52
|
+
controlId: service.rootId(surfaceName)
|
|
53
|
+
foreground: Color.foreground
|
|
54
|
+
fontFamily: Style.font.family
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
Column {
|
|
58
|
+
anchors.centerIn: parent
|
|
59
|
+
spacing: Style.spacing.sm
|
|
60
|
+
visible: !renderer.visible
|
|
61
|
+
QQC.BusyIndicator { anchors.horizontalCenter: parent.horizontalCenter; running: !service.ready && service.lastError === "" }
|
|
62
|
+
Text {
|
|
63
|
+
anchors.horizontalCenter: parent.horizontalCenter
|
|
64
|
+
text: service.lastError !== "" ? service.lastError : "Starting Zui…"
|
|
65
|
+
color: Color.foreground
|
|
66
|
+
font.family: Style.font.family
|
|
67
|
+
font.pixelSize: Style.font.body
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
onClosing: function(close) {
|
|
72
|
+
service.stopping = true
|
|
73
|
+
if (zuiProcess.running) zuiProcess.stop()
|
|
74
|
+
close.accepted = true
|
|
75
|
+
}
|
|
76
|
+
}
|
data/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Adam Moussa Ali
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
data/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Zui
|
|
2
|
+
|
|
3
|
+
Zui is a platform-neutral Ruby framework for native desktop interfaces powered by Qt and QML.
|
|
4
|
+
Ruby owns the application, component tree, state, events, bindings, animation descriptions, and
|
|
5
|
+
business logic. QML is the rendering backend, not application code.
|
|
6
|
+
|
|
7
|
+
This repository is the reusable core. Distribution integrations live at its edges:
|
|
8
|
+
|
|
9
|
+
- Linux and Windows desktop applications use the standalone Qt host.
|
|
10
|
+
- macOS applications use the same host inside a standard application bundle.
|
|
11
|
+
- Omarchy plugins and applications use the separate
|
|
12
|
+
[`omarchy-ui`](https://github.com/AdamMusa/omarchy-ui) adapter.
|
|
13
|
+
|
|
14
|
+
Zui ships a standard Qt `ApplicationWindow`, a bidirectional `QProcess` bridge, its own neutral
|
|
15
|
+
theme and controls, and the full built-in component catalog. It has no runtime dependency on
|
|
16
|
+
Quickshell or an Omarchy installation.
|
|
17
|
+
|
|
18
|
+
## Core API
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
require "zui"
|
|
22
|
+
|
|
23
|
+
Zui.app do
|
|
24
|
+
state :count, 0
|
|
25
|
+
|
|
26
|
+
app :main, title: "Counter", width: 640, height: 420 do
|
|
27
|
+
column spacing: 16 do
|
|
28
|
+
label { "Count: #{state.count}" }
|
|
29
|
+
button "Increment" do
|
|
30
|
+
state.count += 1
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Developer workflow
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
gem install zui
|
|
41
|
+
zui new telemetry-console
|
|
42
|
+
cd telemetry-console
|
|
43
|
+
zui run main.rb
|
|
44
|
+
zui bundle
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Build and install the gem directly from a checkout:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
gem build zui.gemspec
|
|
51
|
+
gem install ./zui-0.0.1.gem
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`zui run` uses the bundled host for supported release targets. If a matching host is not
|
|
55
|
+
bundled, Zui builds and caches it from the checked-in C++ source with CMake and Qt 6.8 or newer.
|
|
56
|
+
|
|
57
|
+
`zui bundle` produces an application directory on Linux and Windows and a standard `.app` bundle
|
|
58
|
+
on macOS. Each package includes the application, Zui Ruby runtime, QML renderer, component catalog,
|
|
59
|
+
theme, controls, and native host. Ruby and the required Qt runtime libraries must be available on
|
|
60
|
+
the destination machine; native installers can add them with the platform's normal deployment
|
|
61
|
+
tools.
|
|
62
|
+
|
|
63
|
+
See [Platform support](docs/platforms.md) for host requirements and bundle layouts.
|
|
64
|
+
|
|
65
|
+
Zui has no separate validation command. `run` opens the app directly, and
|
|
66
|
+
`bundle` packages the project directly. Ruby, DSL, protocol, resource, and QML
|
|
67
|
+
errors are reported by the operation that actually encounters them.
|
|
68
|
+
|
|
69
|
+
## Reusable application UI
|
|
70
|
+
|
|
71
|
+
Application UI modules are scoped to one builder instead of being mixed into Zui globally:
|
|
72
|
+
|
|
73
|
+
```ruby
|
|
74
|
+
module TelemetryConsole
|
|
75
|
+
module UI
|
|
76
|
+
def dashboard
|
|
77
|
+
card { text "System online" }
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def self.build
|
|
82
|
+
Zui::Application.new(ui: UI) do
|
|
83
|
+
app(:main, title: "Telemetry Console") { dashboard }
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def self.run = build.run
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
TelemetryConsole.run
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
This keeps `main.rb` at one domain-level call and prevents components from one application leaking
|
|
94
|
+
into another application's DSL.
|
|
95
|
+
|
|
96
|
+
## Architecture
|
|
97
|
+
|
|
98
|
+
```text
|
|
99
|
+
Ruby application
|
|
100
|
+
→ Zui DSL/state/events
|
|
101
|
+
→ versioned JSON protocol
|
|
102
|
+
→ native Qt host and process transport
|
|
103
|
+
→ platform-neutral QML renderer
|
|
104
|
+
→ Qt Quick / Controls / Multimedia / optional modules
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Every declared component renders as that component or reports an explicit component error. Zui
|
|
108
|
+
does not silently substitute images, alternate controls, or application-specific fallbacks.
|
|
109
|
+
|
|
110
|
+
## License
|
|
111
|
+
|
|
112
|
+
MIT
|
data/Service.qml
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
import QtQuick
|
|
2
|
+
|
|
3
|
+
Item {
|
|
4
|
+
id: root
|
|
5
|
+
|
|
6
|
+
required property var transport
|
|
7
|
+
property string projectDir: ""
|
|
8
|
+
property string componentDir: ""
|
|
9
|
+
property string runtimeExecutable: ""
|
|
10
|
+
property string program: ""
|
|
11
|
+
property string rubyLoadPath: ""
|
|
12
|
+
|
|
13
|
+
property bool ready: false
|
|
14
|
+
property bool stopping: false
|
|
15
|
+
property string lastError: ""
|
|
16
|
+
property var surfaces: ({})
|
|
17
|
+
property var surfaceOptions: ({})
|
|
18
|
+
property var nodeIndex: ({})
|
|
19
|
+
property var componentDefinitions: ({})
|
|
20
|
+
property int revision: 0
|
|
21
|
+
property int restartDelayMs: 500
|
|
22
|
+
property int eventSequence: 0
|
|
23
|
+
readonly property int maxStringLength: 16384
|
|
24
|
+
readonly property int maxCollectionItems: 256
|
|
25
|
+
|
|
26
|
+
property var allowedTypes: ({})
|
|
27
|
+
property var allowedProperties: ({})
|
|
28
|
+
|
|
29
|
+
signal effectReceived(string name, var payload)
|
|
30
|
+
|
|
31
|
+
function reportComponentError(surfaceName, controlId, componentType, code, message) {
|
|
32
|
+
var location = String(surfaceName || "") + "/" + String(controlId || "")
|
|
33
|
+
var detail = String(componentType || "component") + " error at " + location
|
|
34
|
+
if (code) detail += " [" + String(code) + "]"
|
|
35
|
+
if (message) detail += ": " + String(message)
|
|
36
|
+
lastError = detail.slice(0, 512)
|
|
37
|
+
console.warn("zui component:", lastError)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function validId(value) {
|
|
41
|
+
return typeof value === "string"
|
|
42
|
+
&& value.length > 0
|
|
43
|
+
&& value.length <= 128
|
|
44
|
+
&& /^[a-zA-Z0-9_.:-]+$/.test(value)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function plainObject(value) {
|
|
48
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function boundedValue(value, depth) {
|
|
52
|
+
if (depth > 8) return false
|
|
53
|
+
if (value === null || typeof value === "number" || typeof value === "boolean") return true
|
|
54
|
+
if (typeof value === "string") return value.length <= maxStringLength
|
|
55
|
+
if (Array.isArray(value)) {
|
|
56
|
+
if (value.length > maxCollectionItems) return false
|
|
57
|
+
for (var i = 0; i < value.length; i++) if (!boundedValue(value[i], depth + 1)) return false
|
|
58
|
+
return true
|
|
59
|
+
}
|
|
60
|
+
if (plainObject(value)) {
|
|
61
|
+
var keys = Object.keys(value)
|
|
62
|
+
if (keys.length > maxCollectionItems) return false
|
|
63
|
+
for (var k = 0; k < keys.length; k++) {
|
|
64
|
+
if (keys[k].length > 128 || !boundedValue(value[keys[k]], depth + 1)) return false
|
|
65
|
+
}
|
|
66
|
+
return true
|
|
67
|
+
}
|
|
68
|
+
return false
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function validateNode(node, index, depth, count) {
|
|
72
|
+
if (!plainObject(node) || depth > 32 || count.value >= 2000) return false
|
|
73
|
+
if (!allowedTypes[String(node.type || "")] || !validId(node.id)) return false
|
|
74
|
+
if (index[node.id] !== undefined) return false
|
|
75
|
+
|
|
76
|
+
var props = node.props === undefined ? {} : node.props
|
|
77
|
+
if (!plainObject(props)) return false
|
|
78
|
+
var whitelist = allowedProperties[node.type]
|
|
79
|
+
for (var key in props) {
|
|
80
|
+
if (!whitelist[key]) return false
|
|
81
|
+
var value = props[key]
|
|
82
|
+
if (!boundedValue(value, 0)) return false
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
var children = node.children === undefined ? [] : node.children
|
|
86
|
+
if (!Array.isArray(children)) return false
|
|
87
|
+
if (!componentDefinitions[node.type].container && children.length !== 0)
|
|
88
|
+
return false
|
|
89
|
+
var subscriptions = node.events === undefined ? [] : node.events
|
|
90
|
+
if (!Array.isArray(subscriptions)) return false
|
|
91
|
+
for (var e = 0; e < subscriptions.length; e++) {
|
|
92
|
+
var eventName = String(subscriptions[e])
|
|
93
|
+
if (eventName !== "mount" && eventName !== "unmount" && componentDefinitions[node.type].events.indexOf(eventName) < 0)
|
|
94
|
+
return false
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
index[node.id] = node
|
|
98
|
+
count.value += 1
|
|
99
|
+
for (var i = 0; i < children.length; i++)
|
|
100
|
+
if (!validateNode(children[i], index, depth + 1, count)) return false
|
|
101
|
+
return true
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function validateComponents(components) {
|
|
105
|
+
if (!plainObject(components)) return false
|
|
106
|
+
var names = Object.keys(components)
|
|
107
|
+
if (names.length === 0 || names.length > 256) return false
|
|
108
|
+
var validated = ({})
|
|
109
|
+
for (var i = 0; i < names.length; i++) {
|
|
110
|
+
var name = names[i]
|
|
111
|
+
var definition = components[name]
|
|
112
|
+
if (!/^[a-z][a-z0-9_]*$/.test(name) || !plainObject(definition)) return false
|
|
113
|
+
if (!/^[A-Z][A-Za-z0-9]*\.qml$/.test(String(definition.qml || ""))) return false
|
|
114
|
+
if (!Array.isArray(definition.properties) || !Array.isArray(definition.events)
|
|
115
|
+
|| !plainObject(definition.property_map || {}) || !plainObject(definition.event_map || {})) return false
|
|
116
|
+
var propertyMap = ({})
|
|
117
|
+
for (var p = 0; p < definition.properties.length; p++) {
|
|
118
|
+
var propertyName = String(definition.properties[p])
|
|
119
|
+
if (!/^[a-z][a-z0-9_]*$/.test(propertyName)) return false
|
|
120
|
+
propertyMap[propertyName] = true
|
|
121
|
+
}
|
|
122
|
+
validated[name] = {
|
|
123
|
+
qml: definition.qml,
|
|
124
|
+
properties: propertyMap,
|
|
125
|
+
events: definition.events,
|
|
126
|
+
propertyMap: definition.property_map || {},
|
|
127
|
+
eventMap: definition.event_map || {},
|
|
128
|
+
container: definition.container === true,
|
|
129
|
+
autoBind: definition.auto_bind !== false
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
componentDefinitions = validated
|
|
133
|
+
return true
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function installRender(message) {
|
|
137
|
+
if (!plainObject(message.surfaces)) return reject("render surfaces must be an object")
|
|
138
|
+
if (message.surface_options !== undefined && !validateSurfaceOptions(message.surface_options))
|
|
139
|
+
return reject("invalid surface options")
|
|
140
|
+
if (!validateComponents(message.components)) return reject("invalid component registry")
|
|
141
|
+
var dynamicTypes = ({})
|
|
142
|
+
var dynamicProperties = ({})
|
|
143
|
+
for (var componentName in componentDefinitions) {
|
|
144
|
+
dynamicTypes[componentName] = true
|
|
145
|
+
dynamicProperties[componentName] = componentDefinitions[componentName].properties
|
|
146
|
+
}
|
|
147
|
+
allowedTypes = dynamicTypes
|
|
148
|
+
allowedProperties = dynamicProperties
|
|
149
|
+
var nextIndex = ({})
|
|
150
|
+
var count = { value: 0 }
|
|
151
|
+
var names = Object.keys(message.surfaces)
|
|
152
|
+
if (names.length === 0 || names.length > 32) return reject("invalid surface count")
|
|
153
|
+
|
|
154
|
+
for (var i = 0; i < names.length; i++) {
|
|
155
|
+
if (!validId(names[i]) || !validateNode(message.surfaces[names[i]], nextIndex, 0, count))
|
|
156
|
+
return reject("invalid control tree")
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
surfaces = message.surfaces
|
|
160
|
+
surfaceOptions = message.surface_options || ({})
|
|
161
|
+
nodeIndex = nextIndex
|
|
162
|
+
revision += 1
|
|
163
|
+
lastError = ""
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function validateSurfaceOptions(options) {
|
|
167
|
+
if (!plainObject(options)) return false
|
|
168
|
+
var allowed = {
|
|
169
|
+
title: true, width: true, height: true, min_width: true, min_height: true,
|
|
170
|
+
max_width: true, max_height: true, color: true, visible: true,
|
|
171
|
+
maximized: true, fullscreen: true
|
|
172
|
+
}
|
|
173
|
+
for (var surfaceName in options) {
|
|
174
|
+
if (!validId(surfaceName) || !plainObject(options[surfaceName])) return false
|
|
175
|
+
for (var key in options[surfaceName]) {
|
|
176
|
+
if (!allowed[key]) return false
|
|
177
|
+
var value = options[surfaceName][key]
|
|
178
|
+
if (value !== null && typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean")
|
|
179
|
+
return false
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return true
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function applyPatch(message) {
|
|
186
|
+
if (!validId(message.id))
|
|
187
|
+
return reject("invalid patch")
|
|
188
|
+
var node = nodeIndex[message.id]
|
|
189
|
+
if (!node) return reject("patch target rejected")
|
|
190
|
+
|
|
191
|
+
if (message.op === "replace_children") {
|
|
192
|
+
if (!componentDefinitions[node.type].container || !Array.isArray(message.children))
|
|
193
|
+
return reject("children patch rejected")
|
|
194
|
+
var removed = ({})
|
|
195
|
+
function markRemoved(child) {
|
|
196
|
+
removed[child.id] = true
|
|
197
|
+
var nested = Array.isArray(child.children) ? child.children : []
|
|
198
|
+
for (var r = 0; r < nested.length; r++) markRemoved(nested[r])
|
|
199
|
+
}
|
|
200
|
+
var oldChildren = Array.isArray(node.children) ? node.children : []
|
|
201
|
+
for (var oldIndex = 0; oldIndex < oldChildren.length; oldIndex++) markRemoved(oldChildren[oldIndex])
|
|
202
|
+
|
|
203
|
+
var childrenIndex = ({})
|
|
204
|
+
for (var existingId in nodeIndex)
|
|
205
|
+
if (!removed[existingId] && existingId !== node.id) childrenIndex[existingId] = nodeIndex[existingId]
|
|
206
|
+
var count = { value: Object.keys(childrenIndex).length }
|
|
207
|
+
for (var childIndex = 0; childIndex < message.children.length; childIndex++)
|
|
208
|
+
if (!validateNode(message.children[childIndex], childrenIndex, 0, count)) return reject("invalid children patch")
|
|
209
|
+
|
|
210
|
+
var containerReplacement = ({ type: node.type, id: node.id, props: node.props || {}, children: message.children })
|
|
211
|
+
if (node.events !== undefined) containerReplacement.events = node.events
|
|
212
|
+
childrenIndex[node.id] = containerReplacement
|
|
213
|
+
nodeIndex = childrenIndex
|
|
214
|
+
revision += 1
|
|
215
|
+
return true
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (message.op === "animate") {
|
|
219
|
+
if (!Array.isArray(message.tracks) || message.tracks.length === 0 || message.tracks.length > 64)
|
|
220
|
+
return reject("animation tracks rejected")
|
|
221
|
+
var animatedProps = ({})
|
|
222
|
+
var validatedTracks = []
|
|
223
|
+
var sourceProps = node.props || {}
|
|
224
|
+
for (var trackIndex = 0; trackIndex < message.tracks.length; trackIndex++) {
|
|
225
|
+
var track = message.tracks[trackIndex]
|
|
226
|
+
if (!plainObject(track) || typeof track.property !== "string"
|
|
227
|
+
|| !allowedProperties[node.type][track.property] || !validAnimation(track))
|
|
228
|
+
return reject("animation track rejected")
|
|
229
|
+
var targetValue = track.to
|
|
230
|
+
if (targetValue !== null && typeof targetValue !== "string" && typeof targetValue !== "number")
|
|
231
|
+
return reject("animation target rejected")
|
|
232
|
+
animatedProps[track.property] = targetValue
|
|
233
|
+
validatedTracks.push(track)
|
|
234
|
+
}
|
|
235
|
+
var animatedReplacement = ({ type: node.type, id: node.id })
|
|
236
|
+
var finalProps = ({})
|
|
237
|
+
for (var sourceKey in sourceProps) finalProps[sourceKey] = sourceProps[sourceKey]
|
|
238
|
+
for (var animatedKey in animatedProps) finalProps[animatedKey] = animatedProps[animatedKey]
|
|
239
|
+
animatedReplacement.props = finalProps
|
|
240
|
+
if (node.children !== undefined) animatedReplacement.children = node.children
|
|
241
|
+
if (node.events !== undefined) animatedReplacement.events = node.events
|
|
242
|
+
animatedReplacement.transitions = validatedTracks
|
|
243
|
+
var animatedIndex = ({})
|
|
244
|
+
for (var animatedId in nodeIndex) animatedIndex[animatedId] = nodeIndex[animatedId]
|
|
245
|
+
animatedIndex[node.id] = animatedReplacement
|
|
246
|
+
nodeIndex = animatedIndex
|
|
247
|
+
revision += 1
|
|
248
|
+
return true
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (message.op !== "set" || typeof message.property !== "string") return reject("invalid patch")
|
|
252
|
+
if (!allowedProperties[node.type][message.property]) return reject("patch target rejected")
|
|
253
|
+
var value = message.value
|
|
254
|
+
if (!boundedValue(value, 0)) return reject("patch value rejected")
|
|
255
|
+
|
|
256
|
+
var animation = message.animation
|
|
257
|
+
if (animation !== undefined && !validAnimation(animation)) return reject("patch animation rejected")
|
|
258
|
+
|
|
259
|
+
var replacement = ({ type: node.type, id: node.id })
|
|
260
|
+
var props = ({})
|
|
261
|
+
var oldProps = node.props || {}
|
|
262
|
+
for (var key in oldProps) props[key] = oldProps[key]
|
|
263
|
+
props[message.property] = value
|
|
264
|
+
replacement.props = props
|
|
265
|
+
if (node.children !== undefined) replacement.children = node.children
|
|
266
|
+
if (node.events !== undefined) replacement.events = node.events
|
|
267
|
+
if (animation !== undefined) {
|
|
268
|
+
replacement.transition = {
|
|
269
|
+
property: message.property,
|
|
270
|
+
from: oldProps[message.property],
|
|
271
|
+
to: value,
|
|
272
|
+
duration: animation.duration,
|
|
273
|
+
delay: animation.delay,
|
|
274
|
+
easing: animation.easing,
|
|
275
|
+
sequence: revision + 1
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
var nextIndex = ({})
|
|
280
|
+
for (var id in nodeIndex) nextIndex[id] = nodeIndex[id]
|
|
281
|
+
nextIndex[node.id] = replacement
|
|
282
|
+
nodeIndex = nextIndex
|
|
283
|
+
revision += 1
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function validAnimation(animation) {
|
|
287
|
+
if (!plainObject(animation)
|
|
288
|
+
|| typeof animation.duration !== "number" || animation.duration < 0 || animation.duration > 60000
|
|
289
|
+
|| typeof animation.delay !== "number" || animation.delay < 0 || animation.delay > 60000
|
|
290
|
+
|| typeof animation.easing !== "string") return false
|
|
291
|
+
var easings = ["linear", "in_quad", "out_quad", "in_out_quad", "in_cubic", "out_cubic", "in_out_cubic",
|
|
292
|
+
"in_back", "out_back", "in_out_back", "in_elastic", "out_elastic", "in_out_elastic",
|
|
293
|
+
"in_bounce", "out_bounce", "in_out_bounce"]
|
|
294
|
+
return easings.indexOf(animation.easing) >= 0
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function handleLine(line) {
|
|
298
|
+
var raw = String(line || "").trim()
|
|
299
|
+
if (raw === "" || raw.length > 1048576) return reject("invalid message size")
|
|
300
|
+
|
|
301
|
+
var message
|
|
302
|
+
try { message = JSON.parse(raw) }
|
|
303
|
+
catch (error) { return reject("invalid JSON from Ruby") }
|
|
304
|
+
if (!plainObject(message) || message.v !== 1 || typeof message.type !== "string")
|
|
305
|
+
return reject("invalid protocol envelope")
|
|
306
|
+
|
|
307
|
+
if (message.type === "ready") {
|
|
308
|
+
ready = true
|
|
309
|
+
restartDelayMs = 500
|
|
310
|
+
lastError = ""
|
|
311
|
+
} else if (message.type === "render") {
|
|
312
|
+
installRender(message)
|
|
313
|
+
} else if (message.type === "patch") {
|
|
314
|
+
applyPatch(message)
|
|
315
|
+
} else if (message.type === "effect") {
|
|
316
|
+
handleEffect(message)
|
|
317
|
+
} else if (message.type === "ack") {
|
|
318
|
+
// Acknowledgements are consumed by benchmarks and future diagnostics.
|
|
319
|
+
} else if (message.type === "handler_error" || message.type === "runtime_error" || message.type === "protocol_error") {
|
|
320
|
+
lastError = String(message.message || message.code || "Ruby runtime error").slice(0, 512)
|
|
321
|
+
} else {
|
|
322
|
+
reject("unknown message type")
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function handleEffect(message) {
|
|
327
|
+
if (typeof message.name !== "string" || message.name.length > 64
|
|
328
|
+
|| !plainObject(message.payload || {}) || !boundedValue(message.payload || {}, 0))
|
|
329
|
+
return reject("invalid effect")
|
|
330
|
+
var payload = message.payload || {}
|
|
331
|
+
effectReceived(message.name, payload)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function reject(reason) {
|
|
335
|
+
lastError = String(reason || "protocol error").slice(0, 512)
|
|
336
|
+
console.warn("zui bridge:", lastError)
|
|
337
|
+
return false
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function rootId(surfaceName) {
|
|
341
|
+
var surface = surfaces[String(surfaceName || "")]
|
|
342
|
+
return surface && validId(surface.id) ? surface.id : ""
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function optionsFor(surfaceName) {
|
|
346
|
+
var options = surfaceOptions[String(surfaceName || "")]
|
|
347
|
+
return plainObject(options) ? options : ({})
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function nodeFor(controlId) {
|
|
351
|
+
return nodeIndex[String(controlId || "")] || null
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function componentSource(typeName) {
|
|
355
|
+
var definition = componentDefinitions[String(typeName || "")]
|
|
356
|
+
return definition ? componentDir + "/" + definition.qml : ""
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function componentDefinition(typeName) {
|
|
360
|
+
return componentDefinitions[String(typeName || "")] || null
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function sendEvent(surfaceName, controlId, eventName, payload) {
|
|
364
|
+
if (!transport.running || !validId(controlId) || !/^[a-z][a-z0-9_]{0,63}$/.test(eventName)
|
|
365
|
+
|| !plainObject(payload || {}) || !boundedValue(payload || {}, 0)) return false
|
|
366
|
+
var target = nodeIndex[String(controlId)]
|
|
367
|
+
var definition = target ? componentDefinitions[String(target.type)] : null
|
|
368
|
+
var subscriptions = target && Array.isArray(target.events) ? target.events : []
|
|
369
|
+
if (!definition || subscriptions.indexOf(eventName) < 0) return false
|
|
370
|
+
eventSequence += 1
|
|
371
|
+
transport.write(JSON.stringify({
|
|
372
|
+
v: 1,
|
|
373
|
+
type: "event",
|
|
374
|
+
surface: String(surfaceName || ""),
|
|
375
|
+
id: String(controlId),
|
|
376
|
+
event: eventName,
|
|
377
|
+
seq: eventSequence,
|
|
378
|
+
payload: plainObject(payload) ? payload : {}
|
|
379
|
+
}) + "\n")
|
|
380
|
+
return true
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function startRuby() {
|
|
384
|
+
if (stopping || projectDir === "" || program === "" || transport.running) return
|
|
385
|
+
transport.start(runtimeExecutable, program, projectDir, rubyLoadPath)
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
onProjectDirChanged: Qt.callLater(startRuby)
|
|
389
|
+
onProgramChanged: Qt.callLater(startRuby)
|
|
390
|
+
Component.onCompleted: Qt.callLater(startRuby)
|
|
391
|
+
|
|
392
|
+
Component.onDestruction: {
|
|
393
|
+
stopping = true
|
|
394
|
+
restartTimer.stop()
|
|
395
|
+
if (transport.running) transport.stop()
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
Connections {
|
|
399
|
+
target: transport
|
|
400
|
+
function onLineReceived(line) { root.handleLine(line) }
|
|
401
|
+
function onErrorLineReceived(line) {
|
|
402
|
+
var message = String(line || "").trim()
|
|
403
|
+
if (message !== "") console.warn("zui ruby:", message)
|
|
404
|
+
}
|
|
405
|
+
function onExited(exitCode) {
|
|
406
|
+
root.ready = false
|
|
407
|
+
if (root.stopping || root.projectDir === "") return
|
|
408
|
+
root.lastError = exitCode === 0
|
|
409
|
+
? "Zui runtime stopped"
|
|
410
|
+
: "Zui runtime crashed (exit " + exitCode + ")"
|
|
411
|
+
restartTimer.interval = root.restartDelayMs
|
|
412
|
+
restartTimer.start()
|
|
413
|
+
root.restartDelayMs = Math.min(30000, root.restartDelayMs * 2)
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
Timer {
|
|
418
|
+
interval: 100
|
|
419
|
+
repeat: true
|
|
420
|
+
running: transport.running
|
|
421
|
+
onTriggered: transport.write('{"v":1,"type":"tick"}\n')
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
Timer {
|
|
425
|
+
id: restartTimer
|
|
426
|
+
interval: 500
|
|
427
|
+
repeat: false
|
|
428
|
+
onTriggered: root.startRuby()
|
|
429
|
+
}
|
|
430
|
+
}
|
data/Theme/Border.qml
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
pragma Singleton
|
|
2
|
+
import QtQuick
|
|
3
|
+
|
|
4
|
+
QtObject {
|
|
5
|
+
function none() {
|
|
6
|
+
return ({ color: "transparent", widths: ({ top: 0, right: 0, bottom: 0, left: 0 }) })
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function widths(value) {
|
|
10
|
+
if (typeof value === "object") return value
|
|
11
|
+
var width = Number(value || 0)
|
|
12
|
+
return ({ top: width, right: width, bottom: width, left: width })
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function flat(color, width) {
|
|
16
|
+
return ({ color: color, widths: widths(width), gradient: ({ enabled: false, colors: [] }) })
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function controlSpec(_kind, color, _accent) {
|
|
20
|
+
return flat(color, 1)
|
|
21
|
+
}
|
|
22
|
+
}
|
data/Theme/Color.qml
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
pragma Singleton
|
|
2
|
+
import QtQuick
|
|
3
|
+
|
|
4
|
+
QtObject {
|
|
5
|
+
readonly property color background: "#0b1118"
|
|
6
|
+
readonly property color foreground: "#edf5f7"
|
|
7
|
+
readonly property color accent: "#59e1ff"
|
|
8
|
+
readonly property color muted: "#78909c"
|
|
9
|
+
readonly property color urgent: "#ff5d7a"
|
|
10
|
+
readonly property color border: "#29404c"
|
|
11
|
+
|
|
12
|
+
readonly property QtObject popups: QtObject {
|
|
13
|
+
readonly property color background: "#111c26"
|
|
14
|
+
readonly property color border: "#2c4654"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
readonly property QtObject tooltip: QtObject {
|
|
18
|
+
readonly property color background: "#172631"
|
|
19
|
+
readonly property color text: "#f4fbfd"
|
|
20
|
+
readonly property color border: "#355363"
|
|
21
|
+
}
|
|
22
|
+
}
|