This website is built entirely using Java
Objectos Way allows you to create complete web applications using nothing but the Java programming language.
Live Objectos Way Demo
This demo is written entirely in Java
using Objectos Way, JDK 23 and the H2 database engine.
The main panel displays the application itself,
while the secondary panel shows the source code used to generate the view.
Now Showing
Please choose a movie
/* * Copyright (C) 2024-2025 Objectos Software LTDA. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package demo.landing.app;import java.util.List;import objectos.way.Html;import objectos.way.Http;import objectos.way.Sql;/** * The "Now Showing" controller. */final class NowShowing implements Kino.GET { private final Kino.Ctx ctx; NowShowing(Kino.Ctx ctx) { this.ctx = ctx; } @Override public final Html.Component get(Http.Exchange http) { final Sql.Transaction trx; trx = http.get(Sql.Transaction.class); final List<NowShowingModel> items; items = NowShowingModel.query(trx); return Shell.create(shell -> { shell.appFrame = shell.sourceFrame = "now-showing"; shell.app = new NowShowingView(ctx, items); shell.sources( Source.NowShowing, Source.NowShowingModel, Source.NowShowingView ); }); }}
/* * Copyright (C) 2024-2025 Objectos Software LTDA. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package demo.landing.app;import java.sql.ResultSet;import java.sql.SQLException;import java.util.List;import objectos.way.Sql;/** * Represents a movie in the "Now Showing" view. */record NowShowingModel( int id, String title) { private NowShowingModel(ResultSet rs, int idx) throws SQLException { this( rs.getInt(idx++), rs.getString(idx++) ); } public static List<NowShowingModel> query(Sql.Transaction trx) { trx.sql(""" select MOVIE.MOVIE_ID, MOVIE.TITLE from MOVIE order by MOVIE_ID """); return trx.query(NowShowingModel::new); }}
/* * Copyright (C) 2024-2025 Objectos Software LTDA. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package demo.landing.app;import java.util.List;import objectos.way.Css;/** * Renders the "Now Showing" view. */@Css.Sourcefinal class NowShowingView extends Kino.View { private final Kino.Ctx ctx; private final List<NowShowingModel> items; NowShowingView(Kino.Ctx ctx, List<NowShowingModel> items) { this.ctx = ctx; this.items = items; } @Override protected final void render() { div( css(""" margin-bottom:32rx """), h2( text("Now Showing") ), p( text("Please choose a movie") ) ); ul( css(""" display:flex flex-wrap:wrap gap:16rx justify-content:space-evenly """), renderFragment(this::renderItems) ); } private void renderItems() { for (NowShowingModel item : items) { li( css(""" flex:0_0_128rx """), a( css(""" group """), dataOnClick(this::navigate), href(ctx.href(Kino.Page.MOVIE, item.id())), rel("nofollow"), img( css(""" aspect-ratio:2/3 background-color:neutral-400 border-radius:6rx group-hover:outline:2px_solid_gray-500 """), src("/demo/landing/poster" + item.id() + ".jpg") ), h3( css(""" font-size:14rx line-height:18rx text-align:center padding-top:8rx group-hover:text-decoration:underline """), text( testableField("movie.title", item.title()) ) ) ) ); } }}
/* * Copyright (C) 2024-2025 Objectos Software LTDA. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package demo.landing.app;import demo.landing.LandingDemo;import demo.landing.LandingDemoConfig;import java.time.Clock;import java.time.LocalDateTime;import java.util.Objects;import objectos.way.Css;import objectos.way.Html;import objectos.way.Http;import objectos.way.Note;import objectos.way.Script;import objectos.way.Sql;/** * Demo entry point. */public final class Kino implements LandingDemo { private final Ctx ctx; private Kino(Ctx ctx) { this.ctx = ctx; } /** * Creates a new {@code Demo} instance with the specified configuration. */ public static Kino create(LandingDemoConfig config) { Objects.requireNonNull(config, "config == null"); final Ctx ctx; ctx = Ctx.of(config); return new Kino(ctx); } /** * Handles a GET request. * * <p> * Typically this would be a {@code Http.Handler} instance. However, as this * will be embedded in another application, we return a HTML component * instead. */ @Override public final Html.Component get(Http.Exchange http) { final Query query; query = ctx.decode(http); // based on the 'demo' value we create our controller final GET controller; controller = switch (query.page) { case CONFIRM -> new Confirm(ctx); case MOVIE -> new Movie(ctx); case NOW_SHOWING -> new NowShowing(ctx); case SEATS -> new Seats(ctx); case TICKET -> new Ticket(); case BAD_REQUEST -> new NotFound(); }; // we intercept all controllers even though // not all require DB access strictly speaking return ctx.transactional(http, controller); } /** * Handles a POST request. */ @Override public final Kino.PostResult post(Http.Exchange http) { final Query query; query = ctx.decode(http); final POST controller; controller = switch (query.page) { case CONFIRM -> new Confirm(ctx); case SEATS -> new Seats(ctx); default -> new NotFound(); }; return ctx.transactional(http, controller); } // // UI related classes // /** * SVG icons from the Lucide project. */ enum Icon { ARROW_LEFT(""" <path d="m12 19-7-7 7-7"/><path d="M19 12H5"/>"""), CALENDAR_CHECK(""" <path d="M8 2v4"/><path d="M16 2v4"/><rect width="18" height="18" x="3" y="4" rx="2"/><path d="M3 10h18"/><path d="m9 16 2 2 4-4"/>"""), CLOCK(""" <circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>"""), CREDIT_CARD(""" <rect width="20" height="14" x="2" y="5" rx="2"/><line x1="2" x2="22" y1="10" y2="10"/>"""), FILM( """ <rect width="18" height="18" x="3" y="3" rx="2"/><path d="M7 3v18"/><path d="M3 7.5h4"/><path d="M3 12h18"/><path d="M3 16.5h4"/><path d="M17 3v18"/><path d="M17 7.5h4"/><path d="M17 16.5h4"/>"""), FROWN(""" <circle cx="12" cy="12" r="10"/><path d="M16 16s-1.5-2-4-2-4 2-4 2"/><line x1="9" x2="9.01" y1="9" y2="9"/><line x1="15" x2="15.01" y1="9" y2="9"/>"""), INFO(""" <circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/>"""), PROJECTOR( """ <path d="M5 7 3 5"/><path d="M9 6V3"/><path d="m13 7 2-2"/><circle cx="9" cy="13" r="3"/><path d="M11.83 12H20a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2h2.17"/><path d="M16 16h2"/>"""), RECEIPT(""" <path d="M4 2v20l2-1 2 1 2-1 2 1 2-1 2 1 2-1 2 1V2l-2 1-2-1-2 1-2-1-2 1-2-1-2 1Z"/><path d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8"/><path d="M12 17.5v-11"/>"""), TICKET(""" <path d="M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z"/><path d="M13 5v2"/><path d="M13 17v2"/><path d="M13 11v2"/>"""); final String contents; Icon(String contents) { this.contents = contents; } } /** * Base HTML template of the application. Provides a utility methods for * rendering UI fragments common to the application. */ @Css.Source // Indicates to the CSS Generator that it should scan this class for CSS utilities. static abstract class View extends Html.Template { static final Html.ClassName PRIMARY = Html.ClassName.ofText(""" appearance:none background-color:btn-primary color:btn-primary-text cursor:pointer display:flex font-size:14rx min-height:48rx padding:14rx_63rx_14rx_15rx active:background-color:btn-primary-active hover:background-color:btn-primary-hover """); // // component methods // /** * Renders the "Go Back" link. */ final Html.Instruction.OfElement backLink(Ctx ctx, Page page) { testableField("back-link", page.name()); return backLink(ctx.href(page)); } /** * Renders the "Go Back" link. */ final Html.Instruction.OfElement backLink(Ctx ctx, Page page, long id) { testableField("back-link", page.name() + ":" + id); return backLink(ctx.href(page, id)); } /** * Renders the "Go Back" link. */ final Html.Instruction.OfElement backLink(Ctx ctx, Page page, long id, int aux) { testableField("back-link", page.name() + ":" + id + ":" + aux); Query query = page.query(id, aux); return backLink(ctx.href(query)); } /** * Renders the "Go Back" link. */ final Html.Instruction.OfElement backLink(String href) { return a( css(""" border-radius:9999px padding:6rx margin:6rx_0_0_-6rx position:absolute active:background-color:btn-ghost-active hover:background-color:btn-ghost-hover """), dataOnClick(this::navigate), href(href), rel("nofollow"), icon( Kino.Icon.ARROW_LEFT, css(""" height:20rx width:20rx """) ) ); } final Html.Instruction.OfAttribute formAction(Ctx ctx, Page page, long id) { testableField("action", page.name() + ":" + id); return action(ctx.action(page, id)); } final Html.Instruction.OfAttribute formAction(Ctx ctx, Page page, long id, int aux) { testableField("action", page.name() + ":" + id + ":" + aux); return action(ctx.action(page, id, aux)); } /** * Renders a Lucide SVG icon. */ final Html.Instruction icon(Kino.Icon icon, Html.Instruction... more) { return svg( xmlns("http://www.w3.org/2000/svg"), width("24"), height("24"), viewBox("0 0 24 24"), fill("none"), stroke("currentColor"), strokeWidth("2"), strokeLinecap("round"), strokeLinejoin("round"), flatten(more), raw(icon.contents) ); } /* * Typically we would use Script::navigate for "boosted" links. * But a regular Script::navigate performs a scrollTo(0,0) after * the request is completed. We don't want that as this demo is * embedded in another page. In other words, we want the scroll * position to remain the same after, e.g., we click on a movie. */ final void navigate(Script script) { var el = script.element(); script.request(req -> { req.method(Script.GET); req.url(el.attr(Html.AttributeName.HREF)); req.onSuccess(() -> { var shell = script.elementById(Shell.APP); shell.scroll(0, 0); }); }); } } // // Configuration related classes // /** * Application-level context. */ record Ctx( Clock clock, KinoCodec codec, Note.Sink noteSink, Reservation reservation, Transactional transactional ) { static Ctx of(LandingDemoConfig config) { final Clock clock; clock = config.clock; final byte[] codecKey; codecKey = config.codecKey(); final KinoCodec codec; codec = new KinoCodec(clock, codecKey); final Note.Sink noteSink; noteSink = config.noteSink; final Reservation reservation; reservation = new Reservation(clock, config.reservationEpoch, config.reservationRandom); final Transactional transactional; transactional = new Transactional(config.stage, config.database); return new Ctx(clock, codec, noteSink, reservation, transactional); } final String action(Page page, long id) { return action(page, id, 0); } final String action(Page page, long id, int aux) { Query query; query = page.query(id, aux); String demo; demo = codec.encode(query); return "/demo/landing?demo=" + demo; } final String href(Page page) { return href(page, 0L); } final String href(Page page, long id) { Query query; query = page.query(id); return href(query); } final String href(Query query) { String demo; demo = codec.encode(query); return "/index.html?demo=" + demo; } final long nextReservation() { return reservation.next(); } final <T1> void send(Note.Ref1<T1> note, T1 v1) { noteSink.send(note, v1); } final LocalDateTime today() { return LocalDateTime.now(clock); } final Html.Component transactional(Http.Exchange http, GET action) { return transactional.get(http, action); } final PostResult transactional(Http.Exchange http, POST action) { return transactional.post(http, action); } private Query decode(Http.Exchange http) { // We cannot rely on the path to render the different pages // of the application because this demo will be embedded in another page. // So, we use an URL query parameter. final String demo; demo = http.queryParam("demo"); // the query parameter value is encoded/obfuscated. // we use the codec to decode it. final Query query; query = codec.decode(demo); http.set(Query.class, query); return query; } } // // SQL related classes // private static final class Transactional { private final Kino.Stage stage; private final Sql.Database db; Transactional(Kino.Stage stage, Sql.Database db) { this.stage = stage; this.db = db; } public final Html.Component get(Http.Exchange http, Kino.GET action) { return execute(http, action::get); } public final Kino.PostResult post(Http.Exchange http, Kino.POST action) { return execute(http, action::post); } private <T> T execute(Http.Exchange http, Action<T> action) { return switch (stage) { case DEFAULT -> { final Sql.Transaction trx; trx = db.beginTransaction(Sql.READ_COMMITED); try { trx.sql("set schema CINEMA"); trx.update(); http.set(Sql.Transaction.class, trx); final T result; result = action.execute(http); trx.commit(); yield result; } catch (Throwable e) { throw trx.rollbackAndWrap(e); } finally { trx.close(); } } // this is a no-op during testing. case TESTING -> action.execute(http); }; } } // // Embedded related classes // // Most Objectos Way applications will not require the classes in this section. // They are required because this demo will be embedded in another application. // /** * Represents an HTTP action in the demo application. */ @FunctionalInterface private interface Action<T> { T execute(Http.Exchange http); } /** * Handles a GET request. Similar to a {@code Http.Handler} instance, but for * an embedded application. */ interface GET { Html.Component get(Http.Exchange http); } /** * Handles a POST request. Similar to a {@code Http.Handler} instance, but for * an embedded application. */ interface POST { PostResult post(Http.Exchange http); } /** * The pages of this application. * * <p> * As a reminder, as this application will be embedded in another one, it does * not have actual pages. */ enum Page { NOW_SHOWING, MOVIE, SEATS, CONFIRM, TICKET, BAD_REQUEST; final Query query() { return new Query(this, 0L, 0); } final Query query(long id) { return new Query(this, id, 0); } final Query query(long id, int aux) { return new Query(this, id, aux); } } /** * Represents the demo query parameter. */ record Query(Page page, long id, int aux) { final int idAsInt() { return (int) id; } }}
/* * Copyright (C) 2024-2025 Objectos Software LTDA. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package demo.landing.app;import java.util.ArrayList;import java.util.List;import java.util.Objects;import java.util.function.Consumer;import objectos.way.Css;import objectos.way.Html;import objectos.way.Syntax;/** * The demo UI shell responsible for displaying the application on the * top/right and the source code on the bottom/left. */@Css.Source // Indicates to the CSS Generator that it should scan this class for CSS utilities.final class Shell extends Kino.View { static final class Builder { String appFrame; Html.Component app; String sourceFrame; private final List<SourceModel> sources = new ArrayList<>(); private Builder() {} final void sources(SourceModel... values) { for (SourceModel value : values) { sources.add(value); } } private Shell build() { Objects.requireNonNull(appFrame, "appFrame == null"); Objects.requireNonNull(app, "app == null"); Objects.requireNonNull(sourceFrame, "sourceFrame == null"); sources.add(Source.Kino); sources.add(Source.Shell); sources.add(Source.SourceModel_); return new Shell(this); } } static final Html.Id APP = Html.Id.of("demo-app"); private final Builder builder; private Shell(Builder builder) { this.builder = builder; } public static Shell create(Consumer<Builder> config) { final Builder builder; builder = new Builder(); config.accept(builder); return builder.build(); } private final Html.Id sourceFrame = Html.Id.of("source-frame"); private final Html.AttributeName dataButton = Html.AttributeName.of("data-button"); private final Html.AttributeName dataPanel = Html.AttributeName.of("data-panel"); private final Html.AttributeName dataSelected = Html.AttributeName.of("data-selected"); @Override protected final void render() { // the demo container div( css(""" display:grid grid-template:'a'_448rx_'b'_auto_'c'_448rx_/_1fr lg:grid-template:'c_a'_512rx_'b_b'_auto_/_1fr_1fr xl:grid-template:'b_c_a'_512rx_/_200rx_1fr_1fr """), renderFragment(this::renderApp), renderFragment(this::renderSourceMenu), renderFragment(this::renderSourceCode) ); } private void renderApp() { // the app container div( APP, css(""" border:1px_solid_border overflow:auto grid-area:a position:relative lg:border-bottom-width:1px lg:border-left-width:0px """), div( css(""" align-items:center background-color:layer color:gray-500 display:flex height:64rx justify-content:space-between padding:0_16rx position:sticky top:0px z-index:8000 """), a( css(""" align-items:center display:flex gap:6rx height:100% """), href("/index.html"), dataOnClick(this::navigate), objectosLogo(), span( css(""" font-size:24rx font-weight:300 line-height:1 transform:translateY(-1px) """), text("kino") ) ), a( css(""" align-items:center border-radius:6rx display:flex padding:8rx active:background-color:btn-ghost-active hover:background-color:btn-ghost-hover """), href("https://github.com/objectos/demo.landing"), gitHubLogo() ) ), div( css(""" padding:0_16rx_16rx h2:font-size:36rx h2:font-weight:200 h2:line-height:1 h2:padding:48rx_0_8rx """), dataFrame("demo-app", builder.appFrame), renderComponent(builder.app) ) ); } private Html.Instruction objectosLogo() { return svg( css(""" width:auto height:24rx fill:logo transition:fill_300ms_ease """), xmlns("http://www.w3.org/2000/svg"), width("200"), height("49.28"), viewBox("0 0 200 49.28"), path( d("m189.6 38.21q-2.53 0-5.3-0.932-2.76-0.903-4.6-3.087-0.38-0.495-0.35-1.02 0.12-0.582 0.64-0.99 0.5-0.32 1.02-0.233 0.58 0.09 0.93 0.524 1.4 1.66 3.38 2.33 2.04 0.641 4.43 0.641 4.08 0 5.77-1.456 1.71-1.456 1.71-3.408 0-1.893-1.86-3.087-1.78-1.281-5.56-1.806-4.87-0.669-7.2-2.621-2.33-1.951-2.33-4.514 0-2.417 1.23-4.077 1.19-1.689 3.29-2.534 2.12-0.874 4.86-0.874 3.29 0 5.56 1.223 2.31 1.165 3.7 3.146 0.38 0.495 0.24 1.077-0.1 0.525-0.73 0.874-0.47 0.233-1.02 0.146-0.53-0.09-0.9-0.583-1.23-1.543-2.97-2.33-1.69-0.815-3.99-0.815-3.06 0-4.75 1.31-1.69 1.311-1.69 3.146 0 1.252 0.67 2.242 0.73 0.903 2.33 1.602 1.6 0.612 4.28 1.02 3.64 0.466 5.71 1.63 2.15 1.165 3.03 2.738 0.9 1.485 0.9 3.233 0 2.301-1.46 3.99-1.45 1.689-3.81 2.621-2.39 0.874-5.16 0.874zm-27.97 0q-3.9 0-6.99-1.748-3.05-1.805-4.85-4.863-1.75-3.088-1.75-6.932 0-3.873 1.75-6.931 1.8-3.117 4.85-4.864 3.09-1.806 6.99-1.806 3.89 0 6.95 1.806 3.05 1.747 4.8 4.864 1.81 3.058 1.81 6.931 0 3.844-1.81 6.932-1.75 3.058-4.8 4.863-3.06 1.748-6.95 1.748zm0-2.709q3.07 0 5.43-1.427 2.45-1.456 3.79-3.873 1.42-2.476 1.42-5.592 0-3.058-1.42-5.475-1.34-2.476-3.79-3.874-2.36-1.456-5.43-1.456-3.03 0-5.45 1.456-2.41 1.398-3.83 3.874-1.4 2.417-1.4 5.533 0 3.058 1.4 5.534 1.42 2.417 3.83 3.873 2.42 1.427 5.45 1.427zm-19.01 2.418q-2.65-0.06-4.75-1.224-2.09-1.194-3.26-3.291-1.16-2.126-1.16-4.805v-24.17q0-0.67 0.4-1.078 0.44-0.436 1.05-0.436 0.7 0 1.08 0.436 0.44 0.408 0.44 1.078v24.17q0 2.825 1.74 4.601 1.75 1.748 4.52 1.748h1.08q0.67 0 1.05 0.437 0.43 0.407 0.43 1.077 0 0.641-0.43 1.078-0.38 0.379-1.05 0.379zm-12.96-22.95q-0.58 0-0.96-0.35-0.35-0.378-0.35-0.961 0-0.582 0.35-0.932 0.38-0.378 0.96-0.378h13.16q0.59 0 0.94 0.378 0.38 0.35 0.38 0.932 0 0.583-0.38 0.961-0.35 0.35-0.94 0.35zm-13.09 23.24q-3.78 0-6.79-1.806-2.96-1.776-4.71-4.834-1.7-3.059-1.7-6.903 0-3.873 1.6-6.931t4.42-4.806q2.81-1.806 6.45-1.806 3.1 0 5.7 1.224 2.56 1.165 4.45 3.582 0.38 0.495 0.29 1.019-0.1 0.525-0.64 0.874-0.44 0.349-0.96 0.291-0.52-0.09-0.93-0.582-3.09-3.699-7.91-3.699-2.86 0-5.04 1.427-2.14 1.398-3.35 3.815-1.17 2.447-1.17 5.592 0 3.058 1.31 5.534 1.31 2.417 3.59 3.873 2.33 1.427 5.39 1.427 1.99 0 3.74-0.582 1.81-0.583 3.12-1.806 0.44-0.379 0.96-0.437 0.52-0.06 0.93 0.35 0.47 0.437 0.47 1.019 0.1 0.524-0.38 0.903-3.55 3.262-8.84 3.262zm-27.69-0.06q-3.84 0-6.85-1.69-2.96-1.747-4.66-4.805t-1.7-6.99q0-3.99 1.61-6.99 1.6-3.058 4.41-4.805 2.82-1.748 6.46-1.748 3.59 0 6.36 1.69 2.76 1.66 4.31 4.63 1.56 2.913 1.56 6.728 0 0.641-0.39 1.019-0.39 0.35-1.02 0.35h-21.35v-2.534h22.13l-2.14 1.602q0.1-3.146-1.07-5.563-1.16-2.446-3.35-3.786-2.13-1.427-5.04-1.427-2.77 0-4.95 1.427-2.14 1.34-3.4 3.786-1.21 2.417-1.21 5.621 0 3.146 1.31 5.592 1.31 2.417 3.64 3.815 2.33 1.369 5.34 1.369 1.89 0 3.78-0.641 1.94-0.67 3.06-1.747 0.39-0.379 0.92-0.379 0.58-0.06 0.97 0.291 0.54 0.437 0.54 0.962 0 0.553-0.44 0.99-1.55 1.398-4.08 2.33-2.47 0.903-4.75 0.903zm-30.93 11.13q-0.64 0-1.07-0.44-0.44-0.38-0.44-1.02 0-0.67 0.44-1.1 0.43-0.41 1.07-0.41 2.47 0 4.31-1.05 1.9-1.08 2.97-2.97 1.06-1.89 1.06-4.313v-25.16q0-0.67 0.39-1.049 0.44-0.408 1.07-0.408 0.68 0 1.07 0.408 0.43 0.379 0.43 1.049v25.16q0 3.291-1.45 5.821-1.46 2.57-4.03 4.02-2.52 1.46-5.82 1.46zm9.75-43.48q-0.92 0-1.6-0.641-0.63-0.67-0.63-1.66 0-1.107 0.68-1.631 0.73-0.582 1.6-0.582 0.82 0 1.5 0.582 0.73 0.524 0.73 1.631 0 0.99-0.68 1.66-0.63 0.641-1.6 0.641zm-21.55 32.42q-3.79 0-6.84-1.748-3.06-1.747-4.86-4.747-1.75-3.029-1.84-6.815v-23.44q0-0.67 0.39-1.048 0.43-0.408 1.06-0.408 0.68 0 1.07 0.408 0.39 0.378 0.39 1.048v15.14q1.55-2.505 4.32-4.019 2.82-1.515 6.31-1.515 3.88 0 6.94 1.806 3.11 1.747 4.85 4.805 1.8 3.058 1.8 6.932 0 3.902-1.8 6.99-1.74 3.058-4.85 4.863-3.06 1.748-6.94 1.748zm0-2.709q3.06 0 5.44-1.427 2.42-1.456 3.83-3.873 1.41-2.476 1.41-5.592 0-3.087-1.41-5.534-1.41-2.417-3.83-3.815-2.38-1.456-5.44-1.456-3.01 0-5.44 1.456-2.42 1.398-3.83 3.815-1.36 2.447-1.36 5.534 0 3.116 1.36 5.592 1.41 2.417 3.83 3.873 2.43 1.427 5.44 1.427zm-32.53 2.709q-3.88 0-6.99-1.748-3.06-1.805-4.85-4.863-1.75-3.088-1.75-6.932 0-3.873 1.75-6.931 1.79-3.117 4.85-4.864 3.11-1.806 6.99-1.806t6.94 1.806q3.06 1.747 4.8 4.864 1.8 3.058 1.8 6.931 0 3.844-1.8 6.932-1.74 3.058-4.8 4.863-3.06 1.748-6.94 1.748zm0-2.709q3.06 0 5.44-1.427 2.42-1.456 3.78-3.873 1.41-2.476 1.41-5.592 0-3.058-1.41-5.475-1.36-2.476-3.78-3.874-2.38-1.456-5.44-1.456-3.01 0-5.44 1.456-2.42 1.398-3.83 3.874-1.41 2.417-1.41 5.533 0 3.058 1.41 5.534 1.41 2.417 3.83 3.873 2.43 1.427 5.44 1.427z"), strokeWidth(".9101") ) ); } private Html.Instruction gitHubLogo() { return svg( css(""" width:auto height:24rx fill:logo """), xmlns("http://www.w3.org/2000/svg"), width("98"), height("96"), viewBox("0 0 98 96"), path( fillRule("evenodd"), clipRule("evenodd"), d( "M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z") ) ); } private void renderSourceMenu() { final List<SourceModel> items; items = builder.sources; final SourceModel first; first = items.getFirst(); // the source file selector div( sourceFrame, css(""" border-left:1px_solid_border border-right:1px_solid_border display:flex font-size:14rx gap:4rx_8rx grid-area:b padding:16rx overflow-x:auto lg:border-bottom:1px_solid_border xl:border-top:1px_solid_border xl:border-right-width:0px xl:flex-direction:column """), dataFrame("demo-source-menu", builder.sourceFrame), // stores the current selected button in the data-button attribute attr(dataButton, first.button().value()), // stores the current selected panel in the data-panel attribute attr(dataPanel, first.panel().value()), renderFragment(this::renderSourceMenuItems) ); } private void renderSourceMenuItems() { final List<SourceModel> items; items = builder.sources; for (int idx = 0, size = items.size(); idx < size; idx++) { final SourceModel item; item = items.get(idx); button( item.button(), css(""" border-radius:6rx cursor:pointer padding:4rx_8rx [data-selected=true]:background-color:btn-ghost-active active:background-color:btn-ghost-active hover:background-color:btn-ghost-hover """), attr(dataSelected, Boolean.toString(idx == 0)), dataOnClick(script -> { // 'deselects' current var frame = script.elementById(sourceFrame); var selectedButton = script.elementById(frame.attr(dataButton)); selectedButton.attr(dataSelected, "false"); var selectedPanel = script.elementById(frame.attr(dataPanel)); selectedPanel.attr(dataSelected, "false"); // 'selects' self var selfButton = script.elementById(item.button()); selfButton.attr(dataSelected, "true"); var selfPanel = script.elementById(item.panel()); selfPanel.attr(dataSelected, "true"); // stores selected frame.attr(dataButton, item.button().value()); frame.attr(dataPanel, item.panel().value()); }), text(item.name()) ); } } private void renderSourceCode() { // the Java source code display div( css(""" border:1px_solid_border display:flex flex-direction:column grid-area:c """), css(""" flex:1 min-height:0 overflow:auto """), dataFrame("demo-source-code", builder.sourceFrame), renderFragment(this::renderSourceCodeItems) ); } private void renderSourceCodeItems() { final List<SourceModel> items; items = builder.sources; for (int idx = 0, size = items.size(); idx < size; idx++) { final SourceModel item; item = items.get(idx); final String source; source = item.value(); pre( item.panel(), css(""" display:none font-family:mono font-size:13rx line-height:18.6rx padding:16rx [data-selected=true]:display:flex span:[data-line]:display:block span:[data-line]:min-height:1lh span:[data-line]:nth-child(-n+15):display:none span:[data-high=annotation]:color:high-meta span:[data-high=comment]:color:high-comment span:[data-high=comment]:font-style:italic span:[data-high=keyword]:color:high-keyword span:[data-high=string]:color:high-string """), attr(dataSelected, Boolean.toString(idx == 0)), code( css(""" flex-grow:1 """), renderComponent( Syntax.highlight(Syntax.JAVA, source) ) ) ); } }}
/* * Copyright (C) 2024-2025 Objectos Software LTDA. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package demo.landing.app;import objectos.way.Html;record SourceModel(String name, String value, Html.Id button, Html.Id panel) { private static int INDEX = 0; public static SourceModel create(String name, String value) { final int index; index = INDEX++; return new SourceModel( name, value, Html.Id.of("src-btn-" + index), Html.Id.of("src-panel-" + index) ); }}