Beépített alkalmazások
Az ElyOS rendszer 6 beépített alkalmazással érkezik, amelyek a rendszer alapvető funkcióit biztosítják. Minden alkalmazás egy egységes keretrendszert használ, amely biztosítja a konzisztens felhasználói élményt és a könnyű bővíthetőséget.
Alkalmazás keretrendszer
Szekció neve “Alkalmazás keretrendszer”Az ElyOS alkalmazások egy egységes keretrendszert használnak, amely biztosítja a konzisztens felhasználói élményt, a könnyű bővíthetőséget és a hatékony állapotkezelést. A keretrendszer központi eleme az AppShell (állapotkezelő logika) és az AppLayout (vizuális struktúra), amelyek együttműködve kezelik a menünavigációt, a dinamikus komponens betöltést és az alkalmazás életciklusát.
Architektúra áttekintés
Szekció neve “Architektúra áttekintés”Minden beépített alkalmazás ugyanazt az architektúrát követi:
┌───────────────────────────────────────────────────────┐│ AppLayout ││ ┌──────────────┐ ┌──────────────────────────────┐ ││ │ │ │ │ ││ │ AppSideBar │ │ AppContentArea │ ││ │ │ │ (dinamikus komponens) │ ││ │ ┌────────┐ │ │ │ ││ │ │ Menu │ │ │ - Vite glob import │ ││ │ │ Items │ │ │ - Lazy loading │ ││ │ └────────┘ │ │ - Props átadás │ ││ │ │ │ │ ││ └──────────────┘ └──────────────────────────────┘ ││ ┌──────────────────────────────┐ ││ │ ActionBar │ ││ │ (opcionális funkciósáv) │ ││ └──────────────────────────────┘ │└───────────────────────────────────────────────────────┘Adatfolyam:
- AppShell kezeli az állapotot (aktív menüpont, komponens, props)
- AppSideBarMenu megjeleníti a menüt és kezeli a kattintásokat
- AppShell.handleMenuItemClick() frissíti az állapotot
- AppContentArea detektálja a változást és betölti az új komponenst
- Komponens renderelődik a props-okkal és beállíthatja az ActionBar-t
Főbb komponensek
Szekció neve “Főbb komponensek”1. AppShell
Szekció neve “1. AppShell”Az AppShell a központi állapotkezelő és logikai réteg minden alkalmazáshoz. Felelős:
- Menü állapot kezelése (aktív menüpont, kinyitott szülők)
- Komponens betöltés koordinálása
- Navigáció kezelése
- Lokalizáció (többnyelvű menü)
- Paraméter kezelés (URL hash alapú navigáció)
Használat:
<script lang="ts"> import { createAppShell } from '$lib/apps/appShell.svelte'; import menuData from './menu.json';
const shell = createAppShell({ appName: 'settings', menuData: menuData as RawMenuItem[] });</script>Főbb funkciók:
menuItems- Lokalizált menüpontok (reaktív)activeMenuItem- Aktív menüpont href-jeactiveComponent- Betöltött komponens nevecomponentProps- Komponensnek átadott propsexpandedParents- Kinyitott szülő menüpontokhandleMenuItemClick(item)- Menüpont kattintás kezelőnavigateTo(component, props, menuHref)- Programozott navigáció
2. AppLayout
Szekció neve “2. AppLayout”Az AppLayout a vizuális keretrendszer, amely összeköti az összes UI elemet:
<AppLayout {shell} namespaces={['settings', 'common']} maxWidthClass="max-w-3xl" sidebarWidth={230} searchable={false}/>Paraméterek:
shell- AppShell példány (kötelező)namespaces- i18n namespace-ek (automatikusan hozzáadja a ‘common’-t)maxWidthClass- Tartalom max szélessége (Tailwind osztály)sidebarWidth- Sidebar szélessége pixelben vagy ‘auto’searchable- Keresés engedélyezése a menübenisPlugin- Plugin mód (komponensek API-n keresztül töltődnek)
Funkciók:
- I18nProvider beállítása
- Sidebar és menü renderelése
- Tartalom terület kezelése
- ActionBar megjelenítése (ha van tartalom)
- Automatikus ActionBar törlés komponens váltáskor
3. AppSideBar és AppSideBarMenu
Szekció neve “3. AppSideBar és AppSideBarMenu”A bal oldali sidebar tartalmazza a navigációs menüt és biztosítja az összecsukható/kinyitható funkcionalitást.
AppSideBar funkciók:
- Összecsukható sidebar (ChevronLeft/Right gomb)
- Állapot mentése localStorage-ba (
app-sidebar-collapsed-${appName}) - Konfigurálható szélesség (pixelben vagy ‘auto’)
- Smooth animációk (CSS transitions)
- Gradient háttér (light/dark mode támogatás)
AppSideBarMenu funkciók:
- Hierarchikus menü struktúra: Szülő-gyerek kapcsolatok támogatása
- Összecsukható szülők: Chevron ikon animációval
- Aktív menüpont kiemelés: Vizuális feedback
- Keresés: Opcionális keresőmező a menüpontok szűréséhez
- Smooth animációk: CSS Grid alapú (
grid-template-rows: 0fr → 1fr) - Ikon támogatás: UniversalIcon komponens (Lucide/Phosphor)
- Separator támogatás: Elválasztó vonalak a menüben
- Rejtett menüpontok szűrése:
hidden: truemezővel
Menü állapot kezelés:
// Kinyitott menüpontoklet expandedItems = $state<Set<string>>(new Set());
// Felhasználó által manuálisan bezárt menüpontok// (ezeket nem nyitjuk ki automatikusan)let manuallyClosed = $state<Set<string>>(new Set());Automatikus kinyitás:
- Kereséskor: Minden szülő menüpont kinyílik, amely tartalmaz találatot
- Aktív menüpont: A szülő menüpontok automatikusan kinyílnak
- Inicializáláskor: Az
initialExpandedParentsprop alapján
Keresés működése:
- Felhasználó beír egy keresőszót
filterItemsBySearch()rekurzívan szűri a menüpontokatgetExpandedParentsForSearch()meghatározza a kinyitandó szülőketexpandedItemsfrissül, a menü animáltan kinyílikmanuallyClosedtörlődik (keresés közben minden látható)
Menüpont kattintás:
function handleClick(item: MenuItem, event: MouseEvent) { event.preventDefault(); onItemClick?.(item); // AppShell.handleMenuItemClick()}4. AppContentArea
Szekció neve “4. AppContentArea”A tartalmi terület dinamikusan tölti be és rendereli a komponenseket. Ez a keretrendszer egyik legfontosabb része, amely biztosítja a lazy loading-ot és a hatékony komponens kezelést.
Működési elv:
-
Vite glob import az összes app komponenshez:
const appComponentModules = import.meta.glob('/src/apps/*/components/*.svelte');// Eredmény: { '/src/apps/settings/components/ProfileSettings.svelte': () => Promise<Module> } -
Komponens betöltés igény szerint (lazy loading):
const moduleKey = `/src/apps/${appName}/components/${componentName}.svelte`;const moduleLoader = appComponentModules[moduleKey];if (!moduleLoader) {throw new Error(`Komponens nem található: ${componentName}`);}const module = await moduleLoader();loadedComponent = module.default; -
Komponens renderelés props-okkal:
{#if loadedComponent}{@const Component = loadedComponent}<Component {...props} />{/if}
Reaktív betöltés:
$effect(() => { const currentComponent = component;
// Guard: ne töltsünk be újra, ha már töltünk vagy már be van töltve if (isLoadingComponent) return;
if (currentComponent && currentComponent !== lastLoadedComponent) { untrack(() => loadComponent(currentComponent)); } else if (!currentComponent) { untrack(() => { loadedComponent = null; error = null; lastLoadedComponent = null; }); }});Állapotok:
loading- Betöltés folyamatban (spinner megjelenítése)error- Hiba történt (hibaüzenet megjelenítése)loadedComponent- Betöltött komponens (renderelés)placeholder- Nincs kiválasztott menüpont (üres állapot)isLoadingComponent- Guard flag (dupla betöltés elkerülése)lastLoadedComponent- Utoljára betöltött komponens neve (cache)
Plugin támogatás:
Plugin komponensek API-n keresztül töltődnek be és Web Component-ként renderelődnek:
async function loadPluginComponent(componentName: string) { // 1. Komponens kód lekérése API-n keresztül const response = await fetch(`/api/plugins/${appName}/components/${componentName}`); const code = await response.text();
// 2. Kód futtatása (factory function létrehozása) const script = document.createElement('script'); script.textContent = code; document.head.appendChild(script);
// 3. Factory meghívása és custom element tag name lekérése const factoryName = `${appName.replace(/-/g, '_')}_Component_${componentName}`; const componentFactory = window[factoryName]; const componentInfo = componentFactory();
// 4. Custom element renderelése loadedComponent = { __pluginTagName: componentInfo.tagName, __pluginProps: props };}Hibakezelés:
- Komponens nem található → Hibaüzenet megjelenítése
- Betöltési hiba → Konzol log + hibaüzenet
- Plugin betöltési hiba → Factory function hiányzik
Performance optimalizáció:
- Lazy loading: Csak az aktív komponens töltődik be
- Cache:
lastLoadedComponentalapján elkerüljük az újratöltést - Guard flag:
isLoadingComponentmegakadályozza a dupla betöltést untrack(): Elkerüljük a végtelen ciklusokat
5. ActionBar
Szekció neve “5. ActionBar”Az ActionBar egy opcionális funkciósáv az alkalmazás alján, ahol a komponensek gombokat és egyéb vezérlőket helyezhetnek el.
Használat komponensben:
<script lang="ts"> import { getActionBar } from '$lib/apps/actionBar.svelte'; import { Button } from '$lib/components/ui/button';
const actionBar = getActionBar();
// ActionBar tartalom beállítása actionBar.set(myActions);</script>
{#snippet myActions()} <Button onclick={handleSave}>Mentés</Button> <Button variant="outline" onclick={handleCancel}>Mégse</Button>{/snippet}API:
content- Aktuális snippet (null ha nincs)set(snippet)- Snippet beállításaclear()- Tartalom törlése
Automatikus törlés:
Az AppLayout automatikusan törli az ActionBar tartalmát komponens váltáskor, így nem kell manuálisan kezelni.
Menü struktúra (menu.json)
Szekció neve “Menü struktúra (menu.json)”Minden alkalmazás rendelkezik egy menu.json fájllal, amely definiálja a menüpontokat. A menü hierarchikus struktúrát támogat, lokalizálható és jogosultság alapú szűrést biztosít.
Példa menü struktúra:
[ { "labelKey": "menu.profile", "href": "#profile", "icon": "User", "component": "ProfileSettings" }, { "labelKey": "menu.desktop", "href": "#", "icon": "Monitor", "children": [ { "labelKey": "menu.general", "href": "#desktop", "icon": "Settings", "component": "DesktopSettings" }, { "labelKey": "menu.background", "href": "#background", "icon": "Image", "component": "BackgroundSettings" } ] }, { "separator": true }, { "labelKey": "menu.advanced", "href": "#advanced", "icon": "Settings", "component": "AdvancedSettings", "requiredPermission": "settings.advanced.view", "hideWhen": "notDevMode" }]Menüpont mezők:
| Mező | Típus | Kötelező | Leírás |
|---|---|---|---|
labelKey | string | Igen | Fordítási kulcs (pl. “menu.profile” → “settings.menu.profile”) |
href | string | Igen | URL hash (pl. “#profile”). Szülő menüpontoknál lehet ”#“ |
icon | string | Nem | Ikon neve (Lucide vagy Phosphor, pl. “User”, “Settings”) |
component | string | Nem* | Komponens neve a components/ mappából (pl. “ProfileSettings”) |
children | array | Nem | Almenüpontok tömbje (hierarchikus menü) |
separator | boolean | Nem | Elválasztó vonal (true esetén más mező nem kell) |
requiredPermission | string | Nem | Szükséges jogosultság (pl. “settings.advanced.view”) |
hideWhen | string | Nem | Feltételes elrejtés (pl. “notDevMode”, “singleLocale”) |
props | object | Nem | Komponensnek átadott props (pl. { "mode": "advanced" }) |
hidden | boolean | Nem | Menüpont elrejtése (true esetén nem jelenik meg) |
* A component mező kötelező, ha a menüpontnak nincs children mezője és nem separator.
Menü lokalizáció:
A menü automatikusan lokalizálódik a localizeMenuItems() függvény segítségével:
// Namespace: appName (pl. 'settings')// Kulcs: labelKey (pl. 'menu.profile')// Teljes kulcs: 'settings.menu.profile'
const menuItems = $derived.by(() => { void translationStore.currentLocale; return localizeMenuItems(appName, rawMenuData);});Fordítások hozzáadása:
INSERT INTO translations (namespace, key, locale, value) VALUES ('settings', 'menu.profile', 'hu', 'Profil'), ('settings', 'menu.profile', 'en', 'Profile'), ('settings', 'menu.desktop', 'hu', 'Asztal'), ('settings', 'menu.desktop', 'en', 'Desktop');Hierarchikus menü:
A children mező használatával többszintű menüt hozhatunk létre:
{ "labelKey": "menu.desktop", "href": "#", "icon": "Monitor", "children": [ { "labelKey": "menu.general", "href": "#desktop", "component": "DesktopSettings" } ]}Fontos szabályok:
- Szülő menüpont href-je: Ha van
children, akkor ahreflehet ”#” (nem navigál) - Szülő menüpont component-je: Opcionális, ha van, akkor a szülőre kattintva is betöltődik
- Gyerek menüpontok: Mindig kell
componentmező - Separator: Csak
separator: truemező kell, más nem - Ikon: Opcionális, de ajánlott a jobb UX érdekében
Jogosultság alapú szűrés:
A requiredPermission mező használatával korlátozhatjuk a menüpontok láthatóságát:
{ "labelKey": "menu.advanced", "href": "#advanced", "component": "AdvancedSettings", "requiredPermission": "settings.advanced.view"}Ha a felhasználónak nincs meg a szükséges jogosultsága, a menüpont automatikusan elrejtésre kerül.
Feltételes elrejtés:
A hideWhen mező használatával feltételesen rejthetünk el menüpontokat:
{ "labelKey": "menu.language", "href": "#language", "component": "LanguageSettings", "hideWhen": "singleLocale"}Támogatott értékek:
"singleLocale"- Elrejti, ha csak egy nyelv van a rendszerben"notDevMode"- Elrejti, ha nem fejlesztői módban fut a rendszer
Komponens életciklus
Szekció neve “Komponens életciklus”-
Alkalmazás indítása
- AppShell létrehozása
- Menü betöltése és lokalizáció
- Alapértelmezett menüpont kiválasztása (első component-tel rendelkező)
-
Menüpont kattintás
handleMenuItemClick(item)meghívásaactiveComponentfrissítésecomponentPropsbeállítása- URL hash frissítése (section paraméter)
-
Komponens betöltés
- AppContentArea
$effecttriggerelése - Vite glob import használata
- Aszinkron komponens betöltés
- Komponens renderelés props-okkal
- AppContentArea
-
ActionBar kezelés
- Komponens beállítja az ActionBar-t (
actionBar.set()) - ActionBar megjelenik az alkalmazás alján
- Komponens váltáskor automatikus törlés
- Komponens beállítja az ActionBar-t (
-
Navigáció
- URL hash változás (
#profile→#security) - AppShell
$effectdetektálja a változást - Megfelelő menüpont aktiválása
- Komponens betöltés
- URL hash változás (
Többnyelvűség
Szekció neve “Többnyelvűség”A menü automatikusan lokalizálódik a localizeMenuItems függvény segítségével:
// Namespace: appName (pl. 'settings')// Kulcs: labelKey (pl. 'menu.profile')// Teljes kulcs: 'settings.menu.profile'
const menuItems = $derived.by(() => { void translationStore.currentLocale; return localizeMenuItems(appName, rawMenuData);});Fordítások:
INSERT INTO translations (namespace, key, locale, value) VALUES ('settings', 'menu.profile', 'hu', 'Profil'), ('settings', 'menu.profile', 'en', 'Profile');Jogosultság ellenőrzés
Szekció neve “Jogosultság ellenőrzés”A menüpontok automatikusan szűrődnek a felhasználó jogosultságai alapján:
// menu.json{ "labelKey": "menu.advanced", "requiredPermission": "settings.advanced.view", // ...}Ha a felhasználónak nincs meg a szükséges jogosultsága, a menüpont nem jelenik meg.
URL paraméterek
Szekció neve “URL paraméterek”Az alkalmazások támogatják a section paramétert az URL hash-ben:
// Ablak megnyitása section paraméterrelwindowManager.openWindow('settings', 'Beállítások', settingsApp, { section: 'profile' // → #profile menüpont aktiválása});Az AppShell automatikusan detektálja a section paramétert és aktiválja a megfelelő menüpontot.
Beépített alkalmazások listája
Szekció neve “Beépített alkalmazások listája”1. Beállítások (Settings)
Szekció neve “1. Beállítások (Settings)”Rendszer és felhasználói beállítások kezelése.
Főbb funkciók:
- Profil beállítások
- Megjelenés (téma, nyelv)
- Biztonság (jelszó, 2FA)
- Téma előnézetek
Menü struktúra: 3 fő kategória, 11 komponens
2. Felhasználók (Users)
Szekció neve “2. Felhasználók (Users)”Felhasználók, csoportok, szerepkörök és jogosultságok kezelése.
Főbb funkciók:
- Felhasználó CRUD
- Csoport kezelés
- Szerepkör kezelés
- Jogosultság kezelés
- Erőforrás kezelés
Jogosultság: Csak admin felhasználók
3. Chat
Szekció neve “3. Chat”Valós idejű üzenetküldő rendszer Socket.IO-val.
Főbb funkciók:
- Valós idejű üzenetküldés
- Online/offline státusz
- Gépelés jelzés
- Olvasatlan üzenetek
- Toast értesítések
Technológia: Socket.IO + REST API fallback
4. Naplók (Log)
Szekció neve “4. Naplók (Log)”Rendszer és hibanaplók megjelenítése.
Főbb funkciók:
- Hibanaplók táblázatos megjelenítése
- Szűrés log szint szerint
- Szűrés forrás szerint
- Rendezés és lapozás
Jogosultság: log.error.view (admin)
5. Plugin Manager
Szekció neve “5. Plugin Manager”Plugin telepítés, kezelés és eltávolítás.
Főbb funkciók:
- Plugin feltöltés (.elyospkg)
- Plugin validáció
- Telepített pluginek listája
- Plugin részletek
- Plugin eltávolítás
Jogosultság: plugin.manual.install (admin)
6. Súgó (Help)
Szekció neve “6. Súgó (Help)”Kontextusfüggő súgó rendszer.
Állapot: Fejlesztés alatt
Tervezett funkciók:
- Kontextusfüggő súgó
- Súgó gomb az ablakokban
- Keresés a súgó tartalomban
- Többnyelvű tartalom
Új alkalmazás létrehozása
Szekció neve “Új alkalmazás létrehozása”1. Alapstruktúra
Szekció neve “1. Alapstruktúra”apps/my-app/├── index.svelte # Fő belépési pont├── icon.svg # Alkalmazás ikon├── menu.json # Menü definíció├── my-app.remote.ts # Server actions├── components/ # UI komponensek│ ├── Component1.svelte│ └── Component2.svelte└── stores/ # Állapotkezelés (opcionális) └── myAppStore.svelte.ts2. index.svelte létrehozása
Szekció neve “2. index.svelte létrehozása”<script lang="ts"> import type { RawMenuItem } from '$lib/types/menu'; import { AppLayout } from '$lib/components/shared'; import { createAppShell } from '$lib/apps/appShell.svelte'; import menuData from './menu.json';
const shell = createAppShell({ appName: 'my-app', menuData: menuData as RawMenuItem[] });</script>
<AppLayout {shell} namespaces={['my-app']} />3. menu.json létrehozása
Szekció neve “3. menu.json létrehozása”[ { "labelKey": "menu.overview", "href": "#overview", "icon": "Home", "component": "Overview" }, { "labelKey": "menu.settings", "href": "#settings", "icon": "Settings", "component": "Settings" }]4. Komponensek létrehozása
Szekció neve “4. Komponensek létrehozása”<script lang="ts"> import { getActionBar } from '$lib/apps/actionBar.svelte'; import { Button } from '$lib/components/ui/button';
const actionBar = getActionBar(); actionBar.set(actions);
function handleAction() { console.log('Action clicked'); }</script>
{#snippet actions()} <Button onclick={handleAction}>Művelet</Button>{/snippet}
<div class="title-block"> <h2>Áttekintés</h2> <h3>Alkalmazás áttekintő oldal</h3></div>
<p>Tartalom...</p>5. Fordítások hozzáadása
Szekció neve “5. Fordítások hozzáadása”-- packages/database/src/seeds/translations/my-app.tsINSERT INTO translations (namespace, key, locale, value) VALUES ('my-app', 'title', 'hu', 'Saját Alkalmazás'), ('my-app', 'title', 'en', 'My App'), ('my-app', 'menu.overview', 'hu', 'Áttekintés'), ('my-app', 'menu.overview', 'en', 'Overview');6. Alkalmazás regisztrálása
Szekció neve “6. Alkalmazás regisztrálása”-- packages/database/src/seeds/platform/apps.ts{ appId: 'my-app', appType: 'builtin', name: { hu: 'Saját Alkalmazás', en: 'My App' }, description: { hu: 'Leírás', en: 'Description' }, version: '1.0.0', icon: 'icon.svg', category: 'productivity', isActive: true}Best practice-ek
Szekció neve “Best practice-ek”- Egységes menü struktúra: Használj értelmes href-eket (#profile, #security)
- Komponens nevek: PascalCase (ProfileSettings.svelte)
- ActionBar használat: Csak akkor, ha szükséges (mentés, mégse gombok)
- Fordítások: Mindig adj meg magyar és angol verziót
- Jogosultságok: Használj
requiredPermission-t érzékeny menüpontoknál - Ikon választás: Használj konzisztens ikonokat (Lucide vagy Phosphor)
- Props átadás: Használd a
propsmezőt a menu.json-ban - Hibakezelés: Kezeld le a komponens betöltési hibákat
- Loading állapot: Jelenítsd meg a betöltési állapotot
- Responsive design: Tervezz mobil nézetekre is
Hibaelhárítás
Szekció neve “Hibaelhárítás”Komponens nem töltődik be
Szekció neve “Komponens nem töltődik be”Probléma: “Komponens nem található” hiba.
Megoldás:
- Ellenőrizd a komponens nevét a menu.json-ban
- Ellenőrizd, hogy a komponens létezik-e a
components/mappában - Nézd meg a böngésző konzolt részletes hibáért
Menü nem jelenik meg
Szekció neve “Menü nem jelenik meg”Probléma: A sidebar üres.
Megoldás:
- Ellenőrizd a menu.json szintaxisát
- Nézd meg a fordításokat (namespace + labelKey)
- Ellenőrizd a jogosultságokat
ActionBar nem jelenik meg
Szekció neve “ActionBar nem jelenik meg”Probléma: Az ActionBar nem látszik.
Megoldás:
- Ellenőrizd, hogy meghívtad-e az
actionBar.set()-et - Nézd meg, hogy a snippet helyesen van-e definiálva
- Ellenőrizd, hogy az AppLayout rendereli-e az ActionBar-t