Author SHA1 Message Date
Thore Cimbal fa5dcc5fbc docs(fork): the import is not the v1.12.17 tag, and that changes the graft plan
Adding upstream as a second remote was meant to make updates viewable as a diff. The
first measurement contradicted what this document has claimed since August: diffing
v1.12.17 against the import commit yields 783 files, 115 of which exist only on our
side and 62 only in the tag.

The telling ones are MVVM files - TextualBodyViewModel.tsx and RovingTabIndex.tsx -
absent from the v1.12.17 tag, present in v1.12.26 and present in our import. The
changelog stops at 1.12.17 because it is only written at release time. So the base is
a develop state after the 1.12.17 release, not the tag.

That matters for the plan rather than for the code: grafting onto v1.12.17 would build
every future merge on an invented ancestry. Finding the real ancestor means searching
develop history, which a shallow tag fetch cannot do.

The remote is local configuration, so the setup is written down here instead of
committed.
2026-08-19 12:00:00 +00:00
Thore Cimbal e1e9a19f78 config(desktop): disable_custom_urls here too, not only in the web deployment
Decision sorb. The desktop client loads this config.json rather than the one the web
deployment serves, so setting it in element-values.yaml alone would have hardened the
web client and left the builds that actually get handed out untouched - the same trap
as the themes rollout.

It hides the edit button next to the server name, so the homeserver cannot be changed
through the interface, and it makes the 401/403 login error name the server instead of
staying generic.

Stated in the comment rather than discovered later: this is a surface restriction, not
a technical one. MatrixChat still accepts hs_url from the query string in the mobile
registration and registration-confirmation paths without consulting the setting. It
narrows the way to GHSA-wrcp-5v3v-3j6v, which affects everything below 1.12.22 while
we run 1.12.17, and it does not replace the upstream update that management #0099 is
actually about.

Takes effect with the next desktop build.
2026-08-19 12:00:00 +00:00
Thore CimbalandClaude Opus 5 240021b754 ci: build when .npmrc changes
The previous commit added no pipeline run: .npmrc is not in the changes list that
triggers one. That file decides which registry dependencies come from, so a change
to it deserves a build rather than a wave-through. This commit touches
.gitlab-ci.yml, which is on the list - so it verifies itself.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 12:00:00 +00:00
Thore CimbalandClaude Opus 5 be323edf10 build: pin the @sorb scope to rohana instead of relying on the lockfile
Nothing in this repo said where @sorb/threadnet-call-embedded comes from. It
worked only because pnpm-lock.yaml carries the full tarball URL and CI installs
with --frozen-lockfile; the first version bump drops that cover. Today that ends
in a 404 because the name is still free on npmjs - which makes our only
protection the fact that nobody has claimed @sorb there. Claim it and the same
command resolves silently against a stranger's package, in the exact moment a
fresh download looks normal.

The checked-in .npmrc closes the wrong path instead of documenting it. It holds a
URL and no secret: read access to the registry is anonymous.

Upstream's .gitignore ignores /.npmrc - reasonable where the file holds tokens,
and it would have swallowed this fix without a word. The negation is explicit and
carries the reason, so the next merge does not quietly restore the hole.

Verified in an isolated tree, not assumed: without the file pnpm resolves to
npmjs and fails; with it the scope resolves to rohana at the same integrity hash
the repo lockfile already carries; with rohana unreachable the install fails
outright rather than falling back to npmjs. pnpm install --frozen-lockfile still
passes and leaves the lockfile untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 12:00:00 +00:00
Thore CimbalandClaude Opus 4.8 6cdb1858a2 build(web): pull in threadnet-call-embedded 0.19.2-threadnet.12
Sender verification for the AI filter: after attaching, the fork now checks
that the RTCRtpSender actually carries the processed track and enforces the
swap if LiveKit skipped it silently - the Safari field finding where level
0-100 made no audible difference. The log line states the outcome.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-17 12:00:00 +00:00
Thore CimbalandClaude Opus 4.8 7e6b08774b build(web): pull in threadnet-call-embedded 0.19.2-threadnet.11
Opens the AI noise suppression gate after the passed two-person acceptance:
checkbox and slider return to the in-call audio settings, clients with the
setting enabled get the filter again. The rollback lever for any regression is
the gate in threadnet-call, not a deployment revert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-17 12:00:00 +00:00
Thore CimbalandClaude Opus 4.8 51b6691caa build(web): pull in threadnet-call-embedded 0.19.2-threadnet.10
Way B from #0054: the AI filter attaches to the local microphone track after
publication, with its own AudioContext on just that track - no webAudioMix, no
processor key in the capture defaults in any state. The feature gate stays
closed; a single test client opts in via two localStorage keys. For everyone
else this build behaves identically to .9.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 12:00:00 +00:00
Thore CimbalandClaude Opus 4.8 45dc53155b build(web): pull in threadnet-call-embedded 0.19.2-threadnet.9
The .8 widget broke unmuting in production (both with the AI filter on and off);
threadnet-call dcc8643 restores the upstream off-path and hard-gates the feature
until the webAudioMix decision. Verified locally end to end: the installed
package carries the model assets, webpack lands them under
webapp/widgets/element-call/assets/dfn3, and the widget index.html references
the .9 bundle (0.19.2-threadnet.9+dcc86431).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 12:00:00 +00:00
Thore CimbalandClaude Opus 4.8 7abe9023f9 build(web): pull in threadnet-call-embedded 0.19.2-threadnet.8
Brings the AI noise suppression (ADR-0018) into the widget we actually ship.
The .7 package built and ran fine but carried no model assets, because the
embedded Vite config had publicDir off — the filter would have been dead only
inside the widget, and nowhere else.

Verified the whole chain locally rather than trusting the green build: the
23 MB of model and wasm arrive under webapp/widgets/element-call/assets/dfn3,
which is the path the widget requests at runtime.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 12:00:00 +00:00
Thore CimbalandClaude Opus 4.8 e7b00288ed docs: add group-rules pointer (CLAUDE.md) and repo-specific AGENTS.md
Field test F-011 found four of five components carried no pointer file, so a
session landing here had no path to the group rules at all. CLAUDE.md is the
one-line pointer the check looks for; AGENTS.md links the canonical rules in the
management repo (with the Gitea mirror URL for readers outside the lab) and
otherwise carries only what is specific and easy to get wrong here.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-15 12:00:00 +00:00
Thore Cimbal d3bd5b20c2 feat(desktop): give the desktop client a way to obtain its logs
The desktop variant had no bug_report_endpoint_url at all, so the whole section stayed hidden and a desktop user could not produce logs for a report. Setting local shows a Download logs button that writes them to the device and uploads nothing.

Matches what the web client now serves. Takes effect with the next desktop build.

Refs axion1337.chat/ThreadNet-Web#9
2026-08-09 12:00:00 +00:00
Thore Cimbal 3cb43f5149 ci: refuse to overwrite an already published release image
Moving a tag retriggers docker_web, which would rebuild a published release from old source against today's base images and dependencies and push it over the existing one. On 2026-08-09 that nearly happened to v0.4.0 while the rewritten tags were force-pushed; it was averted only because the protected registry variables were unavailable in that window, so docker login failed first.

The job now aborts if the target tag already exists in the registry. latest-ci stays overwritable on purpose - it names the newest state, not a version, and carries no promise.

Closes axion1337.chat/ThreadNet-Web#14
2026-08-09 12:00:00 +00:00
Thore Cimbal 26b6fd8938 ci: do not create a pipeline for docs-only commits
A commit touching only docs/ produced a pipeline with zero jobs, which GitLab marks as failed - pipelines 203 and 204 on 2026-08-06 were both red for nothing. Cause is the needs chain: web is skipped by its changes rule, and desktop_linux/desktop_windows depend on it, which collapses the whole pipeline including the manual jobs.

This matters beyond cosmetics: gitops/CLAUDE.md makes the red pipeline the alarm for the TURN rotation, stating there is no separate reminder. Red that means nothing trains people to stop looking.

The path list is a YAML anchor shared with the web job - two copies would drift. CI_PIPELINE_SOURCE == web stays allowed so the manual maintenance jobs remain reachable via Run pipeline.
2026-08-07 12:00:00 +00:00
Thore Cimbal 965c456dee Doku: Merge-Risiko praeziser fassen - Art des Eingriffs, nicht Umfang
Meine Aussage "ClamAV kostet mehr als das gesamte Branding zusammen" war so nicht haltbar: gemessen sind es ~130 zu ~104 Zeilen, der groesste Einzelpatch ist sogar Branding (HelpUserSettingsTab.tsx, 71 Zeilen).

Der Unterschied liegt woanders: Branding-Patches stehen am Rand und sind nach einem Umbau wiederfindbar, ClamAV-Patches stehen mitten in Entschluesselungs- und Fehlerpfaden. Dazu der eigentliche Haken - verschiebt Upstream die viewmodels/-Dateien, entsteht kein Konflikt, die Zeilen sind schlicht weg und Git meldet nichts. Deshalb ist dort ein Funktionstest maszgeblich, nicht die Merge-Ausgabe.

Refs axion1337.chat/ThreadNet-Web#7
2026-08-06 12:00:00 +00:00
Thore Cimbal 9ac56d671b Doku: Merge-Reibung gemessen (Arbeitspaket 3 aus #7)
Der wichtigste Befund ist die Grundlage, nicht die Liste: das Repo enthaelt KEINE Upstream-Historie. Element Web 1.12.17 kam am 2026-05-10 als kompletter Baum herein, im selben Commit wie das erste eigene Feature. Es gibt also keinen gemeinsamen Vorfahren mit element-hq/element-web - ein git merge ist nicht moeglich, ein Update heisst neu auftragen.

Gemessen statt geschaetzt: 102 geaenderte Dateien, davon nur 12 Upstream-Quellcode. Fuenf davon Branding (flach, isolierbar), sieben ClamAV in der Medien-Pipeline - und genau dort baut Upstream gerade auf MVVM um. Das Update wird an ClamAV mehr kosten als am gesamten Branding.

Refs axion1337.chat/ThreadNet-Web#7
2026-08-06 12:00:00 +00:00
Thore Cimbal f47baa553a Call-Widget auf 0.19.2-threadnet.7 - heisst jetzt aXion1337.Chat
Erste Widget-Version mit VITE_PRODUCT_NAME: Fehlermeldungen und die Versionszeile im Entwicklermodus sagen nicht mehr "Element Call".

pnpm-lock.yaml von Hand nachgezogen (Version an 6 Stellen, integrity aus dem veroeffentlichten Tarball berechnet) - auf diesem Mac gibt es kein Node. Faellt beim CI-Schritt pnpm install --frozen-lockfile auf, falls etwas nicht stimmt.

Die uebersprungene .6 liegt kaputt in der Registry, siehe axion1337.chat/threadnet-call#4.
2026-08-06 12:00:00 +00:00
Thore Cimbal 1d5a136dff Titelbild ausgetauscht: Alpengluehen statt Elements See
alpenglow.jpg (John Towner, Unsplash) ersetzt lake.jpg als Hintergrund der Login-Seite. Die Unsplash License erlaubt kommerzielle Nutzung und Aenderung ohne Genehmigung und verlangt keine Namensnennung - wir nennen ihn trotzdem, das ist die faire Form.

Die Danksagung ist dabei bewusst nicht uebersetzt: der Schluessel credits|default_cover_photo liegt in 32 Sprachdateien, 31 nennen Elements Fotografen namentlich. Nur en/de anzupassen haette in 29 Sprachen eine falsche Attribution stehen lassen, und diese 29 pflegen wir nicht - sie kommen aus Elements Uebersetzungsdienst.

lake.jpg ist entfernt, weil es durch diese Aenderung unerreichbar wird - 610 KB, die sonst in jedem Image mitfahren.
2026-08-06 12:00:00 +00:00
Thore CimbalandClaude Fable 5 580827f7ca desktop: branding-Block, damit der Client nicht Elements Logo zeigt
Die heute im Web gesetzten branding-Werte fehlten der Desktop-Config. Ohne sie
faellt AuthHeaderLogo.tsx auf themes/element/img/logos/element-logo.svg zurueck -
die Anmeldemaske des Desktop-Clients haette also weiter Elements gruenes Logo
gezeigt, waehrend der Web-Client bereits die ThreadNet-Marke traegt.

vector-icons/512.png liegt im webapp-Bundle des Desktops (geprueft), der Pfad
loest also auch dort auf.

Chirurgisch eingefuegt, nicht neu serialisiert - die 17 Themes und der
Gedankenstrich in der Beschreibung bleiben unangetastet.

Danach gilt: Web-Konfiguration ist eine echte Teilmenge der Desktop-Konfiguration,
ohne einen einzigen Wertkonflikt (verglichen ueber alle verschachtelten
Schluessel).

Refs ThreadNet-Web#1

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-08-06 12:00:00 +00:00
Thore CimbalandClaude Fable 5 6f33c7277a desktop: macOS baut ohne volles Xcode (BUILD-01, management#22)
Upstream setzt mac.icon auf build/icon.icon - das Icon-Composer-Bundle von
macOS 26. Dessen Verarbeitung ruft actool auf, das es nur mit dem vollen Xcode
gibt (~10 GB, App-Store-Login). Mit blossen CommandLineTools scheitert damit
JEDER macOS-Build:

  ⨯ Failed to check actool version. Is Xcode 26 or higher installed?

Die bisherige Diagnose in management#22 war falsch: Dort stand, nur das
DMG-Target brauche actool und das ZIP baue problemlos. Tatsaechlich trifft es
alle macOS-Targets, weil mac.icon fuer alle gilt - das ZIP scheitert genauso.
Der dort empfohlene Ausweg (DMG per hdiutil bauen) haette ein Problem geloest,
das gar nicht am DMG lag.

Loesung: mac.icon und dmg.badgeIcon auf das klassische build/icon.icns. Damit
baut electron-builder ZIP UND DMG selbst, inklusive Blockmap - verifiziert am
2026-08-06, beide Artefakte in Release v0.4.1.

Umgesetzt als Variantenoption statt als Aenderung an Upstreams Defaults: Die
Werte stehen in unserem axion1337/build.json, electron-builder.ts wird nur
additiv um zwei optionale Schluessel erweitert - dieselbe Machart wie das
bereits vorhandene linux.deb.name. Ein Upstream-Merge erzeugt an der
mac.icon-Zeile damit keinen Konflikt.

Preis: kein macOS-26-Icon-Rendering. Ohne Xcode gaebe es ohnehin keinen Build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-08-06 12:00:00 +00:00
Thore CimbalandClaude Fable 5 6f1dfedde2 web: Tab-Titel, Favicon, Fehlerseite und Desktop-Hinweis auf ThreadNet
Nachtrag zu v0.4.0. Meine Icon-Arbeit deckte nur vector-icons/ ab; an vier
weiteren Stellen stand weiterhin Element - sichtbar, nicht im Verborgenen.

<title>Element</title> stand statisch in der index.html. Im Browser-Tab stand
also 'Element', unabhaengig vom Icon daneben. Ebenso application-name und
apple-mobile-web-app-title, die beim Ablegen als PWA greifen.

favicon.ico neu: Browser fragen diesen Pfad automatisch ab, unabhaengig von den
<link rel=icon>-Tags. Bisher kam dort ein 404 mit text/html zurueck - derselbe
Fehler, den wir am 2026-08-02 im Docusaurus-Wiki hatten. Sieben Groessen von 16
bis 256, aus derselben zentrierten Quelle wie alle anderen Icons.

Fehlerseite (ErrorView): zeigte Elements element-app-logo.png mit alt='Element'.
Ausgerechnet die Seite, die man sieht, wenn sonst nichts funktioniert.

Desktop-Hinweis (SdkConfig defaults): trug Elements Logo und verlinkte auf
element.io/get-started - also auf fremde Downloads, obwohl wir eigene Builds
ausliefern. Zeigt jetzt auf unsere Releases.

Die drei Bildverweise gehen alle auf vector-icons/512.png statt auf neue Dateien:
webpack kopiert aus res/ nur themes/** und vector-icons/**, und eine zweite
Logo-Datei koennte von den uebrigen abdriften.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-08-06 12:00:00 +00:00
Thore CimbalandClaude Fable 5 c0774b5f58 desktop: Icons zentriert + About-Attribution im Web-Client (ThreadNet-Web#10)
Zwei Haelften desselben Issues, bewusst in einem Commit, weil sie zusammen
gebaut und ausgeliefert werden.

Desktop-Icons: build/icon.png war byte-identisch mit dem alten, unzentrierten
Web-Icon - das Motiv klebte an der Oberkante (Rand oben 3 %, unten 40 %). macOS
und Windows zeigten also dasselbe schiefe Bild wie der Browser-Tab. Alle vier
Artefakte aus der jetzt zentrierten Quelle neu erzeugt: icon.png, icon.ico
(sieben Groessen von 16 bis 256), das Layer-Asset des macOS-Icon-Composers und
icon.icns ueber iconutil (10 Einzelbilder, 16-512 plus @2x). Alle vier gehen auf
dieselbe Datei zurueck, sie koennen also nicht mehr auseinanderlaufen.

About-Attribution: 'ThreadNet - powered by Element' steht jetzt in Einstellungen
-> Hilfe & Info direkt unter der Client-Version, mit Link auf element.io. Das
war der eigentliche Zweck des Issues - bisher stand die Zeile nur in der
Build-Beschreibung des Desktop-Pakets und war im Client nirgends sichtbar.

Zwei bewusste Entscheidungen dabei, beide im Code kommentiert:

- NICHT in getVersionTextToCopy aufgenommen. Der Text dort landet in
  Fehlerberichten; die Herkunft des Forks ist da nur Rauschen.
- Ohne _t(). Ein Markenhinweis wird nicht uebersetzt, und jeder zusaetzliche
  i18n-Schluessel ist Reibung beim naechsten Upstream-Merge - genau das, was das
  Issue mit 'chirurgisch halten' meint.

Das Stylesheet nutzt nur Variablen, die im Projekt bereits verbreitet sind
(--cpd-space-2x in 43, --cpd-color-text-secondary in 28,
--cpd-font-body-sm-regular in 22 Dateien) - kein Blindflug mit erfundenen Tokens.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-08-06 12:00:00 +00:00
Thore CimbalandClaude Fable 5 4fdad89fa6 web: Icons zentriert, theme_color auf die Markenfarbe (ThreadNet-Web#10)
Die Web-Icons trugen bereits die ThreadNet-Marke - anders als im Issue vermerkt.
Sie waren aber falsch zugeschnitten: Rand oben 3 %, unten 40 %, das Motiv klebte
an der Oberkante. In runden und quadratischen Icon-Slots sitzt es dadurch
sichtbar zu hoch.

Alle sieben Groessen aus dem 1024er neu erzeugt: auf die Motiv-Bounding-Box
beschnitten und bei gleicher Groesse (81 % der Kante) mittig gesetzt - jetzt
21 % Rand oben wie unten. resize() statt thumbnail(), sonst waere nur verkleinert
worden.

theme_color stand auf #76CFA6, Elements Mintgruen. Die Marke ist #ed4f4c
(dominante Farbe des Motivs gemessen, nicht geschaetzt) - das Gruen war ein
Upstream-Rest und faerbte die Browser-/PWA-Leiste falsch ein.

config.sample.json: brand von Element auf aXion1337.Chat, damit die Vorlage
zeigt, was Prod tatsaechlich setzt.

Nicht angefasst: der Favicon-Snapshot-Test - er prueft Canvas-Operationen
(clearRect, Dimensionen), nicht den Bildinhalt, und bleibt gruen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-08-06 12:00:00 +00:00
Thore CimbalandClaude Fable 5 80fcf6c227 desktop: Themes mit dem Web-Stand gleichgezogen
Die zehn theme-factory-Themes sind jetzt in beiden Configs identisch (gitops
c47b0a3 + Nachfolger). Ohne das haetten Desktop-Clients weiter meine alte
Interpretation der Paletten gezeigt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-08-02 12:00:00 +00:00
Thore CimbalandClaude Fable 5 f4995e5882 rebrand: auch 'name' auf threadnet-desktop - Linux-Pakete hiessen sonst weiter Element
electron-builder leitet unterschiedliche Dinge aus zwei Feldern ab: productName
bestimmt den App-Namen (macOS .app, Windows Setup.exe), 'name' dagegen den
Linux-Paketnamen, das Binary und den /opt-Pfad. Nach dem Rebrand hiess die App
ueberall ThreadNet - ausser bei .deb und .tar.gz.

Nebenwirkung bewusst in Kauf genommen: apt sieht ein neues Paket, ein
Upgrade-Pfad von element-desktop existiert nicht. Bei einem Rebrand ist das
erwartbar; die appId bleibt unveraendert, damit lokale Daten erhalten bleiben.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-08-02 12:00:00 +00:00
Thore CimbalandClaude Fable 5 e21a895390 ci: VARIANT_PATH in beiden Desktop-Jobs - Pakete hiessen weiter element-desktop
Der Rebrand-Commit legte apps/desktop/axion1337/build.json an, aber electron-builder
nutzt eine Variante nur, wenn VARIANT_PATH gesetzt ist. Lokal hatte ich sie per
Umgebungsvariable gesetzt, in der CI fehlte sie - deshalb trugen die Linux- und
Windows-Pakete aus Pipeline 147 weiter den Namen 'element-desktop' bzw.
'Element Setup', obwohl die Icons bereits neu waren.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-08-02 12:00:00 +00:00
Thore CimbalandClaude Fable 5 c51b68194a rebrand: alle Icons ersetzt - Windows-.ico und Web-Icons fehlten noch
Gestern wurden nur icon.png und icon.icns getauscht; Windows (.ico) und der
Web-Client (res/vector-icons/, manifest.json) trugen weiter das Element-Icon.

Dazu ein echter Fehler behoben: die Icons wurden mit PIL.thumbnail() erzeugt -
das skaliert NUR nach unten. Das 277px-Motiv wurde in 1024er-Icons daher nie
vergroessert und fuellte ueberall bloss 54% x 38% der Flaeche. Jetzt mit resize()
aus dem 1024er-Original: 81% Breite statt 54%.

Ersetzt: web/res/vector-icons/{24,120,144,152,180,512,1024}.png, manifest.json
(name/short_name -> ThreadNet), desktop/build/icon.{png,ico,icns}.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-08-02 12:00:00 +00:00
Thore CimbalandClaude Fable 5 6b0261d66c rebrand: Desktop-Client heisst ThreadNet und traegt die eigene Marke
Erster Schritt aus ThreadNet-Web#10 - der Client hiess bisher 'Element' und
zeigte dessen Icon.

- apps/desktop/axion1337/build.json: eigene Build-Variante (VARIANT_PATH), damit
  element.io/release/build.json unangetastet bleibt und Upstream-Merges nicht
  reiben. productName 'ThreadNet', Beschreibung 'ThreadNet - powered by Element'
  als faire Attribution (Element Web ist AGPL, dies ist ein Fork davon).
- appId bleibt bewusst 'im.riot.app': bestehende Installationen behalten ihre
  lokalen Daten. Ein appId-Wechsel waere ein Datenschnitt und gehoert in die
  Rebranding-Runde, nicht in einen Icon-Commit.
- build/icon.png + icon.icns: ThreadNet-Bildmarke, transparenter Rand entfernt
  (das Motiv fuellte im Original nur 54% x 38% - direkt skaliert verschwindet es).

Verifiziert im Build: CFBundleName/CFBundleDisplayName = ThreadNet, eigenes
Icon, alle 17 Themes im webapp.asar.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-08-02 12:00:00 +00:00
33 changed files with 1002 additions and 658 deletions
+5
View File
@@ -5,6 +5,11 @@
/lib
node_modules
/.npmrc
# ThreadNet-Fork: unsere .npmrc IST versioniert. Sie bindet nur den Scope @sorb an
# die Registry auf rohana und enthaelt kein Geheimnis (Lesezugriff ist anonym).
# Ohne diese Ausnahme bliebe die Datei lokal, und CI wie frischer Klon loesten
# @sorb weiter gegen npmjs auf - siehe management#0055.
!/.npmrc
/*.log
package-lock.json
.DS_Store
+69 -8
View File
@@ -7,6 +7,44 @@
# - webpack braucht ~4 GB Heap -> NODE_OPTIONS
# - Desktop-Build laeuft im dockerbuild-Image (rust:bullseye + node, glibc-2.31-Ziel)
# --- Wann ueberhaupt eine Pipeline entsteht ------------------------------------
# Ohne diesen Block erzeugt ein Commit, der nur docs/ anfasst, eine Pipeline mit
# NULL Jobs - und die zaehlt in GitLab als "failed". Real passiert am 2026-08-06
# (Pipelines 203 und 204): zwei Doku-Commits, zweimal rot, nichts kaputt.
#
# Der Grund liegt an der needs-Kette: web laeuft wegen changes: nicht, und
# desktop_linux/desktop_windows haengen per needs daran - damit faellt die ganze
# Pipeline in sich zusammen, auch die manuellen Jobs ohne needs.
#
# ⚠️ Warum das keine Kosmetik ist: In gitops/CLAUDE.md ist die rote Pipeline die
# ALARMANLAGE fuer die TURN-Rotation ("es gibt keinen separaten Reminder"). Rot,
# das nichts bedeutet, gewoehnt einem das Hinsehen ab.
#
# Preis: Nach einem reinen Doku-Commit entsteht keine Pipeline von selbst. Damit
# die manuellen Wartungsjobs (desktop_image, windows_provision) trotzdem
# erreichbar bleiben, laesst die Regel CI_PIPELINE_SOURCE == "web" durch - eine
# ueber "Run pipeline" in der Oberflaeche gestartete Pipeline entsteht immer.
.pfade_mit_pipeline: &pfade_mit_pipeline
- apps/**/*
- packages/**/*
- patches/**/*
- scripts/**/*
- pnpm-lock.yaml
- pnpm-workspace.yaml
# .npmrc bindet den Scope @sorb an rohana. Aendert sich die Datei, aendert
# sich, WOHER Abhaengigkeiten kommen - das gehoert gebaut, nicht durchgewunken.
- .npmrc
- .gitlab-ci.yml
workflow:
rules:
- if: $CI_COMMIT_TAG =~ /^v/
- if: $CI_PIPELINE_SOURCE == "schedule"
- if: $CI_PIPELINE_SOURCE == "web"
- if: $CI_COMMIT_BRANCH
changes: *pfade_mit_pipeline
- when: never
stages:
- build
- package
@@ -82,14 +120,8 @@ web:
when: never
# Pushes nur, wenn build-relevante Pfade betroffen sind - Doku-Commits kosten nichts
- if: $CI_COMMIT_BRANCH
changes:
- apps/**/*
- packages/**/*
- patches/**/*
- scripts/**/*
- pnpm-lock.yaml
- pnpm-workspace.yaml
- .gitlab-ci.yml
# derselbe Anker wie im workflow-Block oben - zwei Listen wuerden driften
changes: *pfade_mit_pipeline
variables:
NODE_OPTIONS: "--max-old-space-size=6144"
CI_PACKAGE: "true"
@@ -123,8 +155,31 @@ docker_web:
DOCKER_BUILDKIT: "1"
script:
- echo "$REGISTRY_PASSWORD" | docker login rohana.axion1337.de -u "$REGISTRY_USER" --password-stdin
# Ein veroeffentlichtes Release darf sich nicht rueckwirkend aendern.
#
# Am 2026-08-09 hat das Force-Push umgezogener Tags (History-Anonymisierung)
# drei Release-Pipelines neu gestartet - darunter v0.4.0. Ohne diese Sperre
# haette der Lauf das laengst veroeffentlichte Image aus altem Code mit
# heutigen Basis-Images und Abhaengigkeiten neu gebaut und ueberschrieben.
# Dass es damals nicht passierte, lag nur daran, dass die geschuetzten
# Registry-Variablen in dem Moment nicht verfuegbar waren - Glueck, nicht
# Absicht (ThreadNet-Web#14).
#
# Ein Tag ist hier kein Verwaltungseintrag, sondern ein Build-Ausloeser. Wer
# einen Tag verschiebt, loest einen Build aus, ob er will oder nicht. Deshalb
# Unveraenderlichkeit erzwingen statt auf Vorsicht zu hoffen.
- |
if docker manifest inspect "$IMAGE:$CI_COMMIT_TAG" >/dev/null 2>&1; then
echo "FEHLER: $IMAGE:$CI_COMMIT_TAG existiert in der Registry bereits."
echo "Ein veroeffentlichtes Release wird nicht ueberschrieben."
echo "Gewollt? Dann das Image bewusst aus der Registry entfernen oder eine"
echo "neue Version taggen - siehe ThreadNet-Web#14."
exit 1
fi
- docker build -f apps/web/Dockerfile -t "$IMAGE:$CI_COMMIT_TAG" -t "$IMAGE:latest-ci" .
- docker push "$IMAGE:$CI_COMMIT_TAG"
# latest-ci bleibt bewusst ueberschreibbar - der Tag bezeichnet "der neueste
# Stand", nicht eine Version, und traegt deshalb kein Versprechen.
- docker push "$IMAGE:latest-ci"
# Einmalig/selten: Build-Image fuer den Desktop-Build (rust:bullseye + node + tcl/sqlcipher,
@@ -162,6 +217,10 @@ desktop_linux:
MAX_GLIBC: "2.31"
USE_HARD_LINKS: "false"
SQLCIPHER_BUNDLED: "1"
# Eigene Build-Variante: Produktname ThreadNet statt Element. Ohne das nutzt
# electron-builder element.io/release/build.json und die Pakete heissen weiter
# "element-desktop" (real passiert 2026-08-02).
VARIANT_PATH: axion1337/build.json
script:
- pnpm install --frozen-lockfile --filter element-desktop
- cp -r apps/web/webapp apps/desktop/webapp
@@ -201,6 +260,8 @@ desktop_windows:
SQLCIPHER_BUNDLED: "1"
ELECTRON_CACHE: C:\electron-cache
ELECTRON_BUILDER_CACHE: C:\eb-cache
# Eigene Build-Variante (Produktname ThreadNet) - siehe desktop_linux
VARIANT_PATH: axion1337/build.json
script:
- pnpm install --frozen-lockfile --filter element-desktop
# GitHub-CDN-Downloads (Electron-Binary, winCodeSign, NSIS) reissen im Gast
+15
View File
@@ -0,0 +1,15 @@
# Der Scope @sorb liegt in der Gitea-Registry auf rohana, nicht auf npmjs.
#
# Ohne diese Zeile loest pnpm @sorb/threadnet-call-embedded gegen
# registry.npmjs.org auf. Dass das bisher gutging, lag allein am Lockfile: es
# pinnt die vollstaendige Tarball-URL, und die CI installiert mit
# --frozen-lockfile. Beim ersten Anheben der Version faellt dieser Schutz weg.
#
# Heute endet das in einem 404, weil der Name auf npmjs frei ist. Genau darauf
# darf man sich nicht verlassen: registriert dort jemand @sorb, loest derselbe
# Befehl still gegen ein fremdes Paket auf (dependency confusion) - und zwar in
# dem Moment, in dem ein neuer Download ohnehin erwartet wird.
#
# Kein Geheimnis in dieser Datei: der Lesezugriff auf die Registry ist anonym.
# Siehe management#0055.
@sorb:registry=https://rohana.axion1337.de/api/packages/sorb/npm/
+33
View File
@@ -0,0 +1,33 @@
# AGENTS.md — ThreadNet-Web
> **Die Gruppenregeln sind kanonisch im `management`-Repo:**
> [`AGENTS.md`](https://git.lab/axion1337.chat/management/-/blob/main/AGENTS.md)
> — von außerhalb des Labs über den Gitea-Mirror lesbar:
> `https://rohana.axion1337.de/sorb/management`. Dort stehen Repo-Topologie und
> Mirror-Regeln, das Kanban-Framework (Status-Labels, WIP-Limit 2, ADR-Pflicht),
> Deploy-Übergabe und AAR-Verfahren, Secrets-Handhabung und die
> Karpathy-Leitlinien. Sie gelten für **jede** Session in diesem Repo.
> Hier steht nur, was zusätzlich für dieses Repository gilt.
## Was dieses Repo ist
Fork von **Element Web/Desktop** (Basis `1.12.17`) für axion1337.chat.
⚠️ **`README.md` und der Großteil von `docs/` sind unverändertes Upstream-Material** und
beschreiben diesen Fork ausdrücklich **nicht**. Wer sich danach richtet, übersieht jede
Anpassung.
## Der eine Ort, der zählt
**[`docs/axion1337-fork.md`](docs/axion1337-fork.md)** ist die vollständige Liste dessen, was
dieser Fork gegenüber Upstream ändert — und zugleich die **Portier-Checkliste** für jedes
Upstream-Upgrade: dort stehen die kritischen Dateien, an denen die Merge-Reibung entsteht.
**Jede Änderung am Fork gehört dort hinein.** Eine Anpassung, die nur im Code steht, geht beim
nächsten Rebase verloren — genau dafür existiert die Datei.
## Bauen & Ausrollen
Image wird in die Registry auf `rohana.axion1337.de` publiziert; die laufende Version zieht der
Cluster über den Tag im gitops-Repo (`apps/production/`). Ein Upgrade ist deshalb zweistufig:
erst hier rebasen und bauen, dann dort den Tag anheben.
+1
View File
@@ -0,0 +1 @@
Read AGENTS.md — the canonical instruction file for this repository. All rules live there.
+9
View File
@@ -0,0 +1,9 @@
{
"appId": "im.riot.app",
"name": "threadnet-desktop",
"productName": "ThreadNet",
"description": "ThreadNet — powered by Element",
"protocols": ["io.element.desktop", "element"],
"mac.icon": "build/icon.icns",
"dmg.badgeIcon": "build/icon.icns"
}
File diff suppressed because it is too large Load Diff
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 315 KiB

After

Width:  |  Height:  |  Size: 395 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 395 KiB

+27
View File
@@ -48,6 +48,8 @@ interface Variant extends Metadata {
"appId": string;
"linux.executableName"?: string;
"linux.deb.name"?: string;
"mac.icon"?: string;
"dmg.badgeIcon"?: string;
"protocols": string[];
}
@@ -202,6 +204,31 @@ if (variant["linux.deb.name"]) {
config.deb.fpm.push("--name", variant["linux.deb.name"]);
}
/**
* ThreadNet-Fork: Icon-Pfade fuer macOS ueberschreibbar machen.
*
* Upstream setzt `mac.icon` auf das Icon-Composer-Bundle `build/icon.icon`
* (macOS 26). Dessen Verarbeitung ruft `actool` auf, das es nur mit dem vollen
* Xcode gibt (~10 GB, App-Store-Login) - mit blossen CommandLineTools scheitert
* damit JEDER macOS-Build, nicht nur das DMG:
*
* Failed to check actool version. Is Xcode 26 or higher installed?
*
* Ueber die Variante laesst sich stattdessen das klassische `.icns` waehlen.
* Bewusst hier als Variantenoption statt als Aenderung an den Defaults oben:
* So bleibt Upstreams Wert unberuehrt und ein Upstream-Merge erzeugt keinen
* Konflikt an dieser Zeile.
*
* Preis: kein macOS-26-Icon-Rendering. Ohne Xcode gaebe es ohnehin keinen Build.
*/
if (variant["mac.icon"]) {
config.mac.icon = variant["mac.icon"];
}
if (variant["dmg.badgeIcon"]) {
config.dmg.badgeIcon = variant["dmg.badgeIcon"];
}
/**
* Allow specifying windows signing cert via env vars
* @param {string} process.env.ED_SIGNTOOL_SUBJECT_NAME
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "element-desktop",
"productName": "Element",
"productName": "ThreadNet",
"main": "lib/electron-main.js",
"exports": "./lib/electron-main.js",
"version": "1.12.17",
+1 -1
View File
@@ -13,7 +13,7 @@
"disable_login_language_selector": false,
"disable_3pid_login": false,
"force_verification": false,
"brand": "Element",
"brand": "aXion1337.Chat",
"integrations_ui_url": "https://scalar.vector.im/",
"integrations_rest_url": "https://scalar.vector.im/api",
"integrations_widgets_urls": [
+1 -1
View File
@@ -125,7 +125,7 @@
"@babel/preset-react": "^7.12.10",
"@babel/preset-typescript": "^7.12.7",
"@casualbot/jest-sonar-reporter": "2.7.0",
"@sorb/threadnet-call-embedded": "0.19.2-threadnet.5",
"@sorb/threadnet-call-embedded": "0.19.2-threadnet.12",
"@element-hq/element-web-playwright-common": "workspace:*",
"@fetch-mock/jest": "^0.2.20",
"@jest/globals": "^30.2.0",
@@ -10,3 +10,12 @@ Please see LICENSE files in the repository root for full details.
.mx_HelpUserSettingsTab_accessTokenDetails {
width: fit-content;
}
/* ThreadNet-Fork: Attribution unter den Versionsangaben. Zurueckhaltend
gesetzt - sie soll auffindbar sein, nicht um Aufmerksamkeit konkurrieren
mit der Version darueber, die Nutzer hier tatsaechlich suchen. */
.mx_HelpUserSettingsTab_attribution {
margin-top: var(--cpd-space-2x);
color: var(--cpd-color-text-secondary);
font: var(--cpd-font-body-sm-regular);
}
+3 -3
View File
@@ -1,8 +1,8 @@
{
"name": "Element",
"short_name": "Element",
"name": "ThreadNet",
"short_name": "ThreadNet",
"display": "standalone",
"theme_color": "#76CFA6",
"theme_color": "#ed4f4c",
"start_url": "index.html",
"icons": [
{
Binary file not shown.

After

Width:  |  Height:  |  Size: 590 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 596 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 395 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 829 B

After

Width:  |  Height:  |  Size: 901 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

+9 -4
View File
@@ -20,7 +20,11 @@ export const DEFAULTS: DeepReadonly<IConfigOptions> = {
branding: {
logo_link_url: "https://element.io",
auth_header_logo_url: "themes/element/img/logos/element-logo.svg",
welcome_background_url: "themes/element/img/backgrounds/lake.jpg",
// ThreadNet-Fork: eigenes Titelbild statt Elements lake.jpg. Steht hier im
// Default und nicht in element-values.yaml, weil die Bilddatei ohnehin nur
// ueber einen Build in den Container kommt - eine zusaetzliche Config-Zeile
// waere eine zweite Stelle, die mitwandern muesste.
welcome_background_url: "themes/element/img/backgrounds/alpenglow.jpg",
},
help_url: "https://element.io/help",
help_encryption_url: "https://element.io/help#encryption",
@@ -44,9 +48,10 @@ export const DEFAULTS: DeepReadonly<IConfigOptions> = {
// be preferred over their config.
desktopBuilds: {
available: true,
// eslint-disable-next-line @typescript-eslint/no-require-imports
logo: require("../res/img/element-desktop-logo.svg").default,
url: "https://element.io/get-started",
// ThreadNet-Fork: eigene Marke und eigener Download-Pfad statt
// element.io - wir liefern eigene Desktop-Builds aus.
logo: "vector-icons/512.png",
url: "https://git.lab/axion1337.chat/ThreadNet-Web/-/releases",
},
feedback: {
@@ -32,8 +32,8 @@ export const ErrorView: React.FC<IProps> = ({ title, messages, footer, children
<img
className="mx_ErrorView_logo"
height="160"
src="themes/element/img/logos/element-app-logo.png"
alt="Element"
src="vector-icons/512.png"
alt="ThreadNet"
/>
<div className="mx_ErrorView_container">
<Heading size="md" weight="semibold">
@@ -106,34 +106,42 @@ export default class HelpUserSettingsTab extends React.Component<EmptyObject, IS
<SettingsSubsection heading={_t("common|credits")}>
<SettingsSubsectionText>
<ul>
{/* ThreadNet-Fork: eigenes Titelbild, deshalb eigene Danksagung.
Bewusst OHNE _t(): der Schluessel credits|default_cover_photo
steckt in 32 Sprachdateien, 31 davon nennen Elements Fotografen
namentlich. Wuerden wir nur en/de anpassen, stuende in 29
Sprachen eine falsche Attribution — und die uebrigen 29 koennen
wir nicht pflegen, sie kommen aus Elements Uebersetzungsdienst.
Ein Fotografenname und eine Lizenzbezeichnung werden ohnehin
nicht uebersetzt. Der ungenutzte Schluessel bleibt in den
Sprachdateien stehen — ihn aus 32 Dateien zu entfernen waere
Laerm im naechsten Upstream-Merge. */}
<li>
{_t(
"credits|default_cover_photo",
{},
{
photo: (sub) => (
<ExternalLink
href="themes/element/img/backgrounds/lake.jpg"
rel="noreferrer noopener"
target="_blank"
>
{sub}
</ExternalLink>
),
author: (sub) => (
<ExternalLink href="https://www.flickr.com/golan">{sub}</ExternalLink>
),
terms: (sub) => (
<ExternalLink
href="https://creativecommons.org/licenses/by-sa/4.0/"
rel="noreferrer noopener"
target="_blank"
>
{sub}
</ExternalLink>
),
},
)}
The{" "}
<ExternalLink
href="themes/element/img/backgrounds/alpenglow.jpg"
rel="noreferrer noopener"
target="_blank"
>
default cover photo
</ExternalLink>{" "}
is by <ExternalLink href="https://unsplash.com/@heytowner">John Towner</ExternalLink> on{" "}
<ExternalLink
href="https://unsplash.com/photos/JgOeRuGD_Y4"
rel="noreferrer noopener"
target="_blank"
>
Unsplash
</ExternalLink>
, used under the{" "}
<ExternalLink
href="https://unsplash.com/license"
rel="noreferrer noopener"
target="_blank"
>
Unsplash License
</ExternalLink>
.
</li>
<li>
{_t(
@@ -259,6 +267,15 @@ export default class HelpUserSettingsTab extends React.Component<EmptyObject, IS
{cryptoVersion}
<br />
</CopyableText>
{/* ThreadNet-Fork: Attribution an Element. Bewusst NICHT in
getVersionTextToCopy — der Text dort geht in Fehlerberichte,
dort ist die Herkunft des Forks nur Rauschen. Ebenso bewusst
ohne _t(): ein Markenhinweis wird nicht übersetzt, und jeder
zusätzliche i18n-Schlüssel ist Reibung beim Upstream-Merge. */}
<div className="mx_HelpUserSettingsTab_attribution">
ThreadNet powered by{" "}
<ExternalLink href="https://element.io">Element</ExternalLink>
</div>
{updateButton}
</SettingsSubsectionText>
</SettingsSubsection>
+4 -3
View File
@@ -2,19 +2,20 @@
<html lang="en" style="height: 100%;">
<head>
<meta charset="utf-8">
<title>Element</title>
<title>ThreadNet</title>
<link rel="apple-touch-icon" sizes="120x120" href="<%= require('../../res/vector-icons/120.png') %>">
<link rel="apple-touch-icon" sizes="144x144" href="<%= require('../../res/vector-icons/144.png') %>">
<link rel="apple-touch-icon" sizes="152x152" href="<%= require('../../res/vector-icons/152.png') %>">
<link rel="apple-touch-icon" sizes="180x180" href="<%= require('../../res/vector-icons/180.png') %>">
<link rel="icon" href="vector-icons/favicon.ico" sizes="any">
<link rel="manifest" href="manifest.json">
<meta name="referrer" content="no-referrer">
<link rel="icon" type="image/png" sizes="24x24" href="<%= require('../../res/vector-icons/24.png') %>">
<link rel="icon" type="image/png" sizes="144x144" href="<%= require('../../res/vector-icons/144.png') %>">
<link rel="icon" type="image/png" sizes="512x512" href="<%= require('../../res/vector-icons/512.png') %>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="apple-mobile-web-app-title" content="Element">
<meta name="application-name" content="Element">
<meta name="apple-mobile-web-app-title" content="ThreadNet">
<meta name="application-name" content="ThreadNet">
<meta name="theme-color" content="#ffffff">
<meta property="og:image" content="<%= og_image_url %>" />
<meta http-equiv="Content-Security-Policy" content="
+154
View File
@@ -53,6 +53,112 @@ statt 755 committet (nicht ausführbar); (2) der `matrix-js-sdk#develop`-Git-Ref
`pnpm-lock.yaml` war veraltet (fehlte `src/oidc/authorize.ts`, das `apps/web` importiert).
Beide behoben.
## 4. Merge-Reibung: was ein Upstream-Update wirklich kostet
*Arbeitspaket 3 aus ThreadNet-Web#7. Gemessen am 2026-08-06, nicht geschätzt.*
### Die unangenehme Grundlage zuerst
**Dieses Repo enthält keine Upstream-Historie.** Am 2026-05-10 wurde ein kompletter
Element-Web-Baum importiert — in `3da3635`, zusammen mit dem ersten eigenen Feature im
selben Commit. Davor liegt nur ein `Initial commit` mit zwei Dateien.
⚠️ **Korrektur 2026-08-19: Der Import ist NICHT der Tag `v1.12.17`.** Gemessen nach
Aufnahme von `upstream` als zweitem Remote (management #0099, Schritt 2):
- `git diff v1.12.17 3da3635` ergibt **783 Dateien, +17443/10371** — weit mehr als
unser damaliges Feature.
- **115 Dateien existieren nur bei uns, 62 nur im Tag.** Darunter MVVM-Dateien wie
`apps/web/src/viewmodels/room/timeline/event-tile/body/TextualBodyViewModel.tsx` und
`packages/shared-components/src/core/roving/RovingTabIndex.tsx`: im Tag `v1.12.17`
**nicht vorhanden**, in `v1.12.26` und in unserem Import **vorhanden**.
- Der `CHANGELOG.md` unseres Imports endet bei 1.12.17 (2026-04-30) — er wird erst
beim Release fortgeschrieben.
Zusammen heißt das: Die Grundlage ist ein **`develop`-Stand nach dem 1.12.17-Release**
(1.12.18 erschien am 2026-05-12), nicht der Tag. Praktische Folge: **Ein Graft auf
`v1.12.17` wäre falsch** und würde jede künftige Zusammenführung auf eine erfundene
Ahnenreihe stellen. Wer den gemeinsamen Vorfahren nachträglich herstellen will, muss
den passenden `develop`-Commit suchen — das braucht die volle Upstream-Historie, ein
flacher Fetch der Tags genügt dafür nicht.
Daraus folgt das Wesentliche: **es gibt keinen gemeinsamen Vorfahren mit
`element-hq/element-web`.** Ein `git merge upstream/develop` ist nicht möglich; mit
`--allow-unrelated-histories` erzwungen, kollidiert praktisch jede Datei. Wer „mal
eben Upstream nachziehen" sagt, meint in diesem Repo also: neuen Upstream-Stand
beschaffen und unsere Änderungen darauf neu auftragen.
Das ist der Grund, warum die Zahl unten überhaupt zählt — sie ist der Aufwand jedes
Updates.
### Unser Delta: 102 Dateien, davon 12 kritische
`git diff --name-only 3da3635..main`:
| Menge | Bereich | Konfliktrisiko |
|---|---|---|
| 46 | CI/Build (`.github/`, `.gitlab-ci.yml`, `dockerbuild/`) | gering — eigene Strecke, Upstreams Workflows brauchen wir nicht |
| 22 | Config, Lockfiles, Upstream-Varianten | mittel — `pnpm-lock.yaml` konfliktet immer, wird aber regeneriert |
| 13 | Web-Assets (`apps/web/res/`) | gering — meist eigene Dateien |
| 4 + 3 | eigene Icons und `apps/desktop/axion1337/` | **keins** — kein Upstream-Pendant |
| **12** | **Upstream-Quellcode** | **hier entsteht die Arbeit** |
### Die 12 Dateien, und warum sie angefasst wurden
**Branding (5)** — flach, gut isolierbar:
- `apps/web/src/SdkConfig.ts` — Defaults für `brand`, `welcome_background_url`, `desktopBuilds`
- `apps/web/src/vector/index.html``<title>`, Favicon-Link, PWA-Namen
- `apps/web/src/async-components/structures/ErrorView.tsx` — Logo der Fehlerseite
- `apps/web/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx` — Attribution + Danksagung
- `apps/web/src/i18n/strings/en_EN.json` — einzelne Strings
**ClamAV-Client-Scanning (7)** — tief in der Medien-Pipeline:
- `apps/web/src/ContentMessages.ts`, `utils/ContentScanner.ts`, `utils/DecryptFile.ts`
- `apps/web/src/components/views/messages/MImageBody.tsx`, `MAudioBody.tsx`
- `apps/web/src/viewmodels/message-body/FileBodyViewModel.ts`, `VideoBodyViewModel.ts`
### Nicht der Umfang entscheidet, sondern die Art des Eingriffs
Gemessen in geänderten Zeilen nehmen sich beide Gruppen wenig: **ClamAV ~130 Zeilen,
Branding ~104**. Der größte Einzelpatch ist sogar Branding
(`HelpUserSettingsTab.tsx`, 71 Zeilen). Wer nur zählt, hält beide für gleich teuer.
Sie sind es nicht, und der Grund ist die Art des Eingriffs:
- **Branding-Patches stehen am Rand.** Ein zusätzlicher Default in `SdkConfig.ts`,
ein `<link>` im `<head>`, ein `<li>` in einer Settings-Liste. Wird die Datei
umgebaut, sieht man sofort, wo das eigene Stück wieder hin muss.
- **ClamAV-Patches stehen mittendrin** — in Entschlüsselungs- und Fehlerpfaden der
Medien-Pipeline, verschränkt mit Upstream-Logik. Ein geänderter Kontrollfluss
bedeutet nicht „Konflikt lösen", sondern „neu verstehen".
⚠️ **Und der eigentliche Haken: `viewmodels/`.** Element baut die Medien-Anzeige
gerade auf MVVM um (`docs/MVVM.md`; v1 ist dort bereits als deprecated markiert —
der Umbau läuft also schon in zweiter Runde). `FileBodyViewModel.ts` und
`VideoBodyViewModel.ts` gab es in älteren Ständen gar nicht. Unsere Änderung darin
ist mit je 6 Zeilen winzig — aber wenn Upstream diese Dateien verschiebt, umbenennt
oder auflöst, entsteht **kein Konflikt**: die Zeilen sind einfach weg, und Git meldet
nichts. Das ist gefährlicher als ein Konflikt, weil es stillschweigend passiert.
Praktische Folge: Nach einem Upstream-Update ist an ClamAV nicht die Merge-Ausgabe
maßgeblich, sondern ein **Funktionstest** — eine verschlüsselte Datei senden und eine
abgelehnte empfangen. Steht so auch in Abschnitt 2.
### Was daraus für künftige Änderungen folgt
1. **Erst prüfen, ob es die Konfiguration schon kann.** Auth-Logo, `logo_link_url`
und `brand` liefen ohne Rebuild über die ConfigMap; das Call-Widget wurde
vollständig über `VITE_PRODUCT_NAME` umbenannt, ohne eine einzige Quelldatei.
Jede so vermiedene Datei ist eine, die beim Update nicht kollidiert.
2. **Eigene Datei schlagen geänderte Datei.** `apps/desktop/axion1337/` und eigene
Assets kosten beim Merge nichts.
3. **Wenn Upstream-Code sein muss: einen Kommentar mit `ThreadNet-Fork:` und der
Begründung dazu.** Beim Neuauftragen auf einen neuen Stand ist die Frage nie
„was steht hier", sondern „warum stand das da" — und die beantwortet sonst
niemand mehr.
## Repo-Topologie (seit 2026-07-31)
**Kanonisch ist `git.lab/axion1337.chat/ThreadNet-Web`** (Homelab-GitLab, nur im Lab
@@ -60,3 +166,51 @@ auflösbar) — dort laufen Entwicklung und CI (`.gitlab-ci.yml`). Die Kopie auf
`rohana.axion1337.de/sorb/ThreadNet-Web` ist ein **Push-Mirror** (automatisch, GitLab →
Gitea) und dient als Lesekopie plus Standort für Issues, Container-Registry und Releases.
**Niemals direkt nach rohana pushen** — der Mirror überschreibt divergente Stände.
## Element Call anheben (`@sorb/threadnet-call-embedded`)
Das Call-Widget kommt als npm-Paket aus der **Gitea-Registry auf rohana**, nicht von
npmjs. `.npmrc` im Wurzelverzeichnis bindet den Scope fest:
```
@sorb:registry=https://rohana.axion1337.de/api/packages/sorb/npm/
```
Zum Anheben genügt damit der normale Weg — **kein Zusatzschritt, keine Umgebungsvariable**:
```sh
# Version in apps/web/package.json setzen, dann:
pnpm install --lockfile-only # Lockfile zieht die neue Tarball-URL von rohana
pnpm --dir apps/web build # NICHT --filter web: das endet mit exit 0, ohne zu bauen
```
Danach muss die Tarball-URL im `pnpm-lock.yaml` auf `rohana.axion1337.de` zeigen. Tut sie
das nicht, ist die `.npmrc` verlorengegangen: **ohne sie löst pnpm gegen npmjs auf**, und
das geht heute nur deshalb laut aus (404), weil der Name `@sorb` dort noch frei ist —
verlässt man sich darauf, ist es ein *dependency-confusion*-Weg. Siehe management#0055.
Vor dem Anheben liegt das Paket erst dann in der Registry, wenn in `threadnet-call` der
manuelle Job `publish_npm` gelaufen ist — der ist bewusst manuell und wird von sorb
ausgelöst.
## Upstream als zweiter Remote (seit 2026-08-19)
Der Remote ist **lokale Konfiguration**, er wird nicht mitcommittet. Wer den Vergleich
braucht, richtet ihn einmal ein:
```sh
git remote add upstream https://github.com/element-hq/element-web.git
git fetch --depth=1 upstream tag v1.12.26 # aktueller Stand
git fetch --depth=1 upstream tag v1.12.17 # zum Vergleich
```
Damit lässt sich ein Update **als Diff betrachten**, statt Dateien blind zu kopieren —
zum Beispiel für die zwölf Dateien, die wir angefasst haben:
```sh
git diff v1.12.26 main -- apps/web/src/... # unser Delta gegen Upstream
git diff v1.12.17 v1.12.26 -- <datei> # was Upstream seither geaendert hat
```
⚠️ Ein flacher Fetch liefert **keine Historie**: `git merge-base`, `git log` über
Upstream und ein Graft-Versuch brauchen `git fetch upstream` ohne `--depth`.
+5 -5
View File
@@ -617,8 +617,8 @@ importers:
specifier: ^5.0.0
version: 5.2.1(encoding@0.1.13)(webpack@5.106.2)
'@sorb/threadnet-call-embedded':
specifier: 0.19.2-threadnet.5
version: 0.19.2-threadnet.5
specifier: 0.19.2-threadnet.12
version: 0.19.2-threadnet.12
'@stylistic/eslint-plugin':
specifier: ^5.0.0
version: 5.10.0(eslint@8.57.1)
@@ -5003,8 +5003,8 @@ packages:
'@sinonjs/fake-timers@15.1.1':
resolution: {integrity: sha512-cO5W33JgAPbOh07tvZjUOJ7oWhtaqGHiZw+11DPbyqh2kHTBc3eF/CjJDeQ4205RLQsX6rxCuYOroFQwl7JDRw==}
'@sorb/threadnet-call-embedded@0.19.2-threadnet.5':
resolution: {integrity: sha512-u2TG7i82ibkpHSH/s6xA4Q6BTJqITdYWyrVieTiRwnm2Z+SCn/GmPK8JZIp/uSnpMrl9619yp/6vDVtpDJsKZg==, tarball: https://rohana.axion1337.de/api/packages/sorb/npm/%40sorb%2Fthreadnet-call-embedded/-/0.19.2-threadnet.5/threadnet-call-embedded-0.19.2-threadnet.5.tgz}
'@sorb/threadnet-call-embedded@0.19.2-threadnet.12':
resolution: {integrity: sha512-0x9nzkzGXFFVsXXI3LvLdWNXmjzJFsijh29yVXwrtFv/FQXgzwY8BttyMCCFHnEkp4VXtu7KeQfRCEzJB7rdIQ==, tarball: https://rohana.axion1337.de/api/packages/sorb/npm/%40sorb%2Fthreadnet-call-embedded/-/0.19.2-threadnet.12/threadnet-call-embedded-0.19.2-threadnet.12.tgz}
'@standard-schema/spec@1.1.0':
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
@@ -17632,7 +17632,7 @@ snapshots:
dependencies:
'@sinonjs/commons': 3.0.1
'@sorb/threadnet-call-embedded@0.19.2-threadnet.5': {}
'@sorb/threadnet-call-embedded@0.19.2-threadnet.12': {}
'@standard-schema/spec@1.1.0': {}