chore: update example project

This commit is contained in:
Denis Donici 2026-04-10 15:09:57 +03:00
parent af0c4ccf82
commit 8a4714ecaf
12 changed files with 23 additions and 244 deletions

View file

@ -33,7 +33,7 @@ example-*.tar
# In case you use Node.js/npm, you want to ignore these.
npm-debug.log
/assets/node_modules/
node_modules/
# Ignore automatically generated Svelte files by the ~V sigil
/assets/svelte/_build/

View file

@ -1,62 +0,0 @@
// Client-side entry point for both Vite dev server (HMR) and production builds.
// Uses virtual:live-svelte-components (provided by liveSveltePlugin) instead of
// the esbuild-plugin-import-glob glob syntax used by the old esbuild app.js entry.
import "phoenix_html"
import {Socket} from "phoenix"
import {LiveSocket} from "phoenix_live_view"
import topbar from "../vendor/topbar"
import {getHooks} from "live_svelte"
import Components from "virtual:live-svelte-components"
function formatPayload(str) {
if (!str || str === "—") return "—"
try {
return JSON.stringify(JSON.parse(str), null, 2)
} catch {
return str
}
}
const PropsDiffPayloadDisplay = {
mounted() {
this.updateDisplays()
const root = this.el
const diffOnEl = root.querySelector("[data-name='PropsDiffDemo'][data-use-diff='true']")
const diffOffEl = root.querySelector("[data-name='PropsDiffDemo'][data-use-diff='false']")
const observer = new MutationObserver(() => this.updateDisplays())
if (diffOnEl) observer.observe(diffOnEl, {attributes: true, attributeFilter: ["data-props"]})
if (diffOffEl) observer.observe(diffOffEl, {attributes: true, attributeFilter: ["data-props"]})
this._observer = observer
},
updated() {
this.updateDisplays()
},
destroyed() {
this._observer?.disconnect()
},
updateDisplays() {
const root = this.el
const diffOnEl = root.querySelector("[data-name='PropsDiffDemo'][data-use-diff='true']")
const diffOffEl = root.querySelector("[data-name='PropsDiffDemo'][data-use-diff='false']")
const preOn = root.querySelector("#payload-display-diff-on")
const preOff = root.querySelector("#payload-display-diff-off")
if (preOn) preOn.textContent = formatPayload(diffOnEl?.getAttribute("data-props") ?? "—")
if (preOff) preOff.textContent = formatPayload(diffOffEl?.getAttribute("data-props") ?? "—")
},
}
const Hooks = {
...getHooks(Components),
PropsDiffPayloadDisplay,
}
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {hooks: Hooks, params: {_csrf_token: csrfToken}})
topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})
window.addEventListener("phx:page-loading-start", _info => topbar.show(300))
window.addEventListener("phx:page-loading-stop", _info => topbar.hide())
liveSocket.connect()
window.liveSocket = liveSocket

View file

@ -1,8 +0,0 @@
// Vite SSR entry point for liveSveltePlugin's /ssr_render endpoint.
// Uses Vite-native import.meta.glob instead of the esbuild-plugin-import-glob
// syntax used by server.js. Configured via vite.config.js:
// liveSveltePlugin({ entrypoint: './js/server.vite.js' })
import { getRender } from "live_svelte"
import Components from "virtual:live-svelte-components"
export const render = getRender(Components)

View file

@ -1,152 +0,0 @@
/**
* @license MIT
* topbar 2.0.0, 2023-02-04
* https://buunguyen.github.io/topbar
* Copyright (c) 2021 Buu Nguyen
*/
;(function (window, document) {
"use strict"
// https://gist.github.com/paulirish/1579671
;(function () {
var lastTime = 0
var vendors = ["ms", "moz", "webkit", "o"]
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x] + "RequestAnimationFrame"]
window.cancelAnimationFrame = window[vendors[x] + "CancelAnimationFrame"] || window[vendors[x] + "CancelRequestAnimationFrame"]
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function (callback, element) {
var currTime = new Date().getTime()
var timeToCall = Math.max(0, 16 - (currTime - lastTime))
var id = window.setTimeout(function () {
callback(currTime + timeToCall)
}, timeToCall)
lastTime = currTime + timeToCall
return id
}
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function (id) {
clearTimeout(id)
}
})()
var canvas,
currentProgress,
showing,
progressTimerId = null,
fadeTimerId = null,
delayTimerId = null,
addEvent = function (elem, type, handler) {
if (elem.addEventListener) elem.addEventListener(type, handler, false)
else if (elem.attachEvent) elem.attachEvent("on" + type, handler)
else elem["on" + type] = handler
},
options = {
autoRun: true,
barThickness: 3,
barColors: {
0: "rgba(26, 188, 156, .9)",
".25": "rgba(52, 152, 219, .9)",
".50": "rgba(241, 196, 15, .9)",
".75": "rgba(230, 126, 34, .9)",
"1.0": "rgba(211, 84, 0, .9)",
},
shadowBlur: 10,
shadowColor: "rgba(0, 0, 0, .6)",
className: null,
},
repaint = function () {
canvas.width = window.innerWidth
canvas.height = options.barThickness * 5 // need space for shadow
var ctx = canvas.getContext("2d")
ctx.shadowBlur = options.shadowBlur
ctx.shadowColor = options.shadowColor
var lineGradient = ctx.createLinearGradient(0, 0, canvas.width, 0)
for (var stop in options.barColors) lineGradient.addColorStop(stop, options.barColors[stop])
ctx.lineWidth = options.barThickness
ctx.beginPath()
ctx.moveTo(0, options.barThickness / 2)
ctx.lineTo(Math.ceil(currentProgress * canvas.width), options.barThickness / 2)
ctx.strokeStyle = lineGradient
ctx.stroke()
},
createCanvas = function () {
canvas = document.createElement("canvas")
var style = canvas.style
style.position = "fixed"
style.top = style.left = style.right = style.margin = style.padding = 0
style.zIndex = 100001
style.display = "none"
if (options.className) canvas.classList.add(options.className)
document.body.appendChild(canvas)
addEvent(window, "resize", repaint)
},
topbar = {
config: function (opts) {
for (var key in opts) if (options.hasOwnProperty(key)) options[key] = opts[key]
},
show: function (delay) {
if (showing) return
if (delay) {
if (delayTimerId) return
delayTimerId = setTimeout(() => topbar.show(), delay)
} else {
showing = true
if (fadeTimerId !== null) window.cancelAnimationFrame(fadeTimerId)
if (!canvas) createCanvas()
canvas.style.opacity = 1
canvas.style.display = "block"
topbar.progress(0)
if (options.autoRun) {
;(function loop() {
progressTimerId = window.requestAnimationFrame(loop)
topbar.progress("+" + 0.05 * Math.pow(1 - Math.sqrt(currentProgress), 2))
})()
}
}
},
progress: function (to) {
if (typeof to === "undefined") return currentProgress
if (typeof to === "string") {
to = (to.indexOf("+") >= 0 || to.indexOf("-") >= 0 ? currentProgress : 0) + parseFloat(to)
}
currentProgress = to > 1 ? 1 : to
repaint()
return currentProgress
},
hide: function () {
clearTimeout(delayTimerId)
delayTimerId = null
if (!showing) return
showing = false
if (progressTimerId != null) {
window.cancelAnimationFrame(progressTimerId)
progressTimerId = null
}
;(function loop() {
if (topbar.progress("+.1") >= 1) {
canvas.style.opacity -= 0.05
if (canvas.style.opacity <= 0.05) {
canvas.style.display = "none"
fadeTimerId = null
return
}
}
fadeTimerId = window.requestAnimationFrame(loop)
})()
},
}
if (typeof module === "object" && typeof module.exports === "object") {
module.exports = topbar
} else if (typeof define === "function" && define.amd) {
define(function () {
return topbar
})
} else {
this.topbar = topbar
}
}).call(this, window, document)

View file

@ -8,7 +8,7 @@
import Config
config :phoenix_vite, PhoenixVite.Npm,
assets: [args: [], cd: __DIR__],
assets: [args: [], cd: Path.expand("..", __DIR__)],
vite: [
args: ~w(exec -- vite),
cd: Path.expand("../assets", __DIR__),
@ -38,17 +38,6 @@ config :example, ExampleWeb.Endpoint,
# at the `config/runtime.exs`.
config :example, Example.Mailer, adapter: Swoosh.Adapters.Local
# Configure tailwind (the version is required)
config :tailwind,
version: "4.1.10",
default: [
args: ~w(
--input=css/app.css
--output=../priv/static/assets/app.css
),
cd: Path.expand("../assets", __DIR__)
]
# Configures Elixir's Logger
config :logger, :console,
format: "$time $metadata[$level] $message\n",

View file

@ -22,7 +22,6 @@ config :example, ExampleWeb.Endpoint,
secret_key_base: "cZ19cfxyAQ7Nr/qlKBKxr/jRRgW6wk8MQEgJrMNFjfOPEo6hSY1v50sFb0vIjv3P",
static_url: [host: "localhost", port: 5173],
watchers: [
tailwind: {Tailwind, :install_and_run, [:default, ~w(--watch)]},
vite: {PhoenixVite.Npm, :run, [:vite, ~w(dev)]}
]

View file

@ -19,3 +19,8 @@ config :logger, level: :info
# Runtime production configuration, including reading
# of environment variables, is done on config/runtime.exs.
# Production SSR via NodeJS
config :live_svelte,
ssr_module: LiveSvelte.SSR.NodeJS,
ssr: true

View file

@ -27,7 +27,11 @@ config :phoenix_test, :endpoint, ExampleWeb.Endpoint
# Disable LiveSvelte SSR in test so E2E tests run against the client-side bundle.
# Otherwise server-rendered HTML can mask client-only bugs (e.g. hardcoded props).
config :live_svelte, ssr: false
# Explicit ssr_module so NodeJS.Supervisor starts (needed by phoenix_test SSR tests
# that temporarily enable SSR via Application.put_env/3).
config :live_svelte,
ssr: false,
ssr_module: LiveSvelte.SSR.NodeJS
# In test we don't send emails.
config :example, Example.Mailer, adapter: Swoosh.Adapters.Test

View file

@ -7,8 +7,14 @@ defmodule Example.Application do
@impl true
def start(_type, _args) do
children = [
{NodeJS.Supervisor, [path: LiveSvelte.SSR.NodeJS.server_path(), pool_size: 4]},
node_js_children =
if Application.get_env(:live_svelte, :ssr_module, nil) == LiveSvelte.SSR.NodeJS do
[{NodeJS.Supervisor, [path: LiveSvelte.SSR.NodeJS.server_path(), pool_size: 4]}]
else
[]
end
children = node_js_children ++ [
# Start the Telemetry supervisor
ExampleWeb.Telemetry,
# Start the Ecto repository

View file

@ -53,8 +53,7 @@ defmodule Example.MixProject do
{:wallaby, "~> 0.30", runtime: false, only: :test},
{:phoenix_test, "~> 0.9", only: :test},
{:phoenix_live_reload, "~> 1.2", only: :dev},
{:phoenix_vite, "~> 0.4"},
{:tailwind, "~> 0.3", runtime: Mix.env() == :dev}
{:phoenix_vite, "~> 0.4"}
]
end
@ -70,7 +69,7 @@ defmodule Example.MixProject do
"ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
"ecto.reset": ["ecto.drop", "ecto.setup"],
test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"],
"assets.setup": ["phoenix_vite.npm assets install", "tailwind.install --if-missing"],
"assets.setup": ["phoenix_vite.npm assets install"],
"assets.build": [
"phoenix_vite.npm vite build --manifest --emptyOutDir true",
"phoenix_vite.npm vite build --ssrManifest --emptyOutDir false --ssr js/server.js --outDir ../priv/svelte"

View file

@ -43,7 +43,6 @@
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"},
"swoosh": {:hex, :swoosh, "1.21.0", "9f4fa629447774cfc9ad684d8a87a85384e8fce828b6390dd535dfbd43c9ee2a", [:mix], [{:bandit, ">= 1.0.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mua, "~> 0.2.3", [hex: :mua, repo: "hexpm", optional: true]}, {:multipart, "~> 0.4", [hex: :multipart, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:req, "~> 0.5.10 or ~> 0.6 or ~> 1.0", [hex: :req, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9127157bfb33b7e154d0f1ba4e888e14b08ede84e81dedcb318a2f33dbc6db51"},
"tailwind": {:hex, :tailwind, "0.4.1", "e7bcc222fe96a1e55f948e76d13dd84a1a7653fb051d2a167135db3b4b08d3e9", [:mix], [], "hexpm", "6249d4f9819052911120dbdbe9e532e6bd64ea23476056adb7f730aa25c220d1"},
"telemetry": {:hex, :telemetry, "1.4.1", "ab6de178e2b29b58e8256b92b382ea3f590a47152ca3651ea857a6cae05ac423", [:rebar3], [], "hexpm", "2172e05a27531d3d31dd9782841065c50dd5c3c7699d95266b2edd54c2dafa1c"},
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.2", "2caabe9344ec17eafe5403304771c3539f3b6e2f7fb6a6f602558c825d0d0bfb", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b43db0dc33863930b9ef9d27137e78974756f5f198cae18409970ed6fa5b561"},
"telemetry_poller": {:hex, :telemetry_poller, "1.3.0", "d5c46420126b5ac2d72bc6580fb4f537d35e851cc0f8dbd571acf6d6e10f5ec7", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "51f18bed7128544a50f75897db9974436ea9bfba560420b646af27a9a9b35211"},

View file

@ -18,7 +18,7 @@
"isDynamicEntry": true
},
"css/app.css": {
"file": "assets/app-CLO35oKD.css",
"file": "assets/app-DAL7h2-Q.css",
"name": "app",
"names": [
"app.css"