33 Commits
Author SHA1 Message Date
thepraandClaude Opus 5.5 dc2190e271 Administration backs the server up, downloads, uploads and restores it
A Backups section (for administrators, PrivaPub's /clientapi/admin/backups): the backups kept with their kind, size,
media and the logins deleted since, what runs, the restore waiting and the last restore's report. Back up now; download
for the password through a link good for ten minutes, which the browser saves to disk; upload a downloaded backup in
pieces straight from the picked file (js/transfer.js PUTs file.slice at the offset PrivaPub has, never through .NET's
memory, and carries on after a refused or lost piece); delete after asking; restore for the password and the host typed
out, saying what it keeps and what it loses, then showing PrivaPub restarting until the ended session sends the page to
sign in.

e2e: a backup made, downloaded and uploaded again; and, only with --filter "Category=Restore", a real restore of the
pasture, which signs the town in again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-07 12:45:30 +02:00
thepraandClaude Opus 5.5 70cb389537 Media survive failures: uploaded when attached, the server's limits, alt text asked for
The composer uploads each file as soon as it is attached (streamed from the browser file, under its own ten-minute
timeout), shows it uploading, processing (a 202 followed until ready), ready or failed with Retry, and cancels one
removed; publishing sends the ids, so a failed publish or a retried upload sends nothing twice. Size limits and types
come from /api/v2/instance, a file without a browser type gets one from its extension, and each refusal names the file
and why. Previews are 320 px thumbnails, not whole files in memory. A picture without a description is asked about once
("Publish anyway"; a setting turns it off), descriptions are sent with PUT, and an edit keeps the post's media, language
and sensitive flag.

Profile pictures are checked against the server's types and can be removed. A card shows "Media unavailable" with the
original's link when a picture fails, the blurhash behind a sensitive placeholder, and plays a GIF only on tap under
reduced motion. A failed export says so instead of downloading part of a file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-07 11:56:59 +02:00
thepraandClaude Opus 5.5 e94e32d198 The globe's nerd stats are instruments in the scene
Owner decision 2026-10-06: the globe tells its numbers where they belong instead of in a table. instruments.js adds
one three.js group to globe.gl's scene and a DOM overlay of chips:
- the Sun: a ray and a bead over the subsolar point (with the irradiance), the day/night line, the golden hour and
  the three twilights, the declination, the noon and midnight meridian, and the analemma at this hour, its stretch to
  the next equinox or solstice brighter;
- a focused post's place: its horizon, its Sun's line and height, and its day lit on its latitude, with one chip for
  the place, the phase, the solar time, the elevation and the day's length;
- the tropics and polar circles (the axial tilt), the CDN ring, the view's centre and spread, and rings around the
  posts in view;
- a small orrery in the top left corner, re-anchored to the camera before each render, with the Earth–Sun distance,
  the seasons, the next equinox or solstice and the next perihelion or aphelion stacked under it;
- the frame rate as a ring around the globe; the rest (points, textures, countries, renderer) as a legend.

The chips are placed after each frame globe.gl draws, clear of each other, the moment's caption, the orrery and the
legend, and slide back inside the canvas at its edge; country names keep clear of them. Geometry changes only inside
a wake globe.js already made, so an idle globe still draws nothing. three.js's classes come from globe.gl's own
objects, looked for before each render until its globe is in the scene. A phone's banner draws the day/night line
and the subsolar point; full screen draws everything, and a globe built in the banner now moves its camera back when
opened full screen.

GlobePanel loses both tooltips and the one-second poll that read every post into a table; the Sun's rows stay as
screen-reader text, and a failed start says so. Seven keys only the tooltips used are gone.

GlobeTests (desktop with a located post written from Milano and pinned, in both themes and turned to the subsolar
point; a phone's banner, full screen and back; an idle globe drawing no frame) pass. Under SwiftShader the instruments
cost 1–3 ms of a ~50 ms frame and under 0.1 ms of script; FeedPerfTests' heaps are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-07 02:15:06 +02:00
thepraandClaude Opus 5.5 18331531b8 The Sun's year is a pure function too, checked against the almanac
sun.js gains analemma(date, days): where the Sun is overhead at the same UTC time on each coming day, the figure-8 the
globe will draw. tools/globe/check.mjs checks the pure functions against almanac values with Node's own test runner
(dev only, nothing installed): the 2026 June solstice's declination, the March equinox, perihelion's distance, Turin's
longest day, the season in October, and the analemma spanning the tropics and the equation of time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-06 23:51:35 +02:00
thepraandClaude Opus 5.5 f1ac5eaad2 Back closes what is open
On a phone the Back button left the page under an open menu, sheet or dialog. An open overlay now says so (new
IDOverlayHost, cascaded by MainLayout; OverlayService keeps them newest last): DModal and DDropdown through the
cascade, so the D components stay free of app services, and the nav menu, PhoneSheet and the full-screen globe through
the service. MainLayout's location-changing handler lets Back close the newest overlay instead of leaving the page; a
link followed or the app's own navigation (signing out after its question, say) closes them all and goes on. Blazor
can't tell Back from the app's navigation, so a capture-phase popstate listener in main.js marks Back
(AppStatusService.TookBack). No history entries are pushed. Phone flow: Back closes the menu, a card's More sheet, the
composer and the full-screen globe one after another without leaving the feed, and with nothing open goes back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-06 23:49:49 +02:00
thepraandClaude Opus 5.5 7b1bfa71c0 On touch, a tap on a post opens its thread
On a phone the only way into a post's thread was a small Expand button (and now an entry in More), while a tap on the
post pinned it to a globe that is usually hidden there, outlining the card. Where the pointer can't hover, a tap on a
post that isn't on its links, controls or menus and doesn't end a text selection opens its thread through the card's
own thread link (main.js, data-thread on the card); cards are pinned to the globe with a mouse only. The globe's corner
stats have 12px text on touch. Phone flow: a tap in a post's margin opens its thread and pins nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-06 23:39:10 +02:00
thepraandClaude Opus 5.5 89cef25def Nerd stats open with a tap
On a touch screen a nerd-stats tooltip showed only through the browser faking hover, unreliably, and couldn't be
scrolled. Where the pointer can't hover (AppStatusService.CanHover), a tap on a NerdTip now opens its rows in a sheet
from the bottom; the trigger is focusable, says it opens a dialog, and is at least 44px on touch. A closed tooltip takes
no room any more (daisyUI only made it transparent, and one past a card's edge made a phone's feed scroll sideways),
and on touch it never shows. The rows are a component of their own (NerdRows), at 12px on phones. A card's More menu
has a Details entry on phones that gathers the post's, its author's, its place's or CDN's and its counts' rows in one
sheet. A picture's tip doesn't open on a tap, the picture does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-06 23:36:03 +02:00
thepraandClaude Opus 5.5 dd38002682 Feed tabs say what they are
Below xl every feed tab but the selected one was a lone icon with no name, for a screen reader or a finger. DTab takes
an AccessibleName, which DTabs renders as the tab's label and tooltip, and FeedTabs names each tab; on a phone every
tab shows its name too. The strip is marked as one that scrolls sideways (data-scroll-x), says it holds the feeds, and
on a phone fades at the edge where more tabs wait (edge-fade-x, a scroll-driven animation where the browser has one);
the selected tab is brought into view when it changes (window.revealActive). Explore's tabs get the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-06 23:31:07 +02:00
thepraandClaude Opus 5.5 931b7fcff1 The globe opens full screen; a phone held sideways keeps its feed
On a phone the globe is a banner above the feed; a tap on it now opens it full screen over everything, with a close
button (Escape closes it too), the focus moving to that button and back, and the feed underneath kept as it was but out
of reach (inert). The camera refits the new shape. Held sideways, a phone (a window at most 30rem tall, the new short:
variant) has no room for the banner: the banner and its toggle give way to a button that opens the globe full screen.
isMobileMedia, which picks the first visit's default, agrees with Tailwind's md now (it was off by one) and counts a
phone held sideways. GlobePanel tells its state (data-globe-state). Phone flows: the globe opens full screen and
closes, and a sideways phone keeps a feed at least 180px tall with no sideways scroll.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-06 23:23:14 +02:00
thepraandClaude Opus 5.5 38336f9869 The page zooms, keeps its height on phones and isn't reloaded by a pull
The viewport no longer forbids zooming (maximum-scale=1, user-scalable=no kept text at 10-12px out of reach), and it
covers the screen with the keyboard resizing the content. The app is 100dvh tall instead of 100vh, so its bottom isn't
under a phone browser's bar, and a pull past the top no longer reloads it (overscroll-behavior-y). New Styles/touch.css
holds what a touch screen gets, under (pointer: coarse) only: for now field text at 16px, so iOS doesn't zoom into a
focused field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-06 22:40:28 +02:00
thepraandClaude Opus 5.5 23dbc68fb4 Coming back to the feed shows it as it was left
Leaving the feed keeps it in Services/FeedMemory.cs (posts, waiting posts, cursors and scroll; ten minutes, for the same
feed of the same persona), so coming back from a thread or a profile shows it again at once, older pages included,
instead of reading it anew; newer posts come behind the "new" button and through the stream as before.
tests/e2e FeedMemoryTests goes to a thread and back without a new read of the home timeline; the whole suite passes
(123, 5 skipped).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-06 01:21:41 +02:00
thepraandClaude Opus 5.5 2024798732 Custom emoji, downvotes, blurhash and focal points on the cards
What other platforms send now shows as they meant it:
- custom emoji (a post's or account's :shortcode:) as their pictures in
  posts, names, content warnings, bios and fields: only the shortcodes the
  server listed, only http(s) pictures, never inside a tag, the shortcode
  kept as alt text;
- a threadiverse post's downvotes next to its favourites;
- blurhash placeholders until a picture loads (wwwroot/js/blurhash.js, a
  small decoder that watches the page), and a picture's focal point;
- an event's online link, participants and capacity, and whether it is
  tentative or cancelled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-05 17:55:39 +02:00
thepraandClaude Opus 5.5 011bafb6af End-to-end tests against the pasture; profiles, follows, search, votes, reactions
tests/e2e (Playwright for .NET 1.61.0 on xunit v3, the Chromium 1228 already
cached, never downloaded, never in CI) runs the published app against
SocialPub's pasture and its town: run.sh publishes it with the Pasture
environment (wwwroot/appsettings.Pasture.json, published only then; the
deploy fails if it ever ships) and serves it as https://decepub.test. The
tests sign in through the real login page and persona chooser, check that
every request goes only to decepub.test and privapub.test with the right
credential on the right API, render one post of each kind from each
platform as its author wrote it, and check every action on the other
side of the wire (town.sh stored): favourites, boosts and replies with
their undoing, follows from a profile, votes, reactions. Each test is also
a cell of the town's report (out/client.jsonl). They found two PrivaPub
bugs fixed there (a blocked author's status answered null; sensitive
remote media invented a content warning) and a reply addressed to nobody.

New in the app, all checked by those tests:
- profiles at /account/{id}: header, bio, fields, counts, the account's
  posts as posts, posts and replies, or media, and follow, unfollow,
  cancel a request, mute and block (AccountService); post authors and
  notifications open them in the app;
- search at /search: accounts, posts and hashtags, a handle or an address
  resolved by PrivaPub;
- poll voting on the card, and emoji reactions shown and given with one
  tap; before a persona's first reaction or vote it is told once that
  both are public (PublicNoticeService, the owner's decision);
- follow requests answered from their notification.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-05 01:23:18 +02:00
thepraandClaude Opus 5.5 0f68e74e38 The globe under the Sun of now or of a post, with borders and country names
Build / Build (push) Successful in 46s
Deploy / decepub.thepra.dev (push) Successful in 1m52s
The Sun lights the globe where it really is (wwwroot/js/sun.js, the Astronomical Almanac's low-precision solar
coordinates): the day map shaded by the Sun's height and the Earth–Sun distance, warm city lights on the night side,
and a twilight blend about 13° wide between them. It shows now, refreshed every 30 s, or the moment of the hovered,
focused or pinned post: a click on a card pins it, and the subsolar point travels there the shorter way round. The
panel's top corner shows that moment, the phase of the day and solar time at the post's place, and the season in its
hemisphere (or in both, for a post without a place). Its tooltip shows the subsolar point, declination, axial tilt,
distance, irradiance, day length and the next equinox or solstice.

Land borders are thin paths. Country names are in the reader's language and appear as the camera comes closer:
largest countries first, away from the globe's edge, never overlapping. They come from world-atlas 2.0.2 (Natural
Earth 1:50m), prepared by tools/globe/prepare-countries.py, and are vendored with their hashes in SOURCES.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-04 20:23:42 +02:00
thepraandClaude Opus 5.5 69d0a641fe The globe draws the poles as they are, and nothing while idle
The globe is a UV sphere: towards the poles its triangles narrow to
slivers, and texture coordinates interpolated across them pinched the
map into a star around each pole. Each pixel now computes its own
coordinates from its exact point on the sphere, with seam-safe mipmap
gradients; the map wraps horizontally and gets 8x anisotropic
filtering.

That showed a flaw in the day texture: NASA's mosaic has a lighter
strip at its east edge near the poles, a line from each pole once drawn
exactly. The day texture is now built from NASA's original Blue Marble
(December 2004) by tools/globe/prepare-day-texture.py, which blends
that strip into the map where both sides of the seam agree; same
4096x2048 size, provenance and hashes in SOURCES.md.

globe.gl redrew 60 times a second even when nothing moved; it now draws
only during camera flights, hover pulses, auto-rotation and the
reader's drags, so an idle globe costs no GPU time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-04 17:46:44 +02:00
thepraandClaude Opus 5.5 620cbef219 CDN-fronted servers grouped per CDN, and servers through time
Build / Build (push) Successful in 26s
Deploy / decepub.thepra.dev (push) Successful in 1m21s
A post whose server is behind a CDN has no place of its own: the globe
groups such posts per CDN in labels on a ring above the Arctic, and
hovering one draws its arc to the CDN's group and a ring where the
server was before the CDN. The card's CDN chip names the CDN, its
domain, how PrivaPub found it (published ranges, response headers or
network), the place before the CDN and the CDN's servers week by week
(/api/privapub/v1/cdns/:domain); the author's tooltip lists the server's
changes of place and CDN week by week (/instances/:host/history). Both
are read on the first hover and kept for the session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-04 11:44:51 +02:00
thepraandClaude Opus 5.5 827ecb770e Wire the client to PrivaPub: one sign-in, real feeds, a globe, nerd stats
One sign-in for both of PrivaPub's APIs: the root logs in on /clientapi,
picks a persona, and each persona gets its own Mastodon token in
exchange for the JWT (PersonaExchange). HttpService picks credentials
and JSON by route (JWT and camelCase for /clientapi, the persona token
and snake_case for /api, nothing for /oauth), retries a refused persona
token once, and records every request for the nerd stats.

The feed page is three columns: navigation with a persona switcher, the
feed with a tab per kind (home, local, federated, nearby, communities
and circles, direct, notifications, favourites, bookmarks, hashtags),
and a 3D globe that looks at the spherical mean of the posts in view
and draws a pulse and an arc to a hovered one. Posts sit at their own
place, their event's venue, the reader (located posts) or their
author's server as PrivaPub publishes it. Cards favourite, boost,
bookmark, reply, mute, block and delete; the composer posts with a
content warning, visibility, media and alt text, or as a located post.
Older pages load as the end nears; newer ones are polled while visible.
Nerd stats in tooltips: provenance, author and server, place, media,
counts, paging, the globe's camera and frame rate.

The mock (Faker, MessagesService, the Message models and store) and the
OIDC template leftovers are gone. Strings in English and Italian.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-04 11:03:14 +02:00
thepraandClaude Opus 5.5 f41ebf2160 Vendor globe.gl and its Earth textures
globe.gl 2.46.2 (MIT, three.js r185 bundled) and the NASA Blue Marble
and Black Marble textures from three-globe 2.45.3, copied once from the
npm registry; SOURCES.md records versions, licences and sha256, and the
bundled libraries' notices are in LICENSES-bundled.txt. The service
worker leaves the folder out of the offline precache: the feed loads it
the first time it shows the globe.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-04 11:03:14 +02:00
thepraandClaude Opus 5.5 483ed271e0 Serve the styleguide's images and links locally
The styleguide loaded 42 images from picsum.photos and linked demo items to
daisyui.com. It now uses six small SVG landscapes in wwwroot/imgs/styleguide/
(the diff demo greys one with the grayscale utility), and its demo links stay on
/styleguide.

It also gains a carousel demo with navigation and indicators.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-03 12:43:07 +02:00
thepraandClaude Opus 5.5 81b92e012b Make every theme setting reachable and the theme controls effective
With the default settings (follow the system, light grey) the nav's hue slider
and dark toggle did nothing visible, and no UI could leave that mode. The slider
also always wrote the light hue, even in dark mode.

PageSettings gains SetDarkMode, SetGray and SetHue, and CascadingState gains
UpdatePageSettings. They keep the flags consistent:
- choosing light or dark leaves system theming;
- picking a hue leaves grey;
- grey follows the variant shown.
theme.js reports which variant is showing (AppStatusService.IsDarkTheme), so the
nav toggle and slider edit that one, including the OS's choice in system mode.

Settings > General, which was empty, now edits every theme setting:
- follow the system;
- dark mode;
- grey;
- the light and dark hues.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-03 12:43:07 +02:00
thepraandClaude Opus 5.5 e075c4040b Delete the SCSS/Bulma pipeline and document the new one
Nothing loads the old bundle any more, so it goes, along with everything that
built it and the csproj entries that pointed at it:
- SCSS/, including the vendored Bulma 0.9.4 sass;
- compilerconfig.json and bundleconfig.json;
- tailwind.config.js and terminal-script.txt;
- wwwroot/css/style.min.css, main.css, main.min.css and tailwind-override.css;
- wwwroot/vendor's bulma, tailwind and toggle-dark-light-mode CSS.
The unused Font Awesome and Open Iconic assets stay.

The deploy workflow now requires css/app.css and its .gz in the publish output.
The .gz only exists when the generated file was registered as a static web
asset, so the check catches a silent regression of that. It also checks for
js/theme.js.

CLAUDE.md, new on this branch, documents the repo:
- the no-Node Tailwind/daisyUI build;
- the theme runtime;
- the neo layer, including the cascade-layer rule its overrides must follow;
- the rules for the D components.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-03 12:08:52 +02:00
thepraandClaude Opus 5.5 90e4b9200c Add D-prefixed Blazor wrappers for every daisyUI 5 component
Components/Daisy/ wraps all 69 components in daisyUI's llms.txt, grouped by
daisyUI's categories (Actions, DataDisplay, Navigation, Feedback, DataInput,
Layout, Mockup), with part components where daisyUI has repeating parts (DTab,
DMenuItem, DTimelineItem, ...).
- Each enum maps to whole literal class names in a switch, so Tailwind's scanner
  generates them. Nothing is concatenated.
- Value-holding fields derive from DInputBase<TValue>, Blazor's InputBase, so
  @bind-Value and EditForm validation work. Invalid fields get the component's
  *-error class.
- Open and active state lives in C# (@bind-IsOpen, @bind-Active) and is rendered
  with daisyUI's *-open modifiers. DDropdown adds an outside-click layer and
  Escape. No component uses JS interop.
- The one exception is DCalendar, which wraps the Cally web component, vendored
  as wwwroot/vendor/cally.js (0.9.2, MIT).

/styleguide (not linked) renders every component in all its colours, sizes and
states, with a theme and hue picker that previews without saving.

neo.css follows what the styleguide showed:
- select keeps daisyUI's arrow;
- skeleton-text isn't sunk;
- the phone mockup and dock are raised;
- active menu items and OTP digits are inset;
- a joined accordion is raised as one block.
The primary, secondary and accent colours are deeper, so they still read as
text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-03 12:01:42 +02:00
thepraandClaude Opus 5.5 8e03d12a3e Apply the theme from PageSettings in JS, before the first paint
wwwroot/js/theme.js now picks the theme. It sets data-theme (neo-light or
neo-dark, or none when the OS decides) and the hue and chroma variables on
<html>, using the same flag precedence as the old CascadingState branches. It
also keeps <meta name="theme-color"> in sync. Because it runs in <head> from the
settings saved in localStorage, a reload no longer flashes the default palette
before Blazor boots. With system theming it also follows the OS when the colour
scheme changes.

CascadingState loses its ~200 lines of generated <style> and <Meta> tags. It now
calls AppStatusService.ApplyTheme on load and whenever the settings change. The
--fa-* icon colour variables are gone; nothing read them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-03 11:29:31 +02:00
thepraandClaude Opus 5.5 3551da1b02 Build CSS with Tailwind 4.3.3 standalone and daisyUI 5.7.47
tools/tailwind.sh downloads the pinned Tailwind binary and daisyUI's .mjs
plugins into the gitignored .tools/tailwind/, checking each against a pinned
sha256, so neither developers nor CI need Node. MSBuild compiles
Styles/app.css into wwwroot/css/app.css (also gitignored) on every build,
skipping the step when no source changed. The output is added to Content before
ResolveProjectStaticWebAssets, so a clean-clone publish still gets its .gz/.br
copies and a service-worker-assets.js entry.

index.html now loads css/app.css, plus ionicons as its own stylesheet. The old
bundle stays in the tree until the markup is migrated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-03 11:24:51 +02:00
thepraandClaude Opus 5.5 b9dc865053 net10, pointed at PrivaPub, and deployed as a static site to decepub.thepra.dev
Build / Build (push) Successful in 18s
Deploy / decepub.thepra.dev (push) Successful in 1m11s
The project referenced ../SocialPub/SocialPub/SocialPub.ClientModels, a path
that has not existed since the server became PrivaPub; it references
../SocialPub/PrivaPub.ClientModels now, and CI clones SocialPub beside it.

Retargeted to net10.0 with the Microsoft packages at 10.0.9.
BlazorDownloadFileFast moves to 1.0.0.1 because 0.2.0 used
IJSUnmarshalledRuntime, which .NET 9 removed. The Visual Studio bundler and
web compiler packages are gone; the bundle they produced, css/style.min.css,
is committed and stays the stylesheet, and its stale .gz is deleted because
.NET's static-asset compression writes that path itself.

AppConfiguration.ApiBaseAddress (https://privapub.thepra.dev) is the API
client's base address; the template's placeholder OIDC section and the
Toolbelt.Blazor.HotKeys script tag, whose package is not referenced, are
removed. The vhost sends noindex while the feed shows the prototype's mock
posts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012CzABvBkbcFqoHdmi8b9WB
2026-10-01 08:09:45 +02:00
thepra cc1805e1d4 up 2022-12-16 00:55:43 +01:00
thepra 9ef06db411 up 2022-12-15 20:03:40 +01:00
thepra f622fbcf0a up 2022-12-02 10:19:57 +01:00
Eugenio Chiodo 89f9e1578c Save 2022-02-15 16:15:17 +01:00
Eugenio Chiodo e64bce3171 Save 2022-02-15 00:08:41 +01:00
Eugenio Chiodo 5cba76554a Save 2022-02-14 01:51:52 +01:00
Eugenio Chiodo ba4a4b14c8 Saving 2022-02-14 01:51:37 +01:00
Eugenio Chiodo f5f006e017 Add project files. 2022-02-02 20:26:14 +01:00