Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
6bcd0f3
chore(js-libs): Update path to generate_python.py for generated files
snacksbro Sep 24, 2026
54bec66
feat(v4): Add vite bundle scaffolding
snacksbro Sep 24, 2026
bc33975
fix(generate-python): Use third .parent call
snacksbro Sep 21, 2026
f290e6b
chore(module): Ignore v4-serve
snacksbro Sep 21, 2026
5c571c4
chore(v4): Add generated vuetify4 widgets
snacksbro Sep 21, 2026
1270342
feat(ui): Add vuetify4
snacksbro Sep 21, 2026
3f4a53c
feat(vuetify4): Expose modules into Trame namespace
snacksbro Sep 21, 2026
692ebf4
chore(pyproject): Bump version
snacksbro Sep 22, 2026
14fc394
ci: Add vuetify 4 + labs to CI steps
snacksbro Sep 24, 2026
3d14e06
chore(codespell): Ignore generated v4 widgets
snacksbro Sep 24, 2026
729c03a
fix(generate-python): Use the v4 module properly
snacksbro Sep 24, 2026
8dd1461
fix(generate-python): Import v3 module properly
snacksbro Sep 24, 2026
ee9e2ce
build(js-libs): Add helper script to install deps
snacksbro Sep 24, 2026
6507faa
chore(manifest): Include v4-serve and v4-lab-serve
snacksbro Sep 24, 2026
1601726
chore(v4): Fix typo referencing vue4
snacksbro Sep 24, 2026
9b6843b
Revert "chore(pyproject): Bump version"
snacksbro Sep 24, 2026
ab51c69
build(deps): Bump to Vite from v4->v8
snacksbro Sep 28, 2026
74328cb
build: Remove MANIFEST.in
snacksbro Sep 28, 2026
7e4f822
chore(examples): Add basic example showing how to re-make drawer layout
snacksbro Sep 28, 2026
cb64a5d
chore(examples): Add progress example
snacksbro Sep 29, 2026
566dbf4
chore(examples): Add highlight example
snacksbro Sep 29, 2026
0c33ca4
chore(examples): Add month picker example
snacksbro Sep 29, 2026
bd0a6e4
chore(examples): Add date range example
snacksbro Sep 29, 2026
0ce4d06
chore(examples): Add heatmap example
snacksbro Sep 29, 2026
06c7d0a
refactor(examples): Inherit from TrameApp base class
snacksbro Sep 29, 2026
d0e93db
refactor(examples): Rename vue->vuetify for directories
snacksbro Sep 29, 2026
b55e60d
refactor(examples): Use VAppLayout instead of SinglePageLayout
snacksbro Sep 29, 2026
2190585
chore(vuetify4): Remove SinglePageLayout and SinglePageWithDrawerLayout
snacksbro Sep 29, 2026
035ffa2
chore(examples): Add self.ui to examples from VAppLayout
snacksbro Sep 29, 2026
d8aa1d2
chore(vuetify4): Remove unused get_trame_versions
snacksbro Sep 29, 2026
46ccd30
refactor(ui): Migrate get_trame_versions to utils.py
snacksbro Sep 29, 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
2 changes: 1 addition & 1 deletion .codespellrc
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[codespell]
skip = **/package-lock.json,trame_vuetify/widgets/vuetify3.py
skip = **/package-lock.json,trame_vuetify/widgets/vuetify3.py,trame_vuetify/widgets/vuetify4.py
38 changes: 38 additions & 0 deletions .github/workflows/test_and_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,25 @@ jobs:
mkdir -p v3-lab-serve/{fonts,css}
curl https://cdn.jsdelivr.net/npm/@mdi/font@7.x/css/materialdesignicons.min.css -Lo v3-lab-serve/css/mdi.css
curl https://cdn.jsdelivr.net/npm/@mdi/font@7.x/fonts/materialdesignicons-webfont.woff2 -Lo v3-lab-serve/fonts/materialdesignicons-webfont.woff2

- name: Build Vuetify 4 Components
run: |
mkdir -p trame_vuetify/module/v4-serve/{fonts,css}
cd trame_vuetify/module/v4-serve
curl https://cdn.jsdelivr.net/npm/vuetify@4.2.1/dist/vuetify.min.css -Lo vuetify4.css
curl https://cdn.jsdelivr.net/npm/vuetify@4.2.1/dist/vuetify.min.js -Lo vuetify4.js
curl https://cdn.jsdelivr.net/npm/@mdi/font@7.x/css/materialdesignicons.min.css -Lo css/mdi.css
curl https://cdn.jsdelivr.net/npm/@mdi/font@7.x/fonts/materialdesignicons-webfont.woff2 -Lo fonts/materialdesignicons-webfont.woff2

- name: Build Vuetify 4 Lab Components
run: |
cd js-libs/v4-lab
npm ci
npm run build
cd ../../trame_vuetify/module
mkdir -p v4-lab-serve/{fonts,css}
curl https://cdn.jsdelivr.net/npm/@mdi/font@7.x/css/materialdesignicons.min.css -Lo v4-lab-serve/css/mdi.css
curl https://cdn.jsdelivr.net/npm/@mdi/font@7.x/fonts/materialdesignicons-webfont.woff2 -Lo v4-lab-serve/fonts/materialdesignicons-webfont.woff2
release:
needs: [pre-commit, pytest, test-npm-build]
runs-on: ubuntu-latest
Expand Down Expand Up @@ -143,6 +162,25 @@ jobs:
curl https://cdn.jsdelivr.net/npm/@mdi/font@7.x/css/materialdesignicons.min.css -Lo v3-lab-serve/css/mdi.css
curl https://cdn.jsdelivr.net/npm/@mdi/font@7.x/fonts/materialdesignicons-webfont.woff2 -Lo v3-lab-serve/fonts/materialdesignicons-webfont.woff2

- name: Build Vuetify 4 Components
run: |
mkdir -p trame_vuetify/module/v4-serve/{fonts,css}
cd trame_vuetify/module/v4-serve
curl https://cdn.jsdelivr.net/npm/vuetify@4.2.1/dist/vuetify.min.css -Lo vuetify4.css
curl https://cdn.jsdelivr.net/npm/vuetify@4.2.1/dist/vuetify.min.js -Lo vuetify4.js
curl https://cdn.jsdelivr.net/npm/@mdi/font@7.x/css/materialdesignicons.min.css -Lo css/mdi.css
curl https://cdn.jsdelivr.net/npm/@mdi/font@7.x/fonts/materialdesignicons-webfont.woff2 -Lo fonts/materialdesignicons-webfont.woff2

- name: Build Vuetify4-lab Components
run: |
cd js-libs/v4-lab
npm ci
npm run build
cd ../../trame_vuetify/module
mkdir -p v4-lab-serve/{fonts,css}
curl https://cdn.jsdelivr.net/npm/@mdi/font@7.x/css/materialdesignicons.min.css -Lo v4-lab-serve/css/mdi.css
curl https://cdn.jsdelivr.net/npm/@mdi/font@7.x/fonts/materialdesignicons-webfont.woff2 -Lo v4-lab-serve/fonts/materialdesignicons-webfont.woff2

- name: Python Semantic Release
id: release
uses: python-semantic-release/python-semantic-release@v10.6.2
Expand Down
10 changes: 0 additions & 10 deletions MANIFEST.in

This file was deleted.

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
79 changes: 79 additions & 0 deletions examples/vuetify4-labs/basic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import asyncio

from trame.app import get_server

from trame.ui.vuetify4 import VAppLayout
from trame.widgets import html, vuetify4

# -----------------------------------------------------------------------------
# Trame setup
# -----------------------------------------------------------------------------

server = get_server()
server.client_type = "vue3"
state, ctrl = server.state, server.controller

state.trame__title = "Menu example"
state.drawer_open = False
state.menu_items = ["one", "two", "three"]
state.theme = "dark"


@ctrl.add("on_server_reload")
def print_item(item):
print("Clicked on", item)


async def busy():
await asyncio.sleep(5)


# -----------------------------------------------------------------------------
# GUI
# -----------------------------------------------------------------------------


with VAppLayout(server) as layout:
layout.root.theme = ("theme",)
with vuetify4.VLayout():
with vuetify4.VAppBar():
with html.Div(classes="d-flex w-100"):
vuetify4.VBtn(icon="mdi-menu", click="drawer_open = !drawer_open")
vuetify4.VSpacer()
vuetify4.VCheckboxBtn(
v_model="theme",
density="compact",
false_icon="mdi-theme-light-dark",
false_value="dark",
true_icon="mdi-theme-light-dark",
true_value="light",
classes="pa-0 ma-0",
style="max-width: 30px",
)
with vuetify4.VBtn(icon=True, click=busy):
vuetify4.VIcon("mdi-sleep")
with vuetify4.VMenu(location="bottom"):
with vuetify4.Template(v_slot_activator="{ props }"):
with vuetify4.VBtn(icon=True, v_bind="props"):
vuetify4.VIcon("mdi-dots-vertical")
with vuetify4.VList():
with vuetify4.VListItem(
v_for="(item, i) in menu_items",
key="i",
value=["item"],
):
vuetify4.VListItemTitle(
"{{ item }}", click=(print_item, "[item]")
)
with vuetify4.VMain():
html.P("This example shows how to build your own drawer layout")
with vuetify4.VNavigationDrawer(
model_value=("drawer_open",),
width=350,
):
html.Div("Theme: {{ theme }}")
html.Div("Busy: {{ trame__busy }}")


if __name__ == "__main__":
server.start()
44 changes: 44 additions & 0 deletions examples/vuetify4-labs/v_date_range_picker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import trame_server
from trame.app import TrameApp

from trame.ui.vuetify4 import VAppLayout
from trame.widgets import html
from trame.widgets import vuetify4 as v4

v4.enable_lab()


class MonthPickerExample(TrameApp):
def __init__(self, server: trame_server.Server | str | None = None) -> None:
super().__init__(server)
self.state.date_range = []
self.state.independent_months = False

self._build_ui()

def _build_ui(self) -> None:
with VAppLayout(self.server) as self.ui:
with v4.VMain():
with html.Div(classes="d-flex flex-column align-center ga-5 mt-3"):
html.Span(
"Selected range: {{date_range.map(d => d.toISOString().slice(0, 10)).join(' - ')}}"
)
v4.VCheckbox(
v_model=("independent_months",),
label="Independent Months?",
)
v4.VDateRangePicker(
v_model=("date_range",),
independent_months=("independent_months",),
color="primary",
width="100%",
)


def main():
app = MonthPickerExample()
app.server.start()


if __name__ == "__main__":
main()
73 changes: 73 additions & 0 deletions examples/vuetify4-labs/v_heatmap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import trame_server
from trame.app import TrameApp

from trame.ui.vuetify4 import VAppLayout
from trame.widgets import html
from trame.widgets import vuetify4 as v4

v4.enable_lab()


class HeatmapExample(TrameApp):
def __init__(self, server: trame_server.Server | str | None = None) -> None:
super().__init__(server)

self.state.week_rows = ["Mon", "Tue", "Wed", "Thu", "Fri"]
self.state.week_columns = [f"W{i + 1}" for i in range(12)]
self.state.heatmap_items = [
{
"row": row,
"column": column,
"value": (
(col_idx + 3) * 17 + (row_idx + 2) * 23 + col_idx * row_idx * 7
)
% 100,
}
for col_idx, column in enumerate(self.state.week_columns)
for row_idx, row in enumerate(self.state.week_rows)
]
self.state.heatmap_thresholds = [
{"min": "0", "color": "#172033"},
{"min": "20", "color": "#193b4d"},
{"min": "40", "color": "#1d6370"},
{"min": "60", "color": "#20a08c"},
{"min": "80", "color": "#55d6a9"},
]
self.state.heatmap_legend = {
"labels": [
"Quiet",
"Low",
"Normal",
"Busy",
"Hot",
],
}
self.state.cell_size = [24, 24]

self._build_ui()

def _build_ui(self) -> None:
with VAppLayout(self.server) as self.ui:
with v4.VMain():
with html.Div(classes="d-flex flex-column align-center ga-5 mt-3"):
v4.VHeatmap(
classes="w-66",
items=("heatmap_items",),
rows=("week_rows",),
columns=("week_columns",),
thresholds=("heatmap_thresholds",),
cell_size=("cell_size",),
gap=5,
legend=("heatmap_legend",),
rounded="6",
hover=True,
)


def main():
app = HeatmapExample()
app.server.start()


if __name__ == "__main__":
main()
107 changes: 107 additions & 0 deletions examples/vuetify4-labs/v_highlights.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import trame_server
from trame.app import TrameApp
from trame.decorators import change

from trame.ui.vuetify4 import VAppLayout
from trame.widgets import html
from trame.widgets import vuetify4 as v4

v4.enable_lab()


def fuzzy(text: str, query: str) -> bool:
"""Return True if every char of query appears in text in order."""
it = iter(text.lower())
return all(ch in it for ch in query.lower())


class HighlightsExample(TrameApp):
def __init__(self, server: trame_server.Server | str | None = None) -> None:
super().__init__(server)

self.state.terms = []
self.state.matching_profile = ""
self.state.profiles = [
(
"Sarah specializes in Python and Go, building AST-driven linters and code analyzers. "
"She has deep experience refactoring TypeScript monorepos into modular architectures. "
"Her current focus is a Python-based static analysis pipeline that feeds into Go "
"microservices."
),
(
"Marcus works on a Python-to-Rust compilation target, specializing in AST "
"transformations and type inference. He previously built a JavaScript bundler "
"from scratch, optimizing module graph traversal. His side project extends "
"Rust's parser to support embedded Python scripting."
),
(
"Priya builds enterprise dashboards in TypeScript and Java, with heavy use of "
"AST-based code generators for API clients. She maintains a Go service mesh "
"that proxies traffic between Java and TypeScript tiers. Her team's linter "
"pipeline spans TypeScript, Java, and Go codebases."
),
(
"Lena designs CLI tools in Python and TypeScript that emit human-readable AST "
"diagnostics for developers. She previously worked on a Ruby-based code "
"migration framework that relied on AST pattern matching. Her current role "
"bridges Python scripting and TypeScript frontend tooling."
),
(
"Tom writes infrastructure-as-code in Go and Rust, with a focus on AST "
"validation for declarative config formats. He maintains a JavaScript "
"build pipeline that parses source ASTs to inject telemetry. His weekend "
"project is a Rust-based Python interpreter that reuses CPython's AST node "
"definitions."
),
]

self._build_ui()

def _build_ui(self) -> None:
with VAppLayout(self.server) as self.ui:
with v4.VMain():
v4.VCombobox(
v_model="terms",
label="Highlight terms",
chips=True,
closable_chips=True,
hide_details=True,
multiple=True,
classes="mb-6 mt-4",
variant="outlined",
)
v4.VHighlight(
v_if="matching_profile.length > 0",
query=("terms",),
text=("matching_profile",),
)
with html.Div(v_if="matching_profile.length == 0"):
html.P(v_for="profile in profiles", children="{{ profile }}")

@change("terms")
def update_results(self, **_kwargs):
queries = [q.strip() for q in self.state.terms if q and q.strip()]
if not queries:
self.state.matching_profile = ""
return

scored = [
(item, sum(1 for q in queries if fuzzy(item, q)))
for item in self.state.profiles
]

best = sorted(
(s for s in scored if s[1]),
key=lambda s: s[1],
reverse=True,
)
self.state.matching_profile = best[0][0] if best else ""


def main():
app = HighlightsExample()
app.server.start()


if __name__ == "__main__":
main()
Loading
Loading