Compare commits

...
31 Commits
Author SHA1 Message Date
stefan.tollkuehn ebbfa990fb Relabel creator button as ICS download
The form button generates the ICS file, so name it after
the result. Move the Nextcloud button ahead of it, matching
the visual weight of the primary actions.
2026-11-05 17:52:27 +01:00
stefan.tollkuehn cfb48aa3c9 Group help texts under a help key
meetingPrivacySettings nests one level deeper under help.
The umbrella makes room for further help topics and allows
contextualizing which help a visible view offers. All
data-i18n references follow; no logic changes.
2026-11-05 17:37:24 +01:00
stefan.tollkuehn 4df51ca508 Simplify browser permissions wording
Name only the website-permissions icon when describing
where to change camera/mic decisions. The combined
lock-and-permissions phrasing was clunky in German and
duplicated what the icon already shows. Adjust all
translations accordingly.
2026-11-05 17:33:17 +01:00
stefan.tollkuehn c06127ef2b Document i18n markup and help overlay
Add the missing legalNotice parameter to the config table.
Describe how translations resolve keys, which markup the
renderer accepts, and how the OS-keyed help dialog works.
2026-11-05 17:29:31 +01:00
stefan.tollkuehn da2f6b5c25 Move Windows camera help into overlay
The privacy instructions no longer bloat the launcher card.
A "Help" button in the navbar opens them in a native dialog
(Esc, backdrop and button close it). The button only shows
for Windows users, reusing the existing OS detection.

Translations may contain <kbd>, <b> and <i> markup now. The
renderer builds nodes from a tag whitelist instead of using
innerHTML, so smuggled tags from i18n.json cannot execute.

All help texts live under meetingPrivacySettings.windows;
sibling keys for other operating systems can follow.
2026-11-05 17:24:15 +01:00
stefan.tollkuehn 7961732323 Show Windows privacy hints in launcher
The launcher sentence around the room name was replaced
by a standalone instruction ("meetingText"); the room name
now sits on its own line below it.

Windows users additionally get camera/microphone
permission instructions in the launcher view. The block
reuses the existing i18n/OS detection and only appears
when translations loaded, so a failed i18n.json fetch
cannot render an empty box.
2026-11-05 16:36:43 +01:00
stefan.tollkuehn f746508d90 fix title of AGENTS.md 2026-11-05 15:59:53 +01:00
stefan.tollkuehn 217a076120 Make legal notice link configurable
The legal notice link in the navbar was hardcoded to the
bnvb website. Deployments without that legal notice showed
a dead link to a foreign site.

Move the URL into assets/config.json (legalNotice), like
the Nextcloud and adhoc buttons. The link stays hidden
unless the entry is configured.
2026-11-03 11:09:22 +01:00
stefan.tollkuehn 49a3d90dc5 Scope localStorage keys per app directory
localStorage is scoped per origin, not per path. Multiple
jitsi-launch instances on one origin (e.g. meet.bnvb.eu/i
and /p) therefore shared one language and one theme
setting, and changing it in one instance changed it in
all others.

Prefix the language and theme keys with the app directory
derived from the page path. Each instance keeps its own
state; a root deployment gets the "/" prefix and behaves
as before. Previously saved choices reset once because
the key names change.
2026-11-03 11:06:50 +01:00
stefan.tollkuehn 0e4ba86c38 Make favicon paths configurable
The favicons were hardcoded in launch.html, so
rebranding the page required editing the template.

The paths now come from the favicons block in
assets/config.json. Missing keys keep the defaults
from launch.html.
2026-11-02 15:48:19 +01:00
stefan.tollkuehn 9184f60461 cleanup and htaccess fix 2026-11-02 15:09:00 +01:00
stefan.tollkuehn 64c49d210a update README 2026-11-02 15:02:09 +01:00
stefan.tollkuehn 1e3c7d676c rename config.json to config.json.sample and ignore config.json in git repository 2026-11-02 15:00:48 +01:00
stefan.tollkuehn c9accc2111 fix url for nextcloud 2026-10-22 17:23:56 +02:00
stefan.tollkuehn ce80e52923 - fix escaping in ICS generation
- add CRLF line endings in ICS
- add line folding in ICS
- fix downloadICS()
2026-10-22 17:09:16 +02:00
stefan.tollkuehn 86caf9a3a4 use relative urls, remove shortlink 2026-10-22 16:54:26 +02:00
stefan.tollkuehn 2c5e95ce7f translated comments in css 2026-10-22 16:45:24 +02:00
stefan.tollkuehn 0891a60beb .container should not be partially hidden behind .navbar 2026-10-22 16:44:01 +02:00
stefan.tollkuehn 3267b6a6a3 translation of comments and some other german terms 2026-10-22 16:34:01 +02:00
stefan.tollkuehn 5d54f574e0 use localized placeholder values for text input fields 2026-10-22 16:00:30 +02:00
stefan.tollkuehn 3ff88ce67f fix: remove duplicate line 2026-10-22 16:00:02 +02:00
stefan.tollkuehn c5f145a547 introduce setting "allowAdHocMeetings" to show or hide Adhoc-Meetings button 2026-10-22 15:54:31 +02:00
stefan.tollkuehn 7939d384bc display "Create in Nextcloud Calendar" button only if nextcloudBase is set in config.json 2026-10-22 15:51:01 +02:00
stefan.tollkuehn e9ad3882d2 move const CONFIG to json file and add documentation in README.md 2026-10-22 15:33:52 +02:00
stefan.tollkuehn c172c057eb add compatibility to nextcloud app jitsi_calendar 2026-10-07 22:14:56 +02:00
stefan.tollkuehn 88a9827de2 implement button to create event in nextcloud 2026-10-07 22:06:44 +02:00
stefan.tollkuehn b424abe8e4 add android download options for browsers other than chrome 2026-10-07 17:22:49 +02:00
stefan.tollkuehn 226edd8c48 a more robust way to make sure .card is not glued to bottom in any orientation 2026-10-07 17:16:57 +02:00
stefan.tollkuehn 344ecdf820 fix portrait mode 2026-10-07 17:13:47 +02:00
stefan.tollkuehn e08ffe6415 retry to fix glueing to bottom 2026-10-07 17:10:14 +02:00
stefan.tollkuehn 4f99d96bf4 Revert "test margin botton for mobile devices in landscape mode for create meeting view"
This reverts commit fa7a06be38.
2026-10-07 17:06:53 +02:00
9 changed files with 912 additions and 244 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
assets/config.json
+1 -5
View File
@@ -2,9 +2,5 @@ DirectoryIndex launch.html
# Hide repository meta files behind 404 # Hide repository meta files behind 404
<IfModule mod_rewrite.c> <IfModule mod_rewrite.c>
RewriteEngine On RewriteRule ^(README\.md|LICENSE|AGENTS\.md|.gitignore|.htaccess)$ - [NC,R=404,L]
RewriteRule ^(README\.md|LICENSE|AGENTS\.md)$ - [NC,R=404,L]
# security.txt: redirect to central security contact
RewriteRule ^\.well-known/security\.txt$ https://www.bnvb-ingenieure.de/.well-known/security.txt [NC,R=301,L]
</IfModule> </IfModule>
+1 -1
View File
@@ -1,4 +1,4 @@
# AGENT.MD # AGENTS.MD
- When writing something intended for human consumption, (comment, commit message, reply to prompt) use as few words as possible. Pick every word meticulously to reduce the volume to a strict minimum. Be down to the point. Less is more. - When writing something intended for human consumption, (comment, commit message, reply to prompt) use as few words as possible. Pick every word meticulously to reduce the volume to a strict minimum. Be down to the point. Less is more.
+42
View File
@@ -2,6 +2,48 @@
A small html/js project to nudge a user to use the desktop client for Jitsi. A small html/js project to nudge a user to use the desktop client for Jitsi.
## Configuration
All settings live in `assets/config.json`, loaded at startup. The launcher stays
up but links break if the file is missing or malformed.
Rename `assets/config.json.sample` to `assets/config.json`.
| Parameter | Description |
| --- | --- |
| `jitsiLauncher` | Domain of this Jitsi launcher. Used for the meeting URLs created by the meeting creator (Nextcloud, adhoc link). |
| `jitsiHost` | Jitsi domain. Used for the browser fallback URL, the desktop protocol URL and the UID of generated ICS files. |
| `nextcloudBase` | Nextcloud instance for calendar event creation. The "Create in Nextcloud Calendar" button is only shown if this is set. |
| `legalNotice` | URL of the "Legal Notice" link in the navbar. The link stays hidden if unset. |
| `allowAdhocMeetings` | Whether adhoc meetings are allowed. The "Create Adhoc Link" button is only shown if this is `true`. |
| `launchTimeout` | Timeout in ms before the browser fallback is shown after trying to start the jitsi-meet-electron client. |
| `backgroundImage` | Path to the page background image. Empty: no background. |
| `logo` | Path to the logo shown in the logo bar. Empty: logo bar stays hidden. |
| `favicons` | Paths to the page icons: `ico` (fallback icon), `svg` (icon), `appleTouch` (Apple touch icon). The values from `launch.html` are kept if a key is missing. |
| `downloads` | Download URLs for the jitsi-meet clients, shown in the fallback view per detected OS: `windows`, `macos`, `linux`, `ios`, `android`. |
## Translations
All texts live in `assets/i18n.json`, one object per language
(`en`, `de`, `fr`, `zh`). Elements carry the key in `data-i18n`
(dots descend into nested objects), inputs in `data-i18n-placeholder`.
Translations may contain `<kbd>`, `<b>` and `<i>` markup (no
nesting). The renderer builds only these whitelisted elements;
other tags render literally and never execute.
## Help overlay
The "Help" button in the navbar opens a dialog with
camera/microphone hints. It only shows on the detected OS.
All help texts live under `help` in `assets/i18n.json`,
grouped by topic; the overlay uses
`help.meetingPrivacySettings`, keyed by OS (`windows`;
`macos`, `linux`, `ios` and `android` can follow). Further
topics can extend `help` and be contextualized per visible
view. The dialog width is defined in `dialog.privacy`
(`assets/css/launch.css`).
## Third-party assets ## Third-party assets
- `assets/lib/ua-parser.min.js` — [ua-parser-js](https://github.com/faisalman/ua-parser-js) v2.0.10, © Faisal Salman, licensed under AGPL-3.0. - `assets/lib/ua-parser.min.js` — [ua-parser-js](https://github.com/faisalman/ua-parser-js) v2.0.10, © Faisal Salman, licensed under AGPL-3.0.
+22
View File
@@ -0,0 +1,22 @@
{
"jitsiLauncher": "server.domain.tld/subdir",
"jitsiHost": "jit.si",
"nextcloudBase": "https://cloud.domain.tld",
"legalNotice": "https://www.domain.tld/legal-notice",
"allowAdhocMeetings": true,
"launchTimeout": 2000,
"backgroundImage": "path/to/backgroundimage",
"logo": "path/to/logo",
"favicons": {
"ico": "path/to/favicon.ico",
"svg": "path/to/favicon.svg",
"appleTouch": "path/to/apple-touch-icon.png"
},
"downloads": {
"windows": "https://github.com/jitsi/jitsi-meet-electron/releases/latest/download/jitsi-meet.exe",
"macos": "https://github.com/jitsi/jitsi-meet-electron/releases/latest/download/jitsi-meet.dmg",
"linux": "https://github.com/jitsi/jitsi-meet-electron/releases",
"ios": "https://apps.apple.com/app/jitsi-meet/id1165103905",
"android": "https://play.google.com/store/apps/details?id=org.jitsi.meet"
}
}
+89 -32
View File
@@ -17,11 +17,16 @@ body {
color: #222; color: #222;
} }
/*
* The fixed navbar overlaps the top of the card – keep
* 80px of clearance (all modes, including portrait).
*/
.container { .container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
width: min(700px, calc(100% - 32px)); width: min(700px, calc(100% - 32px));
padding-top: 80px;
} }
.navbar { .navbar {
@@ -65,14 +70,14 @@ body {
margin-left: auto; margin-left: auto;
} }
/* Hamburger: nur im Phone-Portrait sichtbar. /* Hamburger: only visible on phone portrait.
* "button.nav-toggle" ist spezifischer als ".button", * "button.nav-toggle" is more specific than ".button",
* sonst überschreibt display: inline-block diese Regel. */ * otherwise display: inline-block overrides this rule. */
button.nav-toggle { button.nav-toggle {
display: none; display: none;
} }
/* Enger als der globale 6px-Margin der Buttons */ /* Tighter than the global 6px button margin */
.navbar .button { .navbar .button {
margin: 0; margin: 0;
} }
@@ -201,11 +206,46 @@ button, .button {
background: #f5f5f5; background: #f5f5f5;
} }
/* Windows help overlay – long text needs left
* alignment, the page is centered */
dialog.privacy {
width: min(1000px, calc(100% - 32px));
max-height: calc(100vh - 48px);
border: 0;
border-radius: 14px;
background: #f5f5f5;
padding: 20px;
text-align: left;
overflow-y: auto;
}
dialog.privacy::backdrop {
background: rgba(0, 0, 0, 0.5);
}
.help-actions {
margin-top: 24px;
text-align: center;
}
.room { .room {
display: block;
margin-top: 20px;
font-family: monospace; font-family: monospace;
word-break: break-all; word-break: break-all;
} }
/* Keyboard keys, marked with <kbd> in i18n.json */
kbd {
padding: 1px 6px;
border: 1px solid #ccc;
border-bottom-width: 3px;
border-radius: 4px;
background: #f0f0f0;
font-family: monospace;
font-size: 0.9em;
}
.error { .error {
padding: 16px; padding: 16px;
border-radius: 8px; border-radius: 8px;
@@ -264,6 +304,16 @@ button, .button {
color: #1a7f37; color: #1a7f37;
} }
/*
* scrollIntoView() aligns the target with the viewport
* edge, where the fixed navbar sits. scroll-margin-top
* keeps the target clear below the navbar.
*/
.adhoc-result,
.success-message {
scroll-margin-top: 110px;
}
.adhoc-link { .adhoc-link {
margin-top: 12px; margin-top: 12px;
display: flex; display: flex;
@@ -280,8 +330,8 @@ button, .button {
} }
/* /*
* Verhindert abgeschnittene Inhalte, wenn die Karte * Prevents clipped content when the card is taller
* höher als der Viewport ist ("center" schneidet oben ab). * than the viewport ("center" clips at the top).
*/ */
@supports (align-items: safe center) { @supports (align-items: safe center) {
body { body {
@@ -289,7 +339,7 @@ button, .button {
} }
} }
/* Sehr schmale Telefone: noch kompakter */ /* Very narrow phones: even more compact */
@media (max-width: 400px) { @media (max-width: 400px) {
.navbar { .navbar {
left: 2%; left: 2%;
@@ -313,18 +363,23 @@ button, .button {
} }
/* /*
* Phone: Navbar-Aktionen in beiden Ausrichtungen * Phone: show the navbar actions in both orientations
* hinter dem Hamburger-Button als Dropdown anzeigen. * as a dropdown behind the hamburger button.
* *
* Landscape-Phone: Viewport breiter als 600px – kurze * Landscape phone: viewport wider than 600px – short
* Seite (Höhe) und Touch-Eingabe entscheiden. * side (height) and touch input decide.
*/ */
@media (max-width: 600px), @media (max-width: 600px),
(orientation: landscape) and (max-height: 500px) and (pointer: coarse) { (orientation: landscape) and (max-height: 500px) and (pointer: coarse) {
body { body {
/* Navbar ist hier einzeilig */ /*
padding-top: 80px; * Padding instead of margin-bottom on the card:
min-height: calc(100vh - 80px); * margins at the scroll end are not covered by the
* scroll area, padding is.
*/
padding-bottom: 10px;
/* Subtract the 10px bottom padding */
min-height: calc(100vh - 10px);
} }
.navbar { .navbar {
@@ -347,12 +402,18 @@ button, .button {
padding: 20px; padding: 20px;
} }
/* Navbar is flatter here than on the desktop */
.adhoc-result,
.success-message {
scroll-margin-top: 80px;
}
button.nav-toggle { button.nav-toggle {
display: inline-flex; display: inline-flex;
} }
/* ".navbar .button" setzt margin: 0 – deshalb /* ".navbar .button" sets margin: 0 – hence target
* höher spezifisch ans Ziel: Button nach rechts */ * more specifically: push the button to the right */
.navbar .nav-toggle { .navbar .nav-toggle {
margin-left: auto; margin-left: auto;
} }
@@ -376,12 +437,12 @@ button, .button {
display: flex; display: flex;
} }
/* Alle Panel-Buttons gleich breit, Text zentriert */ /* All panel buttons equally wide, text centered */
.nav-actions .button { .nav-actions .button {
text-align: center; text-align: center;
} }
/* Sprach-Button liegt im .lang-menu-Wrapper */ /* Language button sits inside the .lang-menu wrapper */
.nav-actions .lang-menu .button { .nav-actions .lang-menu .button {
display: block; display: block;
width: 100%; width: 100%;
@@ -393,16 +454,6 @@ button, .button {
} }
} }
/*
* Landscape-Phone: Der Creator ist hoch – Abstand nach
* unten, damit der Inhalt beim Scrollen nicht am Rand klebt.
*/
@media (orientation: landscape) and (max-height: 500px) and (pointer: coarse) {
#meetingCreator {
margin-bottom: 10px;
}
}
@keyframes spin { @keyframes spin {
to { to {
transform: rotate(360deg); transform: rotate(360deg);
@@ -410,9 +461,9 @@ button, .button {
} }
/* /*
* Dark-Theme über die Klasse "dark" am html-Element. * Dark theme via the "dark" class on the html element.
* JS setzt sie anhand der gespeicherten Wahl oder der * JS sets it based on the saved choice or the system
* System-Einstellung (siehe detectTheme). * setting (see detectTheme).
*/ */
:root.dark body { :root.dark body {
background: #181818; background: #181818;
@@ -443,13 +494,19 @@ button, .button {
color: #bbb; color: #bbb;
} }
:root.dark kbd {
border-color: #555;
background: #404040;
}
:root.dark .secondary { :root.dark .secondary {
background: #444; background: #444;
color: #eee; color: #eee;
} }
:root.dark .status, :root.dark .status,
:root.dark .download { :root.dark .download,
:root.dark .privacy {
background: #303030; background: #303030;
} }
+136 -20
View File
@@ -1,12 +1,11 @@
{ {
"en": { "en": {
"pageTitle": "Jitsi Meeting", "pageTitle": "Jitsi Meeting",
"impressum": "Legal Notice", "legal_notice": "Legal Notice",
"source": "Source", "source": "Source",
"launcher": { "launcher": {
"title": "Jitsi Meeting", "title": "Jitsi Meeting",
"meetingPrefix": "The meeting", "meetingText": "Please click the button, to join the meeting:",
"meetingSuffix": "will be joined.",
"openButton": "Join meeting" "openButton": "Join meeting"
}, },
"starting": { "starting": {
@@ -42,6 +41,11 @@
"title": "Jitsi Meet for iOS", "title": "Jitsi Meet for iOS",
"description": "Install the Jitsi Meet app and then try opening the meeting again.", "description": "Install the Jitsi Meet app and then try opening the meeting again.",
"button": "Download Jitsi for iOS" "button": "Download Jitsi for iOS"
},
"android": {
"title": "Jitsi Meet for Android",
"description": "Install the Jitsi Meet app and then try opening the meeting again.",
"button": "Download Jitsi for Android"
} }
}, },
"browser": { "browser": {
@@ -59,11 +63,13 @@
"creator": { "creator": {
"title": "Create New Meeting", "title": "Create New Meeting",
"meetingNameLabel": "Meeting Name", "meetingNameLabel": "Meeting Name",
"meetingNamePlaceholder": "My Meeting",
"meetingDescriptionLabel": "Description", "meetingDescriptionLabel": "Description",
"meetingDescriptionPlaceholder": "Meeting description (optional)",
"meetingStartLabel": "Start Date & Time", "meetingStartLabel": "Start Date & Time",
"meetingEndLabel": "End Date & Time", "meetingEndLabel": "End Date & Time",
"meetingOrganizerLabel": "Organizer Email", "meetingOrganizerLabel": "Organizer Email",
"createButton": "Create Meeting", "createButton": "Download ICS File",
"cancelButton": "Cancel", "cancelButton": "Cancel",
"successMessage": "Meeting created! Download your calendar file and open it to import the meeting into your personal calendar.", "successMessage": "Meeting created! Download your calendar file and open it to import the meeting into your personal calendar.",
"downloadButton": "Download ICS File", "downloadButton": "Download ICS File",
@@ -78,17 +84,39 @@
"endTimeRequired": "Please select an end time", "endTimeRequired": "Please select an end time",
"endAfterStart": "End time must be after start time", "endAfterStart": "End time must be after start time",
"organizerRequired": "Please enter an organizer email", "organizerRequired": "Please enter an organizer email",
"organizerInvalid": "Please enter a valid organizer email address" "organizerInvalid": "Please enter a valid organizer email address",
"nextcloudButton": "Create in Nextcloud Calendar"
},
"help": {
"meetingPrivacySettings": {
"windows": {
"title": "Camera and microphone in Windows: How to control your privacy",
"intro": "Your camera and microphone are sensitive devices. Windows therefore ensures that they can only be used with your permission. A few steps give you full control, especially when you use video calls or voice messages directly in your web browser.",
"step1Title": "Step 1: Open the Windows settings",
"step1Description": "Open the <b>Windows Settings</b> (<kbd>⊞ Windows</kbd> + <kbd>I</kbd>). There, select the area <b>Privacy & security</b> and then <b>Camera</b> or <b>Microphone</b>.",
"step2Title": "Step 2: Regulate overall access",
"step2Description": "At the top of the page you will find the central switch for camera and microphone access. If it is disabled, no application and no website can access the device. If it is enabled, you decide on the same page whether applications are allowed access in principle.",
"step3Title": "Step 3: Check website permissions in the browser",
"step3Description": "Even if Windows allows access, your browser asks each website individually whether the camera or microphone may be used. This is the most important protection in everyday use: only when you agree does the access actually become possible.",
"browserPermissions": "You can change or revoke these decisions at any time. To do so, click the website-permissions icon in your browser's address bar and select the option for site settings. There you can allow the camera and microphone for the respective site, ask each time, or block them permanently.",
"goodToKnowTitle": "Good to know",
"goodToKnow1": "You can revoke permissions for individual websites at any time without changing Windows settings.",
"goodToKnow2": "An active microphone or camera is indicated in Windows by a small camera or microphone icon in the taskbar. This way you immediately recognize when a site is accessing a device.",
"goodToKnow3": "If a website does not get access despite working hardware, the cause is often not the device itself, but one of the two permission levels, in Windows or in the browser.",
"adminSupport": "If you need support finding the right settings, please ask <b>your administrator</b>.",
"navButton": "Help",
"closeButton": "Close"
}
}
} }
}, },
"de": { "de": {
"pageTitle": "Jitsi Meeting öffnen", "pageTitle": "Jitsi Meeting öffnen",
"impressum": "Impressum", "legal_notice": "Impressum",
"source": "Quellcode", "source": "Quellcode",
"launcher": { "launcher": {
"title": "Jitsi Meeting", "title": "Jitsi Meeting",
"meetingPrefix": "Das Meeting", "meetingText": "Bitte klicken Sie auf die Schaltfläche, um an dem Meeting teilzunehmen:",
"meetingSuffix": "wird geöffnet.",
"openButton": "An Meeting teilnehmen" "openButton": "An Meeting teilnehmen"
}, },
"starting": { "starting": {
@@ -124,6 +152,11 @@
"title": "Jitsi Meet für iOS", "title": "Jitsi Meet für iOS",
"description": "Installieren Sie die Jitsi Meet App und versuchen Sie anschließend erneut, das Meeting zu öffnen.", "description": "Installieren Sie die Jitsi Meet App und versuchen Sie anschließend erneut, das Meeting zu öffnen.",
"button": "Jitsi für iOS herunterladen" "button": "Jitsi für iOS herunterladen"
},
"android": {
"title": "Jitsi Meet für Android",
"description": "Installieren Sie die Jitsi Meet App und versuchen Sie anschließend erneut, das Meeting zu öffnen.",
"button": "Jitsi für Android herunterladen"
} }
}, },
"browser": { "browser": {
@@ -141,11 +174,13 @@
"creator": { "creator": {
"title": "Neues Meeting erstellen", "title": "Neues Meeting erstellen",
"meetingNameLabel": "Meeting-Name", "meetingNameLabel": "Meeting-Name",
"meetingNamePlaceholder": "Mein Meeting",
"meetingDescriptionLabel": "Beschreibung", "meetingDescriptionLabel": "Beschreibung",
"meetingDescriptionPlaceholder": "Beschreibung des Meetings (optional)",
"meetingStartLabel": "Start Datum & Uhrzeit", "meetingStartLabel": "Start Datum & Uhrzeit",
"meetingEndLabel": "Ende Datum & Uhrzeit", "meetingEndLabel": "Ende Datum & Uhrzeit",
"meetingOrganizerLabel": "Organisator E-Mail", "meetingOrganizerLabel": "Organisator E-Mail",
"createButton": "Meeting erstellen", "createButton": "ICS-Datei herunterladen",
"cancelButton": "Abbrechen", "cancelButton": "Abbrechen",
"successMessage": "Meeting erstellt! Laden Sie Ihre Kalenderdatei herunter und öffnen Sie diese, um das Meeting in Ihren persönlichen Kalender zu importieren.", "successMessage": "Meeting erstellt! Laden Sie Ihre Kalenderdatei herunter und öffnen Sie diese, um das Meeting in Ihren persönlichen Kalender zu importieren.",
"downloadButton": "ICS-Datei herunterladen", "downloadButton": "ICS-Datei herunterladen",
@@ -160,17 +195,39 @@
"endTimeRequired": "Bitte wählen Sie eine Endzeit", "endTimeRequired": "Bitte wählen Sie eine Endzeit",
"endAfterStart": "Die Endzeit muss nach der Startzeit liegen", "endAfterStart": "Die Endzeit muss nach der Startzeit liegen",
"organizerRequired": "Bitte geben Sie eine Organisator-E-Mail ein", "organizerRequired": "Bitte geben Sie eine Organisator-E-Mail ein",
"organizerInvalid": "Bitte geben Sie eine gültige Organisator-E-Mail-Adresse ein" "organizerInvalid": "Bitte geben Sie eine gültige Organisator-E-Mail-Adresse ein",
"nextcloudButton": "Im Nextcloud-Kalender erstellen"
},
"help": {
"meetingPrivacySettings": {
"windows": {
"title": "Kamera und Mikrofon in Windows: So kontrollieren Sie Ihre Privatsphäre",
"intro": "Ihre Kamera und Ihr Mikrofon sind sensible Geräte. Windows sorgt dafür, dass sie nur mit Ihrer Erlaubnis genutzt werden können. Mit wenigen Schritten behalten Sie die volle Kontrolle, insbesondere wenn Sie Videotelefonie oder Sprachnachrichten direkt im Webbrowser nutzen.",
"step1Title": "Schritt 1: Windows-Einstellungen öffnen",
"step1Description": "Öffnen Sie die <b>Windows-Einstellungen</b> (<kbd>⊞ Windows</kbd> + <kbd>I</kbd>). Wählen Sie dort den Bereich <b>Datenschutz und Sicherheit</b> und anschließend <b>Kamera</b> oder <b>Mikrofon</b>.",
"step2Title": "Schritt 2: Gesamtzugriff regeln",
"step2Description": "Oben auf der Seite finden Sie den zentralen Schalter für den Kamera- bzw. Mikrofonzugriff. Ist er deaktiviert, kann keine Anwendung und keine Website auf das Gerät zugreifen. Ist er aktiviert, entscheiden Sie auf der gleichen Seite, ob Anwendungen den Zugriff grundsätzlich erhalten dürfen.",
"step3Title": "Schritt 3: Website-Berechtigungen im Browser prüfen",
"step3Description": "Auch wenn Windows den Zugriff erlaubt, fragt Ihr Browser bei jeder Website einzeln nach, ob Kamera oder Mikrofon genutzt werden dürfen. Das ist der wichtigste Schutz im Alltag: Erst wenn Sie zustimmen, wird der Zugriff tatsächlich möglich.",
"browserPermissions": "Sie können diese Entscheidungen jederzeit ändern oder widerrufen. Klicken Sie dazu in der Adressleiste Ihres Browsers auf das Symbol für die Website-Berechtigungen und wählen Sie die Option für Website-Einstellungen. Dort lassen sich Kamera und Mikrofon für die jeweilige Seite gezielt erlauben, einmalig anfragen oder dauerhaft blockieren.",
"goodToKnowTitle": "Gut zu wissen",
"goodToKnow1": "Sie können Berechtigungen für einzelne Websites jederzeit zurückziehen, ohne Windows-Einstellungen zu ändern.",
"goodToKnow2": "Ein aktives Mikrofon oder eine aktive Kamera wird unter Windows durch ein kleines Kamera- oder Mikrofonsymbol in der Taskleiste angezeigt. So erkennen Sie sofort, wenn eine Seite auf ein Gerät zugreift.",
"goodToKnow3": "Wenn eine Website trotz funktionierender Hardware keinen Zugriff erhält, liegt die Ursache häufig nicht am Gerät selbst, sondern an einer der beiden Berechtigungsebenen, in Windows oder im Browser.",
"adminSupport": "Sollten Sie Unterstützung dabei benötigen, die passenden Einstellungen zu finden, fragen Sie bitte <b>Ihren Administrator</b>.",
"navButton": "Hilfe",
"closeButton": "Schließen"
}
}
} }
}, },
"fr": { "fr": {
"pageTitle": "Réunion Jitsi", "pageTitle": "Réunion Jitsi",
"impressum": "Mentions légales", "legal_notice": "Mentions légales",
"source": "Code source", "source": "Code source",
"launcher": { "launcher": {
"title": "Réunion Jitsi", "title": "Réunion Jitsi",
"meetingPrefix": "La réunion", "meetingText": "Veuillez cliquer sur le bouton pour rejoindre la réunion :",
"meetingSuffix": "va être ouverte.",
"openButton": "Rejoindre la réunion" "openButton": "Rejoindre la réunion"
}, },
"starting": { "starting": {
@@ -206,6 +263,11 @@
"title": "Jitsi Meet pour iOS", "title": "Jitsi Meet pour iOS",
"description": "Installez l'application Jitsi Meet, puis essayez de rouvrir la réunion.", "description": "Installez l'application Jitsi Meet, puis essayez de rouvrir la réunion.",
"button": "Télécharger Jitsi pour iOS" "button": "Télécharger Jitsi pour iOS"
},
"android": {
"title": "Jitsi Meet pour Android",
"description": "Installez l'application Jitsi Meet, puis essayez de rouvrir la réunion.",
"button": "Télécharger Jitsi pour Android"
} }
}, },
"browser": { "browser": {
@@ -223,11 +285,13 @@
"creator": { "creator": {
"title": "Créer une nouvelle réunion", "title": "Créer une nouvelle réunion",
"meetingNameLabel": "Nom de la réunion", "meetingNameLabel": "Nom de la réunion",
"meetingNamePlaceholder": "Ma réunion",
"meetingDescriptionLabel": "Description", "meetingDescriptionLabel": "Description",
"meetingDescriptionPlaceholder": "Description de la réunion (facultatif)",
"meetingStartLabel": "Date et heure de début", "meetingStartLabel": "Date et heure de début",
"meetingEndLabel": "Date et heure de fin", "meetingEndLabel": "Date et heure de fin",
"meetingOrganizerLabel": "Email de l'organisateur", "meetingOrganizerLabel": "Email de l'organisateur",
"createButton": "Créer la réunion", "createButton": "Télécharger le fichier ICS",
"cancelButton": "Annuler", "cancelButton": "Annuler",
"successMessage": "Réunion créée ! Téléchargez votre fichier calendrier et ouvrez-le pour importer la réunion dans votre calendrier personnel.", "successMessage": "Réunion créée ! Téléchargez votre fichier calendrier et ouvrez-le pour importer la réunion dans votre calendrier personnel.",
"downloadButton": "Télécharger le fichier ICS", "downloadButton": "Télécharger le fichier ICS",
@@ -242,17 +306,39 @@
"endTimeRequired": "Veuillez sélectionner une heure de fin", "endTimeRequired": "Veuillez sélectionner une heure de fin",
"endAfterStart": "L'heure de fin doit être après l'heure de début", "endAfterStart": "L'heure de fin doit être après l'heure de début",
"organizerRequired": "Veuillez entrer un email d'organisateur", "organizerRequired": "Veuillez entrer un email d'organisateur",
"organizerInvalid": "Veuillez entrer une adresse email d'organisateur valide" "organizerInvalid": "Veuillez entrer une adresse email d'organisateur valide",
"nextcloudButton": "Créer dans le calendrier Nextcloud"
},
"help": {
"meetingPrivacySettings": {
"windows": {
"title": "Caméra et microphone sous Windows : comment contrôler votre vie privée",
"intro": "Votre caméra et votre microphone sont des appareils sensibles. Windows veille donc à ce qu'ils ne puissent être utilisés qu'avec votre autorisation. En quelques étapes, vous gardez le contrôle total, surtout lorsque vous utilisez la visioconférence ou les messages vocaux directement dans votre navigateur.",
"step1Title": "Étape 1 : Ouvrir les paramètres Windows",
"step1Description": "Ouvrez les <b>paramètres Windows</b> (<kbd>⊞ Windows</kbd> + <kbd>I</kbd>). Sélectionnez ensuite la section <b>Confidentialité et sécurité</b>, puis <b>Caméra</b> ou <b>Micro</b>.",
"step2Title": "Étape 2 : Régler l'accès global",
"step2Description": "En haut de la page se trouve l'interrupteur central de l'accès à la caméra ou au microphone. S'il est désactivé, aucune application ni aucun site web ne peut accéder à l'appareil. S'il est activé, vous décidez sur la même page si les applications peuvent y accéder en principe.",
"step3Title": "Étape 3 : Vérifier les autorisations du site dans le navigateur",
"step3Description": "Même si Windows autorise l'accès, votre navigateur demande site par site si la caméra ou le microphone peuvent être utilisés. C'est la protection la plus importante au quotidien : seul votre consentement rend l'accès réellement possible.",
"browserPermissions": "Vous pouvez modifier ou révoquer ces décisions à tout moment. Pour cela, cliquez dans la barre d'adresse de votre navigateur sur l'icône des autorisations du site, puis choisissez l'option des paramètres du site. Vous y pouvez autoriser, demander à chaque fois ou bloquer définitivement la caméra et le microphone pour le site concerné.",
"goodToKnowTitle": "Bon à savoir",
"goodToKnow1": "Vous pouvez retirer à tout moment les autorisations de sites individuels sans modifier les paramètres Windows.",
"goodToKnow2": "Un microphone ou une caméra active est signalé sous Windows par une petite icône de caméra ou de microphone dans la barre des tâches. Vous reconnaissez ainsi immédiatement quand un site accède à un appareil.",
"goodToKnow3": "Si un site n'obtient pas l'accès malgré un matériel fonctionnel, la cause n'est souvent pas l'appareil lui-même, mais l'un des deux niveaux d'autorisation, sous Windows ou dans le navigateur.",
"adminSupport": "Si vous avez besoin d'aide pour trouver les bons réglages, veuillez demander à <b>votre administrateur</b>.",
"navButton": "Aide",
"closeButton": "Fermer"
}
}
} }
}, },
"zh": { "zh": {
"pageTitle": "Jitsi 会议", "pageTitle": "Jitsi 会议",
"impressum": "法律声明", "legal_notice": "法律声明",
"source": "源代码", "source": "源代码",
"launcher": { "launcher": {
"title": "Jitsi 会议", "title": "Jitsi 会议",
"meetingPrefix": "会议", "meetingText": "请点击按钮加入会议:",
"meetingSuffix": "即将打开。",
"openButton": "加入会议" "openButton": "加入会议"
}, },
"starting": { "starting": {
@@ -288,6 +374,11 @@
"title": "适用于 iOS 的 Jitsi Meet", "title": "适用于 iOS 的 Jitsi Meet",
"description": "请安装 Jitsi Meet 应用,然后再次尝试打开会议。", "description": "请安装 Jitsi Meet 应用,然后再次尝试打开会议。",
"button": "下载 iOS 版 Jitsi" "button": "下载 iOS 版 Jitsi"
},
"android": {
"title": "适用于 Android 的 Jitsi Meet",
"description": "请安装 Jitsi Meet 应用,然后再次尝试打开会议。",
"button": "下载 Android 版 Jitsi"
} }
}, },
"browser": { "browser": {
@@ -305,11 +396,13 @@
"creator": { "creator": {
"title": "创建新会议", "title": "创建新会议",
"meetingNameLabel": "会议名称", "meetingNameLabel": "会议名称",
"meetingNamePlaceholder": "我的会议",
"meetingDescriptionLabel": "描述", "meetingDescriptionLabel": "描述",
"meetingDescriptionPlaceholder": "会议描述(可选)",
"meetingStartLabel": "开始日期和时间", "meetingStartLabel": "开始日期和时间",
"meetingEndLabel": "结束日期和时间", "meetingEndLabel": "结束日期和时间",
"meetingOrganizerLabel": "组织者邮箱", "meetingOrganizerLabel": "组织者邮箱",
"createButton": "创建会议", "createButton": "下载 ICS 文件",
"cancelButton": "取消", "cancelButton": "取消",
"successMessage": "会议已创建!请下载您的日历文件并打开它,将会议导入您的个人日历。", "successMessage": "会议已创建!请下载您的日历文件并打开它,将会议导入您的个人日历。",
"downloadButton": "下载 ICS 文件", "downloadButton": "下载 ICS 文件",
@@ -324,7 +417,30 @@
"endTimeRequired": "请选择结束时间", "endTimeRequired": "请选择结束时间",
"endAfterStart": "结束时间必须晚于开始时间", "endAfterStart": "结束时间必须晚于开始时间",
"organizerRequired": "请输入组织者邮箱", "organizerRequired": "请输入组织者邮箱",
"organizerInvalid": "请输入有效的组织者邮箱地址" "organizerInvalid": "请输入有效的组织者邮箱地址",
"nextcloudButton": "在 Nextcloud 日历中创建"
},
"help": {
"meetingPrivacySettings": {
"windows": {
"title": "Windows 中的摄像头和麦克风:如何掌控您的隐私",
"intro": "摄像头和麦克风是敏感设备。因此,Windows 确保只有获得您的许可才能使用它们。只需几个步骤,您就能完全掌控,尤其是在浏览器中直接使用视频通话或语音消息时。",
"step1Title": "步骤 1:打开 Windows 设置",
"step1Description": "打开 <b>Windows 设置</b>(<kbd>⊞ Windows</kbd> + <kbd>I</kbd>)。选择<b>隐私和安全性</b>,然后选择<b>摄像头</b>或<b>麦克风</b>。",
"step2Title": "步骤 2:管理总体访问权限",
"step2Description": "页面顶部有控制摄像头或麦克风访问的总开关。如果关闭,任何应用和网站都无法访问设备。如果开启,您可以在同一页面决定是否允许应用访问。",
"step3Title": "步骤 3:检查浏览器中的网站权限",
"step3Description": "即使 Windows 允许访问,浏览器也会逐个询问每个网站是否可以使用摄像头或麦克风。这是日常使用中最重要的保护:只有您同意后,访问才会真正生效。",
"browserPermissions": "您可以随时更改或撤销这些决定。为此,请点击浏览器地址栏中的网站权限图标,并选择网站设置选项。在那里可以为相应网站专门允许、每次询问或永久阻止摄像头和麦克风。",
"goodToKnowTitle": "实用提示",
"goodToKnow1": "您可以随时撤销单个网站的权限,而无需更改 Windows 设置。",
"goodToKnow2": "在 Windows 中,使用中的麦克风或摄像头会通过任务栏中的小摄像头或麦克风图标显示。这样您可以立即发现某个网站正在访问设备。",
"goodToKnow3": "如果网站在硬件正常的情况下仍无法获得访问权限,原因往往不在设备本身,而在两个权限层级之一:Windows 或浏览器。",
"adminSupport": "如果您需要帮助查找相应的设置,请咨询<b>您的管理员</b>。",
"navButton": "帮助",
"closeButton": "关闭"
}
}
} }
} }
} }
+549 -169
View File
File diff suppressed because it is too large. Load diff
+71 -17
View File
@@ -4,10 +4,9 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jitsi Meeting</title> <title>Jitsi Meeting</title>
<link rel='shortlink' href='https://meet.bnvb.eu/' /> <link id="faviconIco" href="assets/images/favicon.ico" rel="alternate icon">
<link href="https://meet.bnvb.eu/assets/images/favicon.ico" rel="alternate icon"> <link id="appleTouchIcon" href="assets/images/apple-touch-icon.png" rel="apple-touch-icon" sizes="180x180">
<link href="https://meet.bnvb.eu/assets/images/apple-touch-icon.png" rel="apple-touch-icon" sizes="180x180"> <link id="faviconSvg" href="assets/images/favicon.svg" rel="icon" type="image/svg+xml">
<link href="https://meet.bnvb.eu/assets/images/favicon.svg" rel="icon" type="image/svg+xml">
<link rel="stylesheet" href="assets/css/launch.css"> <link rel="stylesheet" href="assets/css/launch.css">
</head> </head>
@@ -31,9 +30,12 @@
</svg> </svg>
</button> </button>
<div id="navActions" class="nav-actions"> <div id="navActions" class="nav-actions">
<a class="button secondary" href="https://www.bnvb-ingenieure.de/impressum" target="_blank" rel="noopener noreferrer" data-i18n="impressum"> <a id="legalNoticeLink" class="button secondary hidden" href="#" target="_blank" rel="noopener noreferrer" data-i18n="legal_notice">
Impressum Legal Notice
</a> </a>
<button id="windowsHelpButton" class="button secondary hidden" type="button" data-i18n="help.meetingPrivacySettings.windows.navButton">
Help
</button>
<a class="button secondary" href="https://git.bnvb.eu/stefan.tollkuehn/jitsi-launch" target="_blank" rel="noopener noreferrer" data-i18n="source"> <a class="button secondary" href="https://git.bnvb.eu/stefan.tollkuehn/jitsi-launch" target="_blank" rel="noopener noreferrer" data-i18n="source">
Source Source
</a> </a>
@@ -69,19 +71,16 @@
<h1 data-i18n="launcher.title">Jitsi Meeting</h1> <h1 data-i18n="launcher.title">Jitsi Meeting</h1>
<p> <p>
<span data-i18n="launcher.meetingPrefix"> <span data-i18n="launcher.meetingText">
The meeting Please click the button, to join the meeting:
</span> </span>
<span class="room"> <span class="room">
<strong id="roomName"></strong> <strong id="roomName"></strong>
</span> </span>
<span data-i18n="launcher.meetingSuffix">
will be opened.
</span>
</p> </p>
<button id="openButton" class="button primary" type="button" data-i18n="launcher.openButton"> <button id="openButton" class="button primary" type="button" data-i18n="launcher.openButton">
Open meeting Join meeting
</button> </button>
<button id="createNewFromLauncherButton" class="button secondary hidden" type="button" data-i18n="creator.createNewButton"> <button id="createNewFromLauncherButton" class="button secondary hidden" type="button" data-i18n="creator.createNewButton">
@@ -177,6 +176,21 @@
</a> </a>
</div> </div>
<!-- Android -->
<div id="androidDownload" class="download hidden">
<h2 data-i18n="downloads.android.title">
Jitsi Meet for Android
</h2>
<p data-i18n="downloads.android.description">
Install the Jitsi Meet app and then try opening the meeting again.
</p>
<a id="androidLink" class="button secondary" target="_blank" rel="noopener noreferrer" data-i18n="downloads.android.button">
Download Jitsi for Android
</a>
</div>
<!-- unknown --> <!-- unknown -->
<div id="otherDownload" class="download hidden"> <div id="otherDownload" class="download hidden">
<h2 data-i18n="downloads.other.title"> <h2 data-i18n="downloads.other.title">
@@ -247,12 +261,12 @@
<div class="form-group"> <div class="form-group">
<label for="meetingName" data-i18n="creator.meetingNameLabel">Meeting Name</label> <label for="meetingName" data-i18n="creator.meetingNameLabel">Meeting Name</label>
<input type="text" id="meetingName" placeholder="My Meeting" /> <input type="text" id="meetingName" placeholder="My Meeting" data-i18n-placeholder="creator.meetingNamePlaceholder" />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="meetingDescription" data-i18n="creator.meetingDescriptionLabel">Description</label> <label for="meetingDescription" data-i18n="creator.meetingDescriptionLabel">Description</label>
<textarea id="meetingDescription" placeholder="Meeting description (optional)"></textarea> <textarea id="meetingDescription" placeholder="Meeting description (optional)" data-i18n-placeholder="creator.meetingDescriptionPlaceholder"></textarea>
</div> </div>
<div class="form-group"> <div class="form-group">
@@ -271,10 +285,13 @@
</div> </div>
<div class="form-actions"> <div class="form-actions">
<button id="createMeetingButton" class="button primary" type="button" data-i18n="creator.createButton"> <button id="createNextcloudButton" class="button primary hidden" type="button" data-i18n="creator.nextcloudButton">
Create Meeting Create in Nextcloud Calendar
</button> </button>
<button id="createAdhocButton" class="button secondary" type="button" data-i18n="creator.adhocButton"> <button id="createMeetingButton" class="button primary" type="button" data-i18n="creator.createButton">
Download ICS File
</button>
<button id="createAdhocButton" class="button secondary hidden" type="button" data-i18n="creator.adhocButton">
Create Adhoc Link Create Adhoc Link
</button> </button>
<button id="cancelCreateButton" class="button secondary" type="button" data-i18n="creator.cancelButton"> <button id="cancelCreateButton" class="button secondary" type="button" data-i18n="creator.cancelButton">
@@ -294,10 +311,47 @@
<span data-i18n="creator.successMessage">Meeting created! Download your calendar file and open it to import the meeting into your personal calendar.</span> <span data-i18n="creator.successMessage">Meeting created! Download your calendar file and open it to import the meeting into your personal calendar.</span>
<a id="downloadIcsLink" class="button primary" style="margin-top: 12px; display: inline-block;" data-i18n="creator.downloadButton">Download ICS File</a> <a id="downloadIcsLink" class="button primary" style="margin-top: 12px; display: inline-block;" data-i18n="creator.downloadButton">Download ICS File</a>
</div> </div>
<div id="nextcloudStatus" class="success-message hidden" aria-live="polite"></div>
<div id="nextcloudError" class="error hidden" aria-live="assertive"></div>
</div> </div>
</div> </div>
</div> </div>
<!-- WINDOWS-HILFE OVERLAY -->
<dialog id="windowsHelpOverlay" class="privacy">
<h2 data-i18n="help.meetingPrivacySettings.windows.title"></h2>
<p data-i18n="help.meetingPrivacySettings.windows.intro"></p>
<h3 data-i18n="help.meetingPrivacySettings.windows.step1Title"></h3>
<p data-i18n="help.meetingPrivacySettings.windows.step1Description"></p>
<h3 data-i18n="help.meetingPrivacySettings.windows.step2Title"></h3>
<p data-i18n="help.meetingPrivacySettings.windows.step2Description"></p>
<h3 data-i18n="help.meetingPrivacySettings.windows.step3Title"></h3>
<p data-i18n="help.meetingPrivacySettings.windows.step3Description"></p>
<p data-i18n="help.meetingPrivacySettings.windows.browserPermissions"></p>
<h3 data-i18n="help.meetingPrivacySettings.windows.goodToKnowTitle"></h3>
<ul>
<li data-i18n="help.meetingPrivacySettings.windows.goodToKnow1"></li>
<li data-i18n="help.meetingPrivacySettings.windows.goodToKnow2"></li>
<li data-i18n="help.meetingPrivacySettings.windows.goodToKnow3"></li>
</ul>
<p data-i18n="help.meetingPrivacySettings.windows.adminSupport"></p>
<div class="help-actions">
<button id="windowsHelpClose" class="button secondary" type="button" data-i18n="help.meetingPrivacySettings.windows.closeButton">
Close
</button>
</div>
</dialog>
<script src="assets/lib/ua-parser.min.js"></script> <script src="assets/lib/ua-parser.min.js"></script>
<script src="assets/launch.js"></script> <script src="assets/launch.js"></script>
</body> </body>