@hexah/create-skin
v0.1.7
Published
Scaffolder skórki Hexah (zdalny kontener Module Federation, harness dev z HMR).
Maintainers
Readme
@hexah/create-skin
Scaffolder skórki Hexah — generuje projekt zdalnej skórki Module Federation (rspack +
@hexah/skin-sdk) z gotowym workflowem dev (HMR) i podłączeniem do hosta na środowisku
testowym.
Kontrakt skórki (hooki danych, UI, sloty) dostarcza paczka
@hexah/skin-sdk — wygenerowany projekt
ma ją już w zależnościach (npm install @hexah/skin-sdk, by dodać do istniejącego).
Użycie
npm create @hexah/skin moja-skorka # albo: npx @hexah/create-skin moja-skorka
cd moja-skorka
npm install
npm run dev # rspack serve z HMRPodłączenie do test.kf2.pl
Skórkę ładuje aplikacja działająca pod publicznym adresem (HTTPS), a przeglądarka nie pozwala
takiej stronie pobrać zasobu z localhost. Wystaw więc dev server pod publicznym URL przez
tunel:
npx cloudflared tunnel --url http://localhost:3001 # → https://<rand>.trycloudflare.com<rand>.trycloudflare.com to adres, który cloudflared wypisze w terminalu — skopiuj go
(zmienia się po każdym restarcie tunelu). Następnie otwórz https://test.kf2.pl, zaloguj się i
w konsoli przeglądarki (F12 → Console) wklej:
localStorage.setItem('hexah.skinRemoteAllowedOrigins', JSON.stringify(['https://<rand>.trycloudflare.com']))
localStorage.setItem('hexah.skinRemote', JSON.stringify({
name: 'moja_skorka', // = nazwa kontenera (uniqueName w rspack.config.js)
entry: 'https://<rand>.trycloudflare.com/remoteEntry.js',
template: 'sdkremote',
screens: { report: './ReportScreen' },
apiVersion: '0.11.0',
}))Odśwież stronę, w Ustawieniach Gry wybierz motyw „SDK Remote (zdalna skórka)" i wejdź na
ekran zgłoszeń — zobaczysz swój ReportScreen na realnych danych. Po każdej zmianie w src/
odśwież stronę (remote przebuduje się przez HMR).
Budowanie motywu — co masz do dyspozycji
Importuj wyłącznie z @hexah/skin-sdk (+ react i @mui/* jako współdzielone singletony
hosta). Nie masz dostępu do kodu gry — host dostarcza w runtime read-modele danych, usługi
platformy, prymitywy UI i ciężkie sloty. Kontrakt jest wersjonowany (SKIN_API_VERSION,
obecnie 0.11.0); deklarowane apiVersion musi mieć ten sam major i minor ≤ host.
Trzy sposoby na skórkę
- Pojedynczy ekran — komponent zarejestrowany pod kluczem z
SCREEN_KEYS. Wrspack.config.jswystawiasz go (exposes), w configulocalStoragemapujesz na klucz (screens: { report: './ReportScreen' }). Host renderuje go w miejscu danej podstrony. - Rama całej strony (layout/chrome) — komponent dostaje
children(aktywny ekran) i owija go własnym paskiem/nawigacją/stopką. Wystawiasz np../SkinFramei podajesz w configulayout: './SkinFrame'. Możesz dać samą ramę, same ekrany, albo jedno i drugie. Tutorial:docs/skin-sdk/tutorial-frame.md. - Rozmieszczanie slotów — ciężkie fragmenty z logiką/danymi (np. mapa świata, statystyki
zgłoszeń) renderuje host; Ty pobierasz je gotowe przez
useSlots()i tylko ustawiasz w layoutcie.
Konwencja read-modeli
Każdy read-model to hook zwracający obiekt metod. Metoda przyjmuje jeden obiekt opcji z
polem callback (odbiera odpowiedź socketu) — pozostałe pola lecą do hosta jako data:
const reports = useReports()
reports.list({ page: 1, pageSize: 10, callback: (ret) => {
if (ret?.error) return
setIssues(ret.issues) // kształt zwrotki zależy od metody
}})Hooki danych (jeden na podstronę menu)
| Hook | Podstrona | Metody |
| --- | --- | --- |
| useArticles | Terminal / Aktualności / Kurier | list, getBySlug, recordView, markKurierRead, listComments, createComment, toggleReaction |
| useGptThreads | Harold (GPT) | list, get, create, remove, sendMessage |
| useTales | Operacje (opowieści) | list, search, get, getFavorites, getPreferences, togglePreference, join, leave, toggleSubscribe, checkSubscribe |
| useInn | Mesa (karczma) | listTales, getMembersCount |
| useTown | Metropolia (budynki) | bankSend, innRest, innBuyTravelerKit, healerPlantsList, healerHeal, voltarToolsList, voltarBuy, hallTypes, hallPriceList, hallBuy, workshopPlansList, workshopPlanGet, workshopCraftItem, forgePlansList, forgePlanGet, forgeCraftMaterial, workPerform |
| useGuild | Frakcje (gildie) | create, join, list, search, get, sendCrabs, headquartersRestInfo, headquartersRest, leave, getPermissions, specialActions, getProperties, hexmapPopulationTotal, hexmapMappingActivityMonth, provinceInfo, publicStats, hexmapPopulations, hexmapPopulationsByMember, warehouseGet, warehouseDeposit, warehouseYardWithdraw, materialsGet, materialsDonate, materialsDistribute |
| useFamily | Dynastie (rody) | create, list, get, getProperties, choseName, invite, acceptInvitation, removeMember, editMember, editSpouse, transferOwner, setPermission, changeDescription, npcAccessGrant, npcAccessRevoke, npcAccessList, depositCrabs, depositInfluencePoints |
| useVillages | Kolonie (osady) | found, takeAbandoned, get, list, buildCreate, buildCost, buildUpgrade, buildDemolish, buildMove, buildResetAll, depositCrabs, withdrawCrabs, sendResourcesToGuild, abandon, rebellionResolve, taxesSet, governorAssign, techniqueList, techniqueUpgrade, hexDecorationsUpdate, nameUpdate |
| useShop | Zaopatrzenie (sklep) | getOffers, getItems, buyItem, startPayment |
| useRanking | Notowania (rankingi) | ranking, rankingExplorer |
| useAccountProfile | Profil (konto) | getAccountInfo, getBlockedList, blockUser, unblockUser, linkDiscord, getGlobalStats |
| useSettings | Konfiguracja | updateSetting, setInvisibleOnline, updateNewsletterSubscription |
| useReports | Zgłoszenia (bugtracker) | list, markAllRead, setIssueNotification |
Read-modele stanu (czytają aktywny stan, bez argumentów)
useShard() · useShardUser() · useCharacter() · useAccount() · usePageData() ·
useOnlineCharacters() — zwracają bieżące dane świata/postaci/konta (lub null, gdy brak).
Usługi platformy
useSnackbar()→{ notify }—notify([{ severity, message }])pokazuje powiadomienie hosta.useDialog()→{ open, close }— modal hosta (open(config)/close()).useGamePageShell({ title })— ustawia tytuł/powłokę strony (wołaj wuseMemo, jak w przykładzie).
Prymitywy UI (motywowalne komponenty MUI hosta)
PageBox (kontener strony, pełna wysokość) · HexahCharacterSection · AngularPanel ·
StandardButton (tekst ≤ 16 znaków) · RoundAvatar · DataList (lista wierszy z opisem/akcją) ·
HexahChip · HexahPagination + HexahPaginationItem. Poza nimi używaj zwykłego @mui/material
(Box, Typography, …) — to też instancja hosta. Importuj z barrela
(import { Box, Typography } from '@mui/material'), nie z subpath (@mui/material/Box) — host
współdzieli jako singleton tylko barrel, więc subpath nie zbuduje się w skórce.
Sloty (host renderuje, skórka rozmieszcza)
import { useSlots, SLOT_KEYS } from '@hexah/skin-sdk'
const slots = useSlots()
const WorldMap = slots[SLOT_KEYS.WORLD_MAP]
return WorldMap ? <WorldMap /> : null // łagodna degradacja, gdy host nie dostarcza slotuZarejestrowane: WORLD_MAP (Mapa Sektora — mapa świata + ruiny), ISSUE_STATS (statystyki
zgłoszeń). Zarezerwowane (jeszcze bez komponentu): GAME_PAGE, ISSUE_RICH_CONTENT,
ARTICLE_COMMENTS, ARTICLE_ISSUE_GRID.
Stałe i utile
SCREEN_KEYS— klucze ekranów:ARTICLE,KURIER,KURIER_REDAKCJA,KURIER_SKRZYNKA,GPT_LIST,GPT_THREAD,REPORT,REPORT_DETAIL,ARTICLE_DETAIL.SLOT_KEYS— klucze slotów (jak wyżej).DEFAULT_TEMPLATE·SKIN_API_VERSION.- Utile nawigacji ekranu zgłoszeń (czyste funkcje):
buildReportListHref,buildReportDetailHref,buildReportBackHref,parseReportListPage,parseReportStateFilter,parseReportTypeFilter,parseReportMineFilter,parseReportPlannedFilter; style chipów:getStateChipSx,getIssueTypeChipSx.
Pełna mapa „podstrona menu → hook/slot" i decyzje projektowe:
docs/skin-sdk/data-hooks.md.
Co generuje
Gotowy projekt skórki:
rspack.config.js—ModuleFederationPluginwystawiający ramę i ekrany, paczki współdzielone z hostem (import: false), dev server na:3001(CORS +allowedHosts).src/SkinFrame.jsx,src/ReportScreen.jsx,src/ArticleScreen.jsx,src/GptListScreen.jsx— gotowe wzorce (rama całej strony + ekrany) na@hexah/skin-sdk.src/examples/— referencja: wywołania pozostałych read-modeli danych i slotów (do kopiowania).AGENTS.md+CLAUDE.md— zasady i granice dla asystentów AI (import tylko z SDK, brak dostępu do kodu/backendu gry, współdzielone singletony). AI czyta je automatycznie.package.json,.gitignore,README.mdz instrukcją podłączenia do środowiska testowego.
Nazwa kontenera Module Federation jest automatycznie sanityzowana z nazwy katalogu do poprawnego identyfikatora.
Licencja
MIT.
