diff --git a/public/content/did:plc:6qyecktefllvenje24fcxnie/ai.syui.rse.admin/self.json b/public/content/did:plc:6qyecktefllvenje24fcxnie/ai.syui.rse.admin/self.json index 0dcf36b..527e685 100644 --- a/public/content/did:plc:6qyecktefllvenje24fcxnie/ai.syui.rse.admin/self.json +++ b/public/content/did:plc:6qyecktefllvenje24fcxnie/ai.syui.rse.admin/self.json @@ -3,11 +3,12 @@ "cid": "bafyreihgv33l3kdbpghum3ul5uugscfoaooyyx7nu3eaiuge4l6pru6je4", "value": { "item": [ - { "id": 0, "name": "i", "text": { "en": "world", "ja": "世界" } }, - { "id": 1, "name": "gordbox", "text": { "en": "The first color in this world", "ja": "この世界の最初の色" } }, - { "id": 2, "name": "silverbox", "text": { "en": "The second color in this world", "ja": "この世界の2番目の色" } }, - { "id": 3, "name": "bluebox", "text": { "en": "The third color in this world. It was once red, but was rewritten by someone", "ja": "この世界の3番目の色。かつては赤だったが、何者かに書き換えられた" } }, - { "id": 4, "name": "copperbox", "text": { "en": "The Third Lost Color of the World", "ja": "この世界の失われた3番目の色" } } + { "id": 0, "name": "a", "text": { "en": "world", "ja": "世界" } }, + { "id": 1, "name": "i", "text": { "en": "The Pillar of Creation at the center of the galaxy stands tall in a place called the Eternal Stone, which is said to tolerate no change. The three attributes of gold, silver, and copper are engraved on it.", "ja": "銀河の中心にある創造の柱。そこにそびえ立つ永遠の石には金、銀、銅というこの世界の3つの属性が刻み込まれている" } }, + { "id": 2, "name": "gordbox", "text": { "en": "The first color in this world", "ja": "この世界の最初の色" } }, + { "id": 3, "name": "silverbox", "text": { "en": "The second color in this world", "ja": "この世界の2番目の色" } }, + { "id": 4, "name": "bluebox", "text": { "en": "The third color in this world. It was once red, but was rewritten by someone", "ja": "この世界の3番目の色。かつては赤だったが、何者かに書き換えられた" } }, + { "id": 5, "name": "copperbox", "text": { "en": "The Third Lost Color of the World", "ja": "この世界から失われた3番目の色" } } ], "$type": "ai.syui.rse.admin", "system": [ diff --git a/public/rse/item/0.webp b/public/rse/item/0.webp index 749a39d..749921d 100644 Binary files a/public/rse/item/0.webp and b/public/rse/item/0.webp differ diff --git a/public/rse/item/1.webp b/public/rse/item/1.webp index 96f265c..749a39d 100644 Binary files a/public/rse/item/1.webp and b/public/rse/item/1.webp differ diff --git a/public/rse/item/2.webp b/public/rse/item/2.webp index f0f8a90..96f265c 100644 Binary files a/public/rse/item/2.webp and b/public/rse/item/2.webp differ diff --git a/public/rse/item/3.webp b/public/rse/item/3.webp index 19f8548..f0f8a90 100644 Binary files a/public/rse/item/3.webp and b/public/rse/item/3.webp differ diff --git a/public/rse/item/4.webp b/public/rse/item/4.webp index 7a698ab..19f8548 100644 Binary files a/public/rse/item/4.webp and b/public/rse/item/4.webp differ diff --git a/public/rse/item/5.webp b/public/rse/item/5.webp new file mode 100644 index 0000000..7a698ab Binary files /dev/null and b/public/rse/item/5.webp differ diff --git a/src/web/components/card.ts b/src/web/components/card.ts index bbc9aba..4a0877c 100644 --- a/src/web/components/card.ts +++ b/src/web/components/card.ts @@ -156,13 +156,9 @@ export function renderCardPage( } } - // Sort by unique first, then rarity (desc), then by id + // Sort by id const sortedGroups = Array.from(cardGroups.values()) - .sort((a, b) => { - if (a.card.unique !== b.card.unique) return a.card.unique ? -1 : 1 - if (b.card.rare !== a.card.rare) return b.card.rare - a.card.rare - return a.card.id - b.card.id - }) + .sort((a, b) => a.card.id - b.card.id) const cardsHtml = sortedGroups.map(({ card, count }) => { return renderCard(card, '/card', count) diff --git a/src/web/components/rse.ts b/src/web/components/rse.ts index df08a79..c487102 100644 --- a/src/web/components/rse.ts +++ b/src/web/components/rse.ts @@ -1,6 +1,16 @@ // RSE display component for ai.syui.rse.user collection import { renderCard, type UserCard, type CardAdminEntry, type CardAdminData } from './card' +export interface RseAdminItem { + id: number + name: string + text: { ja: string; en: string } +} + +export interface RseAdminData { + item: RseAdminItem[] +} + export interface RseItem { id: number cp: number @@ -15,6 +25,18 @@ export interface RseCollection { updatedAt: string } +// Get current language +function getLang(): string { + return localStorage.getItem('preferredLang') || 'ja' +} + +// Get localized text +function getLocalizedText(obj: { ja: string; en: string } | undefined): string { + if (!obj) return '' + const lang = getLang() + return obj[lang as 'ja' | 'en'] || obj.ja || obj.en || '' +} + // Get rarity class from unique flag function getRarityClass(item: RseItem): string { if (item.unique) return 'unique' @@ -99,13 +121,27 @@ function renderCharacterSection( } // Render single item -function renderRseItem(item: RseItem): string { +function renderRseItem(item: RseItem, rseAdminData: RseAdminData | null): string { const rarityClass = getRarityClass(item) const effectsHtml = rarityClass ? `
` : '' + // Get admin entry for this item + const adminEntry = rseAdminData?.item?.find(i => i.id === item.id) + const name = adminEntry?.name || '' + const text = adminEntry ? getLocalizedText(adminEntry.text) : '' + + const infoHtml = (name || text) ? ` +
+
+ ${name} +
+ ${text ? `
${text}
` : ''} +
+ ` : '' + return `
@@ -117,6 +153,7 @@ function renderRseItem(item: RseItem): string {
${item.cp}
+ ${infoHtml}
` } @@ -126,7 +163,8 @@ export function renderRsePage( collection: RseCollection | null, handle: string, userCards: UserCard[] = [], - adminData: CardAdminData | null = null + adminData: CardAdminData | null = null, + rseAdminData: RseAdminData | null = null ): string { const jsonUrl = `/@${handle}/at/collection/ai.syui.rse.user/self` @@ -157,7 +195,7 @@ export function renderRsePage( const charsHtml = sortedChars.map(c => renderCharacterSection(c, userCards, adminData) ).join('') - const itemsHtml = sortedItems.map(i => renderRseItem(i)).join('') + const itemsHtml = sortedItems.map(i => renderRseItem(i, rseAdminData)).join('') return `
diff --git a/src/web/lib/api.ts b/src/web/lib/api.ts index 755efa4..94efc3d 100644 --- a/src/web/lib/api.ts +++ b/src/web/lib/api.ts @@ -728,6 +728,56 @@ export async function getCardAdmin(did: string): Promise { return null } +// RSE admin data types +export interface RseAdminItem { + id: number + name: string + text: { ja: string; en: string } +} + +export interface RseAdminData { + item: RseAdminItem[] + ability: unknown[] + character: unknown[] + system: unknown[] + collection: unknown[] + createdAt: string + updatedAt: string +} + +// Get RSE admin data (ai.syui.rse.admin) +export async function getRseAdmin(did: string): Promise { + const collection = 'ai.syui.rse.admin' + + // Try local first + try { + const res = await fetch(`/content/${did}/${collection}/self.json`) + if (res.ok && isJsonResponse(res)) { + const record = await res.json() + return record.value as RseAdminData + } + } catch { + // Try remote + } + + // Remote fallback + const pds = await getPds(did) + if (!pds) return null + + try { + const host = pds.replace('https://', '') + const url = `${xrpcUrl(host, comAtprotoRepo.getRecord)}?repo=${did}&collection=${collection}&rkey=self` + const res = await fetch(url) + if (res.ok) { + const record = await res.json() + return record.value as RseAdminData + } + } catch { + // Failed + } + return null +} + // Get user's links (ai.syui.at.link) export async function getLinks(did: string): Promise { const collection = 'ai.syui.at.link' diff --git a/src/web/main.ts b/src/web/main.ts index 0ee84a6..4170f9b 100644 --- a/src/web/main.ts +++ b/src/web/main.ts @@ -1,7 +1,7 @@ import './styles/main.css' import './styles/card.css' import './styles/card-migrate.css' -import { getConfig, resolveHandle, getProfile, getPosts, getPost, describeRepo, listRecords, getRecord, getPds, getNetworks, getChatMessages, getCards, getCardAdmin, getRse, getLinks } from './lib/api' +import { getConfig, resolveHandle, getProfile, getPosts, getPost, describeRepo, listRecords, getRecord, getPds, getNetworks, getChatMessages, getCards, getCardAdmin, getRse, getRseAdmin, getLinks } from './lib/api' import { parseRoute, onRouteChange, navigate, type Route } from './lib/router' import { login, logout, handleCallback, restoreSession, isLoggedIn, getLoggedInHandle, getLoggedInDid, deleteRecord, updatePost, updateChat, updateLinks } from './lib/auth' import { validateRecord } from './lib/lexicon' @@ -256,13 +256,14 @@ async function render(route: Route): Promise { // RSE page with character cards const cardCollection = config.cardCollection || 'ai.syui.card.user' const adminDid = config.bot?.did || config.did || did - const [rseData, cards, adminData] = await Promise.all([ + const [rseData, cards, adminData, rseAdminData] = await Promise.all([ getRse(did), getCards(did, cardCollection), - getCardAdmin(adminDid) + getCardAdmin(adminDid), + getRseAdmin(adminDid) ]) const userCards = cards?.card || [] - html += `
${renderRsePage(rseData, handle, userCards, adminData)}
` + html += `
${renderRsePage(rseData, handle, userCards, adminData, rseAdminData)}
` html += `` } else if (route.type === 'link') {