Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
6badecf
Add web DOM backend skeleton: types, DOM utils, retained store, modul…
tiensonqin Sep 27, 2026
c6557f0
web/melange: port nodes module — base-class table, element factories,…
tiensonqin Sep 27, 2026
9906930
web: port position module (popup geometry, transitions, corridor)
tiensonqin Sep 27, 2026
cb7cb3c
web/events: port event listeners + attach_events dispatcher from web.…
tiensonqin Sep 27, 2026
84e8a02
web(melange): port simulator module (device emulation, keyboard visib…
tiensonqin Sep 27, 2026
1490c5e
Port apply-dom-batch DOM mutation layer + extension plumbing
tiensonqin Sep 27, 2026
21f380e
Merge remote-tracking branch 'origin/devin/lui-web-nodes' into devin/…
tiensonqin Sep 27, 2026
8357b49
Merge branch 'devin/lui-web-position' into devin/lui-web-backend
tiensonqin Sep 27, 2026
04dd343
Merge origin/devin/lui-web-events
tiensonqin Sep 27, 2026
2e0923d
Merge origin/devin/lui-web-simulator
tiensonqin Sep 27, 2026
d836b26
web/focus: port tree/horizontal/toolbar/tabs roving + radio + restore…
tiensonqin Sep 27, 2026
c635983
Port menu module (picker/combobox, dropdown, context-menu) to OCaml/M…
tiensonqin Sep 27, 2026
a425028
web/melange: port props module — apply_property!/remove_property!, cl…
tiensonqin Sep 27, 2026
a1afadc
widgets: port split panes and widget updaters to Melange
tiensonqin Sep 27, 2026
4ef0a73
Merge origin/devin/lui-web-focus
tiensonqin Sep 27, 2026
ee43c8a
Merge origin/devin/lui-web-menu
tiensonqin Sep 27, 2026
b7789a0
Merge origin/devin/lui-web-props
tiensonqin Sep 27, 2026
1ac4330
Merge origin/devin/lui-web-widgets
tiensonqin Sep 27, 2026
d7e7853
Fix cross-module seams after module merges
tiensonqin Sep 27, 2026
85d4482
Port overlay module (modals, tooltips, toasts) to OCaml/Melange
tiensonqin Sep 27, 2026
15a84ec
Merge remote-tracking branch 'origin/devin/lui-web-overlay' into devi…
tiensonqin Sep 27, 2026
fc4e066
web/melange: adapt apply layer to ported overlay signature
tiensonqin Sep 27, 2026
563fa4c
examples/components/web: pure-OCaml Melange web gallery entry
tiensonqin Sep 27, 2026
19eaba2
Merge remote-tracking branch 'origin/devin/lui-web-gallery' into devi…
tiensonqin Sep 27, 2026
4f6239d
web: fix enabled semantics, modal description slots, echo suppression…
tiensonqin Sep 27, 2026
3fcb078
web: hot reload via Melange full reload; drop banned modal descriptio…
tiensonqin Sep 27, 2026
94098f7
gallery: restore lui-combobox-query-value class on shared query text
tiensonqin Sep 27, 2026
dfae872
Merge remote-tracking branch 'origin/devin/lui-web-e2e-ime' into devi…
tiensonqin Sep 27, 2026
30f88f5
gallery: restore group aria-labels and constant-disabled toolbar cont…
tiensonqin Sep 27, 2026
5249130
Merge remote-tracking branch 'origin/devin/lui-web-e2e-groups' into d…
tiensonqin Sep 27, 2026
6bf4138
web: treat MenuTrigger as a menu row and defer picker focus via micro…
tiensonqin Sep 27, 2026
effe510
Merge remote-tracking branch 'origin/devin/lui-web-e2e-menus' into de…
tiensonqin Sep 27, 2026
115c002
web: restore accordion Selected signal binding and fix gallery audit …
tiensonqin Sep 27, 2026
f7126ad
Merge remote-tracking branch 'origin/devin/lui-web-e2e-misc' into dev…
tiensonqin Sep 27, 2026
8866b8b
web: gate hot reload on all Melange entry modules being present
tiensonqin Sep 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions examples/components/web/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
(melange.emit
(target lui-components-web)
(alias web)
(module_systems es6)
(modules web_main web_bootstrap)
(preprocess (pps melange.ppx))
(libraries
gallery_app
lui.web.dom
melange-webapi
melange.dom))
27 changes: 27 additions & 0 deletions examples/components/web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>LUI Components</title>
<link rel="stylesheet" href="/platform/web/dist/lui.css">
<link rel="stylesheet" href="./styles.css">
<script type="importmap">
{
"imports": {
"lui.web.dom/": "/_build/default/examples/components/web/lui-components-web/node_modules/lui.web.dom/",
"lui/": "/_build/default/examples/components/web/lui-components-web/node_modules/lui/",
"melange-webapi/": "/_build/default/examples/components/web/lui-components-web/node_modules/melange-webapi/",
"melange.dom/": "/_build/default/examples/components/web/lui-components-web/node_modules/melange.dom/",
"melange.js/": "/_build/default/examples/components/web/lui-components-web/node_modules/melange.js/",
"melange/": "/_build/default/examples/components/web/lui-components-web/node_modules/melange/",
"ocaml-signal/": "/_build/default/examples/components/web/lui-components-web/node_modules/ocaml-signal/"
}
}
</script>
</head>
<body>
<main id="app" aria-label="LUI component gallery"></main>
<script type="module" src="/_build/default/examples/components/web/lui-components-web/examples/components/web/web_bootstrap.js"></script>
</body>
</html>
266 changes: 266 additions & 0 deletions examples/components/web/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
* {
box-sizing: border-box;
}

body {
min-height: 100vh;
margin: 0;
color: var(--foreground);
background: var(--background);
font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

#app {
min-height: 100vh;
}

.lui-simulator-toolbar {
position: fixed;
z-index: 60;
top: max(0.75rem, env(safe-area-inset-top));
right: max(0.75rem, env(safe-area-inset-right));
padding: 0.375rem;
border: 1px solid var(--border);
border-radius: 0.75rem;
background: color-mix(in srgb, var(--background) 92%, transparent);
box-shadow: 0 0.5rem 1.5rem color-mix(in srgb, var(--foreground) 10%, transparent);
backdrop-filter: blur(1rem);
display: flex;
align-items: center;
gap: 0.375rem;
}

.lui-simulator-platform-field {
display: flex;
align-items: center;
gap: 0.5rem;
padding-left: 0.5rem;
color: var(--muted-foreground);
font-size: 0.75rem;
font-weight: 600;
}

.lui-simulator-platform-field select {
min-height: 2rem;
border: 0;
border-radius: 0.5rem;
background: var(--secondary);
color: var(--secondary-foreground);
padding: 0 1.75rem 0 0.625rem;
font: inherit;
}

.lui-simulator-toolbar > button {
min-height: 2rem;
border: 0;
border-radius: 0.5rem;
background: var(--secondary);
color: var(--secondary-foreground);
padding: 0 0.625rem;
font: inherit;
font-size: 0.75rem;
font-weight: 600;
}

.lui-gallery-shell {
display: grid;
min-height: 100%;
grid-template-columns: 16rem minmax(0, 1fr);
}

.lui-gallery-navigation-bar {
display: none;
}

.lui-gallery-sidebar {
position: sticky;
top: 0;
display: flex;
height: 100vh;
flex-direction: column;
gap: 0.25rem;
overflow-y: auto;
border-right: 1px solid var(--border);
background: var(--background);
padding: 1rem 0.75rem;
}

.lui-gallery-nav-item {
min-height: 2.25rem;
border: 0;
border-radius: 0.5rem;
background: transparent;
color: var(--muted-foreground);
padding: 0.5rem 0.75rem;
text-align: left;
}

.lui-gallery-nav-item:hover,
.lui-gallery-nav-item[data-selected="true"] {
background: var(--accent);
color: var(--accent-foreground);
}

.lui-gallery-nav-item:focus-visible {
outline: 2px solid var(--ring);
outline-offset: 2px;
}

.lui-gallery-content {
min-width: 0;
width: min(56rem, 100%);
}

.lui-native-card {
min-height: 8rem;
width: min(28rem, 100%);
border: 1px solid var(--border);
border-radius: 0.75rem;
background: var(--card);
color: var(--card-foreground);
padding: 1.5rem;
font: inherit;
text-align: left;
transition: transform 150ms ease, box-shadow 150ms ease;
}

.lui-native-card:hover,
.lui-native-card[data-active="true"] {
box-shadow: 0 0.75rem 2rem color-mix(in srgb, var(--foreground) 12%, transparent);
transform: translateY(-2px);
}

.lui-gallery-accent {
@apply rounded-xl border border-sky-200 bg-sky-50 px-3 py-2 text-sky-950 shadow-sm;
}

[data-lui-form-factor="phone"] .lui-gallery-shell {
grid-template-columns: minmax(0, 1fr);
grid-template-rows: auto minmax(0, 1fr);
}

[data-lui-form-factor="phone"] .lui-gallery-navigation-bar {
z-index: 20;
display: grid;
min-height: calc(2.75rem + var(--lui-safe-area-top));
grid-template-columns: minmax(5rem, 1fr) auto minmax(5rem, 1fr);
align-items: end;
border-bottom: 1px solid var(--border);
background: color-mix(in srgb, var(--background) 92%, transparent);
padding: var(--lui-safe-area-top) 0.5rem 0.375rem;
backdrop-filter: blur(1rem);
}

[data-lui-form-factor="phone"] .lui-gallery-navigation-back {
min-height: 2.25rem;
justify-self: start;
border: 0;
background: transparent;
color: var(--primary);
font: inherit;
}

[data-lui-form-factor="phone"] .lui-gallery-navigation-back::before {
content: "‹";
margin-right: 0.25rem;
font-size: 1.5rem;
line-height: 0;
vertical-align: -0.1rem;
}

[data-lui-form-factor="phone"] .lui-gallery-navigation-title {
grid-column: 2;
align-self: center;
color: var(--foreground);
font-weight: 600;
white-space: nowrap;
}

[data-lui-form-factor="phone"] .lui-gallery-sidebar {
position: static;
z-index: 10;
height: auto;
min-height: 0;
flex-direction: column;
overflow: auto;
border-right: 0;
padding: 1rem;
}

[data-lui-form-factor="phone"] .lui-gallery-nav-item {
width: 100%;
min-height: 2.75rem;
flex: 0 0 auto;
}

[data-lui-form-factor="phone"] .lui-gallery-shell[data-lui-navigation="detail"] .lui-gallery-sidebar {
display: none;
}

[data-lui-form-factor="phone"] .lui-gallery-content {
display: none;
}

[data-lui-form-factor="phone"] .lui-gallery-shell[data-lui-navigation="detail"] .lui-gallery-content {
display: block;
}

@media (max-width: 44.999rem) {
.lui-simulator-toolbar {
top: auto;
right: 0.5rem;
bottom: 0.5rem;
left: 0.5rem;
justify-content: center;
}

.lui-simulator-platform-field {
min-width: 0;
flex: 1 1 0;
padding-left: 0;
}

.lui-simulator-platform-field > span {
display: none;
}

.lui-simulator-platform-field select {
width: 100%;
min-width: 0;
}
}

.lui-row,
.lui-column {
display: flex;
}

.lui-row {
flex-wrap: wrap;
align-items: center;
}

.lui-column {
flex-direction: column;
}

.lui-column > .lui-text:first-child {
font-size: 1.5rem;
font-weight: 600;
letter-spacing: -0.025em;
}

.lui-text-field,
.lui-input,
.lui-search-field,
.lui-textarea {
width: min(24rem, 100%);
}

.lui-switch {
width: min(24rem, 100%);
}

.lui-separator-example-row {
height: 2rem;
}
13 changes: 13 additions & 0 deletions examples/components/web/web_bootstrap.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
let show_error host message =
Webapi.Dom.Element.setTextContent host message;
Webapi.Dom.Element.setAttribute "data-error" "true" host

let () =
Printexc.record_backtrace true;
match Webapi.Dom.Document.querySelector "#app" Webapi.Dom.document with
| None -> ()
| Some host -> (
try ignore (Web_main.main host)
with error ->
show_error host
(Printexc.to_string error ^ "\n" ^ Printexc.get_backtrace ()))
Loading
Loading