aidol 1.4.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("@aioia/core"),n=require("./lead-DGummrgU.js"),o=require("zod"),h=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function m(t){if(typeof t!="string"||!h.test(t))throw new Error(`Invalid resource ID: ${String(t)}`)}class b extends c.BaseCrudRepository{constructor(){super(...arguments),this.resource="aidols"}getDataSchema(){return n.aidolSchema}async createAIdol(e){const a=this.apiService.buildUrl(this.resource),i=await this.apiService.request(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});return n.aidolCreateResponseSchema.parse(i)}async getOne(e,a){m(e.id);const i=`${this.apiService.buildUrl(this.resource)}/${e.id}`,r=await this.apiService.request(i,a);return{data:n.aidolSchema.parse(r)}}async update(e){m(e.id);const a=`${this.apiService.buildUrl(this.resource)}/${e.id}`,i=await this.apiService.request(a,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(e.variables)});return{data:n.aidolSchema.parse(i)}}async generateImage(e,a){const i=this.apiService.buildUrl(`${this.resource}/images`),r=await this.apiService.request(i,{...a,method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});return this.validateResponse(r,n.imageGenerationResponseSchema)}}const y=o.z.object({messageId:o.z.string(),content:o.z.string()});class T extends c.BaseCrudRepository{constructor(){super(...arguments),this.resource="chatrooms"}getDataSchema(){return n.chatroomSchema}async getMessages(e,a,i){const r=new URLSearchParams;a?.limit!==void 0&&r.set("limit",a.limit.toString()),a?.offset!==void 0&&r.set("offset",a.offset.toString());const l=r.toString(),d=this.apiService.buildUrl(`${this.resource}/${e}/messages${l?`?${l}`:""}`),u=await this.apiService.request(d,i);return this.validateResponse(u,o.z.array(n.messageSchema))}async sendMessage(e,a,i){const r=this.apiService.buildUrl(`${this.resource}/${e}/messages`),l=await this.apiService.request(r,{...i,method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({content:a,senderType:"user"})});return this.validateResponse(l,n.messageSchema)}async generateResponse(e,a,i){const r=this.apiService.buildUrl(`${this.resource}/${e}/companions/${a}/response`),l=await this.apiService.request(r,{...i,method:"POST",headers:{"Content-Type":"application/json"}});return this.validateResponse(l,y)}}class f extends c.BaseCrudRepository{constructor(){super(...arguments),this.resource="companions"}getDataSchema(){return n.companionSchema}async generateImage(e,a){const i=this.apiService.buildUrl(`${this.resource}/images`),r=await this.apiService.request(i,{...a,method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});return this.validateResponse(r,n.imageGenerationResponseSchema)}}class P{constructor(e){this.apiService=e,this.resource="leads"}async create(e){const a=this.apiService.buildUrl(this.resource),i=await this.apiService.request(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});return n.leadResponseSchema.parse(i)}}const p="aidol_chatroom_ids",C=o.z.record(o.z.string(),o.z.string()),s=()=>{if(typeof window>"u")return{};const t=localStorage.getItem(p);if(!t)return{};try{const e=JSON.parse(t),a=C.safeParse(e);return a.success?a.data:(console.warn("[LocalChatroomIdsRepository] Invalid data, resetting"),{})}catch(e){if(e instanceof SyntaxError)return console.warn("[LocalChatroomIdsRepository] Failed to parse, resetting"),{};throw e}},g=t=>{if(typeof window>"u")throw new Error("LocalChatroomIdsRepository write operations can only be called on the client side.");localStorage.setItem(p,JSON.stringify(t))},S={getChatroomId(t){return s()[t]??null},setChatroomId(t,e){const a=s();a[t]=e,g(a)},removeChatroomId(t){const e=s();delete e[t],g(e)}},R={close:"Close"},w={header:"Position Assignment",title:"Assign positions to each member!",subtitle:"Tap to assign a position",unassigned:"Unassigned",mainVocal:"Main Vocal",subVocal:"Sub Vocal",mainDancer:"Main Dancer",subDancer:"Sub Dancer",mainRapper:"Main Rapper",subRapper:"Sub Rapper",assignedTo:"{{position}}({{name}})",assign:"Assign",confirm:"Plan Group",needMore:"Assign positions to {{count}} more members",error:{load:"Failed to load members",update:"Failed to assign position"}},v={title:"Debut Member Casting",infoBanner:{title:"Cast your trainees",description:"The idol characters (face, voice, video) in this content are original virtual characters created by AI and are not based on any real person, including idols and celebrities."},tabs:{boy:"Boy Group",girl:"Girl Group",mixed:"Mixed Group"},newMember:{prompt:"Can't find a trainee you like?",subPrompt:"Try casting a new member"},addMember:"New Member",abilities:{title:"Abilities",vocal:"Vocal",dance:"Dance",rap:"Rap",visual:"Visual",stamina:"Stamina",charm:"Charm"},castButton:"Cast",castComplete:{title:"Casting Complete!",description:"The trainee has joined the group",viewBoard:"View Casting Board"}},M="Share",k="URL copied!",$={header:"Final Debut Lineup",title:"My Casted Trainees!",subtitle:"Tap to remove",browse:"Browse Trainees",confirm:"Confirm Debut!",needMore:"Cast {{count}} more to create a group!",delete:"Remove",deleted:"Removed successfully.",empty:{title:`Final Debut Lineup
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("@aioia/core"),t=require("./lead-DGummrgU.js"),o=require("zod"),h=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function m(n){if(typeof n!="string"||!h.test(n))throw new Error(`Invalid resource ID: ${String(n)}`)}class b extends c.BaseCrudRepository{constructor(){super(...arguments),this.resource="aidols"}getDataSchema(){return t.aidolSchema}async createAIdol(e){const a=this.apiService.buildUrl(this.resource),i=await this.apiService.request(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});return t.aidolCreateResponseSchema.parse(i)}async getOne(e,a){m(e.id);const i=`${this.apiService.buildUrl(this.resource)}/${e.id}`,r=await this.apiService.request(i,a);return{data:t.aidolSchema.parse(r)}}async update(e){m(e.id);const a=`${this.apiService.buildUrl(this.resource)}/${e.id}`,i=await this.apiService.request(a,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(e.variables)});return{data:t.aidolSchema.parse(i)}}async generateImage(e,a){const i=this.apiService.buildUrl(`${this.resource}/images`),r=await this.apiService.request(i,{...a,method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});return this.validateResponse(r,t.imageGenerationResponseSchema)}}const y=o.z.object({messageId:o.z.string(),content:o.z.string()});class T extends c.BaseCrudRepository{constructor(){super(...arguments),this.resource="chatrooms"}getDataSchema(){return t.chatroomSchema}async getMessages(e,a,i){const r=new URLSearchParams;a?.limit!==void 0&&r.set("limit",a.limit.toString()),a?.offset!==void 0&&r.set("offset",a.offset.toString());const l=r.toString(),d=this.apiService.buildUrl(`${this.resource}/${e}/messages${l?`?${l}`:""}`),u=await this.apiService.request(d,i);return this.validateResponse(u,o.z.array(t.messageSchema))}async sendMessage(e,a,i){const r=this.apiService.buildUrl(`${this.resource}/${e}/messages`),l=await this.apiService.request(r,{...i,method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({content:a,senderType:"user"})});return this.validateResponse(l,t.messageSchema)}async generateResponse(e,a,i){const r=this.apiService.buildUrl(`${this.resource}/${e}/companions/${a}/response`),l=await this.apiService.request(r,{...i,method:"POST",headers:{"Content-Type":"application/json"}});return this.validateResponse(l,y)}}class f extends c.BaseCrudRepository{constructor(){super(...arguments),this.resource="companions"}getDataSchema(){return t.companionSchema}async generateImage(e,a){const i=this.apiService.buildUrl(`${this.resource}/images`),r=await this.apiService.request(i,{...a,method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});return this.validateResponse(r,t.imageGenerationResponseSchema)}}class P{constructor(e){this.apiService=e,this.resource="leads"}async create(e){const a=this.apiService.buildUrl(this.resource),i=await this.apiService.request(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});return o.z.object({data:t.leadResponseSchema}).parse(i).data}}const p="aidol_chatroom_ids",C=o.z.record(o.z.string(),o.z.string()),s=()=>{if(typeof window>"u")return{};const n=localStorage.getItem(p);if(!n)return{};try{const e=JSON.parse(n),a=C.safeParse(e);return a.success?a.data:(console.warn("[LocalChatroomIdsRepository] Invalid data, resetting"),{})}catch(e){if(e instanceof SyntaxError)return console.warn("[LocalChatroomIdsRepository] Failed to parse, resetting"),{};throw e}},g=n=>{if(typeof window>"u")throw new Error("LocalChatroomIdsRepository write operations can only be called on the client side.");localStorage.setItem(p,JSON.stringify(n))},S={getChatroomId(n){return s()[n]??null},setChatroomId(n,e){const a=s();a[n]=e,g(a)},removeChatroomId(n){const e=s();delete e[n],g(e)}},R={close:"Close"},w={header:"Position Assignment",title:"Assign positions to each member!",subtitle:"Tap to assign a position",unassigned:"Unassigned",mainVocal:"Main Vocal",subVocal:"Sub Vocal",mainDancer:"Main Dancer",subDancer:"Sub Dancer",mainRapper:"Main Rapper",subRapper:"Sub Rapper",assignedTo:"{{position}}({{name}})",assign:"Assign",confirm:"Plan Group",needMore:"Assign positions to {{count}} more members",error:{load:"Failed to load members",update:"Failed to assign position"}},v={title:"Debut Member Casting",infoBanner:{title:"Cast your trainees",description:"The idol characters (face, voice, video) in this content are original virtual characters created by AI and are not based on any real person, including idols and celebrities."},tabs:{boy:"Boy Group",girl:"Girl Group",mixed:"Mixed Group"},newMember:{prompt:"Can't find a trainee you like?",subPrompt:"Try casting a new member"},addMember:"New Member",abilities:{title:"Abilities",vocal:"Vocal",dance:"Dance",rap:"Rap",visual:"Visual",stamina:"Stamina",charm:"Charm"},castButton:"Cast",castComplete:{title:"Casting Complete!",description:"The trainee has joined the group",viewBoard:"View Casting Board"}},M="Share",k="URL copied!",$={header:"Final Debut Lineup",title:"My Casted Trainees!",subtitle:"Tap to remove",browse:"Browse Trainees",confirm:"Confirm Debut!",needMore:"Cast {{count}} more to create a group!",delete:"Remove",deleted:"Removed successfully.",empty:{title:`Final Debut Lineup
2
2
  is Empty`,description:"Cast your trainees!"},error:{load:"Failed to load members.",delete:"Failed to remove."}},N={title:"Trainee Casting Complete",subtitle:"Added to the casting candidate list",remainingSlots:"Remaining Casting Slots",findNext:"Find Next Trainee",viewBoard:"View Casting Board",error:{load:"Failed to load slot information"}},I={promptPlaceholder:"How does your idol talk? Describe their vibe...",addMember:"Add Member",signed:"Signed 🥺",grade:"Grade {{grade}}",tab:{profile:"Profile",stats:"Stats"}},B={notFound:"Group not found",members:"Members",noMembers:"No members yet"},L={groupName:"Group Name",groupNamePlaceholder:"e.g. DREAMERS, STARLIGHT",memberName:"Member Name",memberNamePlaceholder:"e.g. Luna, Kai",concept:"Concept",selectConcept:"Select a concept",personality:"Personality",concepts:{cute:"Cute",cool:"Cool",elegant:"Elegant",powerful:"Powerful"},personalities:{cheerful:{label:"Cheerful",description:"Always bright and positive"},cool:{label:"Cool",description:"Calm and collected"},tsundere:{label:"Tsundere",description:"Cold outside, warm inside"},gentle:{label:"Gentle",description:"Kind and caring"}},step1:"Group",step2:"Members",step3:"Done",step1Title:"Create Your Group",step2Title:"Add Members",groupNameRequired:"Please enter a group name",memberNameRequired:"Please enter a member name",memberRequired:"Please add at least one member",emblem:"Emblem",emblemPromptPlaceholder:"Describe your group's emblem...",emblemRequired:"Please generate an emblem image",generate:"Generate",next:"Next",back:"Back",create:"Create",completeTitle:"Your Idol is Ready!",completeDescription:"Start chatting with your dream idol now",viewProfile:"View Profile"},G={greeting1:"Hello!",greeting2:"We are {{name}}!",greeting3:"Nice to meet you",createAnother:"Create Another Group",share:"Share Profile",banner:"Curious about this group's next move?",error:{load:"Failed to load group info"}},D={header:"Enter Email",previewTitle:`Dorm stories, practice tales
3
3
  We'll send you real reviews from members!`,previewMessage:"Shouldn't you talk to your hyung more respectfully?",title:"Stay Updated",description:"Get the latest news and updates about this group via email.",emailLabel:"Email",emailPlaceholder:"example@email.com",submit:"Get Updates",submitting:"Subscribing...",success:"Successfully subscribed!",error:"Failed to subscribe. Please try again."},A={header:"Group Planning",step1Title:"Name your group",step2Title:"Create your group emblem",namePlaceholder:"e.g.) Dreamers, Starlight",promptPlaceholder:"e.g.)",generate:"Generate Image",regenerate:"Regenerate Image",emptyImage:`Generated profile
4
4
  image will
@@ -10,13 +10,13 @@ está Vacía`,description:"¡Elige a tus trainees!"},error:{load:"Error al carga
10
10
  generada aparecerá
11
11
  aquí`,next:"Siguiente",error:{load:"Error al cargar la información del grupo",generate:"Error al generar la imagen",update:"Error al guardar la planificación"}},Z={title:"Casting de Miembro Debut",next:"Siguiente",error:{update:"Error al guardar.",generateImage:"Error al generar imagen.",load:"Error al cargar aprendices."},gender:{stepTitle:"Elige un género",female:"Femenino",male:"Masculino"},personality:{stepTitle:"Cuéntanos sobre la personalidad",energy:"Dirección de Energía",energyLeft:"Extrovertido (E)",energyRight:"Introvertido (I)",perception:"Estilo de Percepción",perceptionLeft:"Sensorial (S)",perceptionRight:"Intuitivo (N)",judgment:"Base de Juicio",judgmentLeft:"Pensamiento (T)",judgmentRight:"Sentimiento (F)",lifestyle:"Estilo de Vida",lifestyleLeft:"Juicio (J)",lifestyleRight:"Percepción (P)"},image:{stepTitle:"¡Vamos a crear una foto de perfil!",promptPlaceholder:"ej.) Una chica con pelo negro y ojos grandes",generate:"Generar Imagen",regenerate:"Regenerar Imagen",placeholder:`La imagen de perfil
12
12
  generada aparecerá
13
- aquí`},complete:{nameStepTitle:"¿Cuál es el nombre?",bioStepTitle:"Cuéntanos la historia de este idol",namePlaceholder:"Ingresa un nombre",bioPlaceholder:"¿Qué camino ha recorrido?",button:"Completar Casting"}},ee={hero:{title:{line1:"Posibilidades Infinitas",line2:"El Nacimiento del Grupo Idol"},line1:"El fandom global",line2:"selecciona y debuta",line3:"Nuevo Grupo de Idol AI",line4:"Proyecto Próxima Generación",cta:"Seleccionar Aprendices"},error:{create:"No se pudo iniciar."}},ae={title:"Debut Temporada 1",chemistryButton:"Química entre Miembros 💭",followButton:"Seguir"},ne={common:j,position:q,casting:K,share:F,urlCopied:U,castingBoard:J,castingComplete:O,companion:z,aidol:X,creation:W,complete:_,newsletter:Y,groupPlanning:Q,companionCreate:Z,landing:ee,group:ae},te={close:"Tutup"},ie={header:"Penetapan Posisi",title:"Tetapkan posisi untuk setiap anggota!",subtitle:"Ketuk untuk menetapkan posisi",unassigned:"Belum ditetapkan",mainVocal:"Vokal Utama",subVocal:"Vokal Pendukung",mainDancer:"Penari Utama",subDancer:"Penari Pendukung",mainRapper:"Rapper Utama",subRapper:"Rapper Pendukung",assignedTo:"{{position}}({{name}})",assign:"Tetapkan",confirm:"Rencanakan Grup",needMore:"Tetapkan posisi untuk {{count}} anggota lagi",error:{load:"Gagal memuat daftar anggota",update:"Gagal menetapkan posisi"}},re={title:"Casting Anggota Debut",infoBanner:{title:"Casting trainee pilihanmu",description:"Karakter idol (wajah, suara, video) dalam konten ini adalah karakter virtual asli yang dibuat oleh AI dan tidak berdasarkan orang nyata, termasuk idol dan selebriti."},tabs:{boy:"Boy Group",girl:"Girl Group",mixed:"Grup Campuran"},newMember:{prompt:"Tidak ada trainee yang kamu suka?",subPrompt:"Coba casting anggota baru"},addMember:"Anggota Baru",abilities:{title:"Kemampuan",vocal:"Vokal",dance:"Dance",rap:"Rap",visual:"Visual",stamina:"Stamina",charm:"Pesona"},castButton:"Casting",castComplete:{title:"Casting Selesai!",description:"Trainee telah bergabung dengan grup",viewBoard:"Lihat Casting Board"}},oe="Bagikan",le="URL disalin!",se={header:"Line-up Debut Final",title:"Trainee yang Sudah Aku Pilih!",subtitle:"Ketuk untuk hapus",browse:"Lihat Trainee",confirm:"Konfirmasi Debut!",needMore:"Pilih {{count}} lagi untuk buat grup!",delete:"Hapus",deleted:"Berhasil dihapus.",empty:{title:`Line-up Debut Final
13
+ aquí`},complete:{nameStepTitle:"¿Cuál es el nombre?",bioStepTitle:"Cuéntanos la historia de este idol",namePlaceholder:"Ingresa un nombre",bioPlaceholder:"¿Qué camino ha recorrido?",button:"Completar Casting"}},ee={hero:{title:{line1:"Posibilidades Infinitas",line2:"El Nacimiento del Grupo Idol"},line1:"El fandom global",line2:"selecciona y debuta",line3:"Nuevo Grupo de Idol AI",line4:"Proyecto Próxima Generación",cta:"Seleccionar Aprendices"},error:{create:"No se pudo iniciar."}},ae={title:"Debut Temporada 1",chemistryButton:"Química entre Miembros 💭",followButton:"Seguir"},te={common:j,position:q,casting:K,share:F,urlCopied:U,castingBoard:J,castingComplete:O,companion:z,aidol:X,creation:W,complete:_,newsletter:Y,groupPlanning:Q,companionCreate:Z,landing:ee,group:ae},ne={close:"Tutup"},ie={header:"Penetapan Posisi",title:"Tetapkan posisi untuk setiap anggota!",subtitle:"Ketuk untuk menetapkan posisi",unassigned:"Belum ditetapkan",mainVocal:"Vokal Utama",subVocal:"Vokal Pendukung",mainDancer:"Penari Utama",subDancer:"Penari Pendukung",mainRapper:"Rapper Utama",subRapper:"Rapper Pendukung",assignedTo:"{{position}}({{name}})",assign:"Tetapkan",confirm:"Rencanakan Grup",needMore:"Tetapkan posisi untuk {{count}} anggota lagi",error:{load:"Gagal memuat daftar anggota",update:"Gagal menetapkan posisi"}},re={title:"Casting Anggota Debut",infoBanner:{title:"Casting trainee pilihanmu",description:"Karakter idol (wajah, suara, video) dalam konten ini adalah karakter virtual asli yang dibuat oleh AI dan tidak berdasarkan orang nyata, termasuk idol dan selebriti."},tabs:{boy:"Boy Group",girl:"Girl Group",mixed:"Grup Campuran"},newMember:{prompt:"Tidak ada trainee yang kamu suka?",subPrompt:"Coba casting anggota baru"},addMember:"Anggota Baru",abilities:{title:"Kemampuan",vocal:"Vokal",dance:"Dance",rap:"Rap",visual:"Visual",stamina:"Stamina",charm:"Pesona"},castButton:"Casting",castComplete:{title:"Casting Selesai!",description:"Trainee telah bergabung dengan grup",viewBoard:"Lihat Casting Board"}},oe="Bagikan",le="URL disalin!",se={header:"Line-up Debut Final",title:"Trainee yang Sudah Aku Pilih!",subtitle:"Ketuk untuk hapus",browse:"Lihat Trainee",confirm:"Konfirmasi Debut!",needMore:"Pilih {{count}} lagi untuk buat grup!",delete:"Hapus",deleted:"Berhasil dihapus.",empty:{title:`Line-up Debut Final
14
14
  Masih Kosong`,description:"Yuk pilih trainee!"},error:{load:"Gagal memuat info anggota.",delete:"Gagal menghapus."}},ce={title:"Casting Trainee Selesai",subtitle:"Ditambahkan ke daftar kandidat casting",remainingSlots:"Slot Casting Tersisa",findNext:"Cari Trainee Berikutnya",viewBoard:"Lihat Papan Casting",error:{load:"Gagal memuat informasi slot"}},me={promptPlaceholder:"Gimana idol kamu ngobrol? Ceritain kepribadiannya...",addMember:"Tambah Anggota",signed:"Sudah Dikontrak 🥺",grade:"Grade {{grade}}",tab:{profile:"Profil",stats:"Statistik"}},ge={notFound:"Grup tidak ditemukan",members:"Anggota",noMembers:"Belum ada anggota"},pe={groupName:"Nama Grup",groupNamePlaceholder:"cth: DREAMERS, STARLIGHT",memberName:"Nama Anggota",memberNamePlaceholder:"cth: Luna, Kai",concept:"Konsep",selectConcept:"Pilih konsep",personality:"Kepribadian",concepts:{cute:"Imut",cool:"Keren",elegant:"Elegan",powerful:"Kuat"},personalities:{cheerful:{label:"Ceria",description:"Selalu cerah dan positif"},cool:{label:"Keren",description:"Tenang dan kalem"},tsundere:{label:"Tsundere",description:"Dingin di luar, hangat di dalam"},gentle:{label:"Lembut",description:"Baik hati dan perhatian"}},step1:"Grup",step2:"Anggota",step3:"Selesai",step1Title:"Buat Grup Kamu",step2Title:"Tambah Anggota",groupNameRequired:"Masukkan nama grup",memberNameRequired:"Masukkan nama anggota",memberRequired:"Tambahkan minimal satu anggota",emblem:"Emblem",emblemPromptPlaceholder:"Deskripsikan emblem grup kamu...",emblemRequired:"Buat gambar emblem dulu",generate:"Buat",next:"Lanjut",back:"Kembali",create:"Buat",completeTitle:"Idol Kamu Siap!",completeDescription:"Langsung ngobrol sama idol impianmu sekarang",viewProfile:"Lihat Profil"},de={greeting1:"Halo!",greeting2:"Kami {{name}}!",greeting3:"Senang berkenalan",createAnother:"Buat Grup Lain",share:"Bagikan Profil",banner:"Penasaran dengan langkah selanjutnya grup ini?",error:{load:"Gagal memuat info grup"}},ue={header:"Masukkan Email",previewTitle:`Cerita asrama debut, cerita latihan
15
15
  Kami akan kirimkan ulasan nyata dari para anggota!`,previewMessage:"Bukankah kamu harus bicara lebih sopan ke kakak?",title:"Dapatkan Info Terbaru",description:"Terima berita dan update terbaru tentang grup ini via email.",emailLabel:"Email",emailPlaceholder:"contoh@email.com",submit:"Terima Kabar",submitting:"Mendaftar...",success:"Berhasil berlangganan!",error:"Gagal berlangganan. Coba lagi."},he={header:"Perencanaan Grup",step1Title:"Beri nama grupmu",step2Title:"Buat emblem grup",namePlaceholder:"cth.) Dreamers, Starlight",promptPlaceholder:"cth.)",generate:"Buat Gambar",regenerate:"Buat Ulang Gambar",emptyImage:`Gambar profil
16
16
  yang dibuat akan
17
17
  muncul di sini`,next:"Selanjutnya",error:{load:"Gagal memuat info grup",generate:"Gagal membuat gambar",update:"Gagal menyimpan perencanaan grup"}},be={title:"Casting Member Debut",next:"Lanjut",error:{update:"Gagal menyimpan.",generateImage:"Gagal membuat gambar.",load:"Gagal memuat daftar trainee."},gender:{stepTitle:"Pilih jenis kelamin",female:"Perempuan",male:"Laki-laki"},personality:{stepTitle:"Ceritakan kepribadiannya",energy:"Arah Energi",energyLeft:"Ekstrovert (E)",energyRight:"Introvert (I)",perception:"Gaya Persepsi",perceptionLeft:"Sensing (S)",perceptionRight:"Intuisi (N)",judgment:"Dasar Penilaian",judgmentLeft:"Pemikiran (T)",judgmentRight:"Perasaan (F)",lifestyle:"Gaya Hidup",lifestyleLeft:"Penilaian (J)",lifestyleRight:"Persepsi (P)"},image:{stepTitle:"Yuk buat foto profil!",promptPlaceholder:"cth) Gadis berambut hitam dengan mata besar",generate:"Buat Gambar",regenerate:"Buat Ulang Gambar",placeholder:`Gambar profil
18
18
  yang dibuat
19
- akan muncul di sini`},complete:{nameStepTitle:"Siapa namanya?",bioStepTitle:"Ceritakan kisah idol ini",namePlaceholder:"Masukkan nama",bioPlaceholder:"Perjalanan apa yang telah dilalui?",button:"Selesai Casting"}},ye={hero:{title:{line1:"Kemungkinan Tak Terbatas",line2:"Kelahiran Grup Idol"},line1:"Fandom global",line2:"casting dan mendebut",line3:"Grup Idol AI Baru",line4:"Proyek Generasi Berikutnya",cta:"Casting Trainee"},error:{create:"Gagal memulai."}},Te={title:"Debut Musim 1",chemistryButton:"Kimia Antar Anggota 💭",followButton:"Ikuti"},fe={common:te,position:ie,casting:re,share:oe,urlCopied:le,castingBoard:se,castingComplete:ce,companion:me,aidol:ge,creation:pe,complete:de,newsletter:ue,groupPlanning:he,companionCreate:be,landing:ye,group:Te},Pe={close:"閉じる"},Ce={header:"ポジション配置",title:"メンバーにポジションを配置しよう!",subtitle:"タップしてポジションを配置できます",unassigned:"未配置",mainVocal:"メインボーカル",subVocal:"サブボーカル",mainDancer:"メインダンサー",subDancer:"サブダンサー",mainRapper:"メインラッパー",subRapper:"サブラッパー",assignedTo:"{{position}}({{name}})",assign:"配置する",confirm:"グループを企画する",needMore:"あと{{count}}人のポジションを配置してください",error:{load:"メンバー一覧の読み込みに失敗しました",update:"ポジションの配置に失敗しました"}},Se={title:"デビューメンバーキャスティング",infoBanner:{title:"練習生をキャスティングしましょう",description:"本コンテンツのアイドルキャラクター(顔・声・映像)はAIで制作されたオリジナルの仮想人物であり、実在の人物(アイドル/芸能人を含む)に基づいていません。"},tabs:{boy:"ボーイグループ",girl:"ガールグループ",mixed:"混合グループ"},newMember:{prompt:"お気に入りの練習生がいませんか?",subPrompt:"新しいメンバーをキャスティングしてみましょう"},addMember:"新メンバー",abilities:{title:"能力値",vocal:"ボーカル",dance:"ダンス",rap:"ラップ",visual:"ビジュアル",stamina:"スタミナ",charm:"魅力"},castButton:"キャスティング",castComplete:{title:"キャスティング完了!",description:"練習生がグループに合流しました",viewBoard:"キャスティングボードを見る"}},Re="共有",we="URLがコピーされました!",ve={header:"最終デビュー組ラインナップ",title:"私がキャスティングした練習生!",subtitle:"タップして削除できます",browse:"練習生を見る",confirm:"デビュー組確定!",needMore:"あと{{count}}人キャスティングするとグループが作れます!",delete:"削除",deleted:"削除されました。",empty:{title:`最終デビュー組ラインナップが
19
+ akan muncul di sini`},complete:{nameStepTitle:"Siapa namanya?",bioStepTitle:"Ceritakan kisah idol ini",namePlaceholder:"Masukkan nama",bioPlaceholder:"Perjalanan apa yang telah dilalui?",button:"Selesai Casting"}},ye={hero:{title:{line1:"Kemungkinan Tak Terbatas",line2:"Kelahiran Grup Idol"},line1:"Fandom global",line2:"casting dan mendebut",line3:"Grup Idol AI Baru",line4:"Proyek Generasi Berikutnya",cta:"Casting Trainee"},error:{create:"Gagal memulai."}},Te={title:"Debut Musim 1",chemistryButton:"Kimia Antar Anggota 💭",followButton:"Ikuti"},fe={common:ne,position:ie,casting:re,share:oe,urlCopied:le,castingBoard:se,castingComplete:ce,companion:me,aidol:ge,creation:pe,complete:de,newsletter:ue,groupPlanning:he,companionCreate:be,landing:ye,group:Te},Pe={close:"閉じる"},Ce={header:"ポジション配置",title:"メンバーにポジションを配置しよう!",subtitle:"タップしてポジションを配置できます",unassigned:"未配置",mainVocal:"メインボーカル",subVocal:"サブボーカル",mainDancer:"メインダンサー",subDancer:"サブダンサー",mainRapper:"メインラッパー",subRapper:"サブラッパー",assignedTo:"{{position}}({{name}})",assign:"配置する",confirm:"グループを企画する",needMore:"あと{{count}}人のポジションを配置してください",error:{load:"メンバー一覧の読み込みに失敗しました",update:"ポジションの配置に失敗しました"}},Se={title:"デビューメンバーキャスティング",infoBanner:{title:"練習生をキャスティングしましょう",description:"本コンテンツのアイドルキャラクター(顔・声・映像)はAIで制作されたオリジナルの仮想人物であり、実在の人物(アイドル/芸能人を含む)に基づいていません。"},tabs:{boy:"ボーイグループ",girl:"ガールグループ",mixed:"混合グループ"},newMember:{prompt:"お気に入りの練習生がいませんか?",subPrompt:"新しいメンバーをキャスティングしてみましょう"},addMember:"新メンバー",abilities:{title:"能力値",vocal:"ボーカル",dance:"ダンス",rap:"ラップ",visual:"ビジュアル",stamina:"スタミナ",charm:"魅力"},castButton:"キャスティング",castComplete:{title:"キャスティング完了!",description:"練習生がグループに合流しました",viewBoard:"キャスティングボードを見る"}},Re="共有",we="URLがコピーされました!",ve={header:"最終デビュー組ラインナップ",title:"私がキャスティングした練習生!",subtitle:"タップして削除できます",browse:"練習生を見る",confirm:"デビュー組確定!",needMore:"あと{{count}}人キャスティングするとグループが作れます!",delete:"削除",deleted:"削除されました。",empty:{title:`最終デビュー組ラインナップが
20
20
  空です`,description:"アイドルをキャスティングしてみよう!"},error:{load:"メンバー情報の読み込みに失敗しました。",delete:"削除に失敗しました。"}},Me={title:"練習生キャスティング完了",subtitle:"候補リストに追加されました",remainingSlots:"残りキャスティングスロット",findNext:"次の練習生を探す",viewBoard:"キャスティングボードを見る",error:{load:"スロット情報の読み込みに失敗しました"}},ke={promptPlaceholder:"どんな話し方をする?性格を教えてね...",addMember:"メンバー追加",signed:"契約完了 🥺",grade:"{{grade}} ランク",tab:{profile:"プロフィール",stats:"能力値"}},$e={notFound:"グループが見つかりません",members:"メンバー",noMembers:"まだメンバーがいません"},Ne={groupName:"グループ名",groupNamePlaceholder:"例: DREAMERS, STARLIGHT",memberName:"メンバー名",memberNamePlaceholder:"例: ルナ, カイ",concept:"コンセプト",selectConcept:"コンセプトを選択",personality:"性格",concepts:{cute:"キュート",cool:"クール",elegant:"エレガント",powerful:"パワフル"},personalities:{cheerful:{label:"明るい",description:"いつも元気でポジティブ"},cool:{label:"クール",description:"冷静で落ち着いている"},tsundere:{label:"ツンデレ",description:"外は冷たく中は温かい"},gentle:{label:"優しい",description:"親切で思いやりがある"}},step1:"グループ",step2:"メンバー",step3:"完了",step1Title:"グループを作ろう",step2Title:"メンバーを追加",groupNameRequired:"グループ名を入力してください",memberNameRequired:"メンバー名を入力してください",memberRequired:"メンバーを1人以上追加してください",emblem:"エンブレム",emblemPromptPlaceholder:"グループのエンブレムを説明してください...",emblemRequired:"エンブレム画像を生成してください",generate:"生成",next:"次へ",back:"戻る",create:"作成",completeTitle:"アイドル完成!",completeDescription:"さっそく推しと会話しよう",viewProfile:"プロフィールを見る"},Ie={greeting1:"こんにちは!",greeting2:"{{name}}です!",greeting3:"よろしくお願いします",createAnother:"別のグループを作成",share:"プロフィールを共有",banner:"このグループの次の動きが気になりますか?",error:{load:"グループ情報の読み込みに失敗しました"}},Be={header:"メールアドレス入力",previewTitle:`デビュー組の寮話、練習話
21
21
  メンバーのリアルな声を
22
22
  お届けします!`,previewMessage:"先輩にそんな言い方しちゃダメじゃない?",title:"最新情報を受け取る",description:"このグループの最新ニュースやアップデートをメールで受け取りましょう。",emailLabel:"メールアドレス",emailPlaceholder:"example@email.com",submit:"ニュースを受け取る",submitting:"購読中...",success:"購読が完了しました!",error:"購読に失敗しました。もう一度お試しください。"},Le={header:"グループ企画",step1Title:"グループの名前を決めてください",step2Title:"グループのエンブレムを作成してください",namePlaceholder:"例)ドリーマーズ、スターライト",promptPlaceholder:"例)",generate:"画像生成",regenerate:"画像再生成",emptyImage:`生成されたプロフィール
@@ -29,13 +29,13 @@ akan muncul di sini`},complete:{nameStepTitle:"Siapa namanya?",bioStepTitle:"Cer
29
29
  이미지가
30
30
  표시돼요`,next:"다음",error:{load:"그룹 정보를 불러오지 못했습니다",generate:"이미지 생성에 실패했습니다",update:"그룹 기획 저장에 실패했습니다"}},_e={title:"데뷔 멤버 캐스팅",next:"다음",error:{update:"저장에 실패했습니다.",generateImage:"이미지 생성에 실패했습니다.",load:"연습생 목록을 불러오는데 실패했습니다."},gender:{stepTitle:"성별을 정해주세요",female:"여성",male:"남성"},personality:{stepTitle:"성격을 알려주세요",energy:"에너지 방향",energyLeft:"외향 (E)",energyRight:"내향 (I)",perception:"정보 인식 방식",perceptionLeft:"직관 (S)",perceptionRight:"감각 (N)",judgment:"판단 기준",judgmentLeft:"사고 (T)",judgmentRight:"감정 (F)",lifestyle:"생활 방식",lifestyleLeft:"인식형 (J)",lifestyleRight:"판단형 (P)"},image:{stepTitle:"프로필 사진을 만들어볼까요?",promptPlaceholder:"예) 검은 머리에 큰 눈을 가진 소녀",generate:"이미지 생성",regenerate:"이미지 재생성",placeholder:`생성된 프로필
31
31
  이미지가
32
- 표시돼요`},complete:{nameStepTitle:"이름은 무엇인가요?",bioStepTitle:"이 아이돌의 서사를 알려주세요",namePlaceholder:"이름을 입력해주세요",bioPlaceholder:"어떤 시간을 보냈나요?",button:"캐스팅 완료"}},Ye={hero:{title:{line1:"무한한 가능성의",line2:"아이돌 그룹의 탄생"},line1:"글로벌 팬덤이",line2:"직접 캐스팅하고 데뷔시키는",line3:"신인 AI 아이돌 그룹",line4:"넥스트 제너레이션 프로젝트",cta:"연습생 캐스팅하기"},error:{create:"시작에 실패했습니다."}},Qe={title:"시즌1 데뷔조",chemistryButton:"멤버 간 케미 💭",followButton:"팔로우"},Ze={common:xe,position:Ve,casting:He,share:je,urlCopied:qe,castingBoard:Ke,castingComplete:Fe,companion:Ue,aidol:Je,creation:Oe,complete:ze,newsletter:Xe,groupPlanning:We,companionCreate:_e,landing:Ye,group:Qe},ea={close:"ปิด"},aa={header:"กำหนดตำแหน่ง",title:"กำหนดตำแหน่งให้สมาชิกแต่ละคน!",subtitle:"แตะเพื่อกำหนดตำแหน่ง",unassigned:"ยังไม่กำหนด",mainVocal:"เมนโวคอล",subVocal:"ซับโวคอล",mainDancer:"เมนแดนเซอร์",subDancer:"ซับแดนเซอร์",mainRapper:"เมนแร็ปเปอร์",subRapper:"ซับแร็ปเปอร์",assignedTo:"{{position}}({{name}})",assign:"กำหนด",confirm:"วางแผนกลุ่ม",needMore:"กำหนดตำแหน่งอีก {{count}} คน",error:{load:"ไม่สามารถโหลดรายชื่อสมาชิก",update:"ไม่สามารถกำหนดตำแหน่ง"}},na={title:"แคสติ้งสมาชิกเดบิวต์",infoBanner:{title:"แคสต์เทรนนี่ของคุณ",description:"ตัวละครไอดอล (ใบหน้า เสียง วิดีโอ) ในเนื้อหานี้เป็นตัวละครเสมือนจริงต้นฉบับที่สร้างด้วย AI และไม่ได้อิงจากบุคคลจริง รวมถึงไอดอลและคนดัง"},tabs:{boy:"บอยกรุ๊ป",girl:"เกิร์ลกรุ๊ป",mixed:"กรุ๊ปผสม"},newMember:{prompt:"ไม่มีเทรนนี่ที่ถูกใจเหรอ?",subPrompt:"ลองแคสต์สมาชิกใหม่สิ"},addMember:"สมาชิกใหม่",abilities:{title:"ความสามารถ",vocal:"ร้อง",dance:"เต้น",rap:"แร็พ",visual:"วิชวล",stamina:"ความอดทน",charm:"เสน่ห์"},castButton:"แคสติ้ง",castComplete:{title:"แคสติ้งสำเร็จ!",description:"เทรนนี่ได้เข้าร่วมกลุ่มแล้ว",viewBoard:"ดูบอร์ดแคสติ้ง"}},ta="แชร์",ia="คัดลอก URL แล้ว!",ra={header:"รายชื่อเดบิวต์สุดท้าย",title:"เทรนนี่ที่ฉันเลือก!",subtitle:"แตะเพื่อลบ",browse:"ดูเทรนนี่",confirm:"ยืนยันเดบิวต์!",needMore:"เลือกอีก {{count}} คนเพื่อสร้างกลุ่ม!",delete:"ลบ",deleted:"ลบสำเร็จแล้ว",empty:{title:`รายชื่อเดบิวต์สุดท้าย
32
+ 표시돼요`},complete:{nameStepTitle:"이름은 무엇인가요?",bioStepTitle:"이 아이돌의 서사를 알려주세요",namePlaceholder:"이름을 입력해주세요",bioPlaceholder:"어떤 시간을 보냈나요?",button:"캐스팅 완료"}},Ye={hero:{title:{line1:"무한한 가능성의",line2:"아이돌 그룹의 탄생"},line1:"글로벌 팬덤이",line2:"직접 캐스팅하고 데뷔시키는",line3:"신인 AI 아이돌 그룹",line4:"넥스트 제너레이션 프로젝트",cta:"연습생 캐스팅하기"},error:{create:"시작에 실패했습니다."}},Qe={title:"시즌1 데뷔조",chemistryButton:"멤버 간 케미 💭",followButton:"팔로우"},Ze={common:xe,position:Ve,casting:He,share:je,urlCopied:qe,castingBoard:Ke,castingComplete:Fe,companion:Ue,aidol:Je,creation:Oe,complete:ze,newsletter:Xe,groupPlanning:We,companionCreate:_e,landing:Ye,group:Qe},ea={close:"ปิด"},aa={header:"กำหนดตำแหน่ง",title:"กำหนดตำแหน่งให้สมาชิกแต่ละคน!",subtitle:"แตะเพื่อกำหนดตำแหน่ง",unassigned:"ยังไม่กำหนด",mainVocal:"เมนโวคอล",subVocal:"ซับโวคอล",mainDancer:"เมนแดนเซอร์",subDancer:"ซับแดนเซอร์",mainRapper:"เมนแร็ปเปอร์",subRapper:"ซับแร็ปเปอร์",assignedTo:"{{position}}({{name}})",assign:"กำหนด",confirm:"วางแผนกลุ่ม",needMore:"กำหนดตำแหน่งอีก {{count}} คน",error:{load:"ไม่สามารถโหลดรายชื่อสมาชิก",update:"ไม่สามารถกำหนดตำแหน่ง"}},ta={title:"แคสติ้งสมาชิกเดบิวต์",infoBanner:{title:"แคสต์เทรนนี่ของคุณ",description:"ตัวละครไอดอล (ใบหน้า เสียง วิดีโอ) ในเนื้อหานี้เป็นตัวละครเสมือนจริงต้นฉบับที่สร้างด้วย AI และไม่ได้อิงจากบุคคลจริง รวมถึงไอดอลและคนดัง"},tabs:{boy:"บอยกรุ๊ป",girl:"เกิร์ลกรุ๊ป",mixed:"กรุ๊ปผสม"},newMember:{prompt:"ไม่มีเทรนนี่ที่ถูกใจเหรอ?",subPrompt:"ลองแคสต์สมาชิกใหม่สิ"},addMember:"สมาชิกใหม่",abilities:{title:"ความสามารถ",vocal:"ร้อง",dance:"เต้น",rap:"แร็พ",visual:"วิชวล",stamina:"ความอดทน",charm:"เสน่ห์"},castButton:"แคสติ้ง",castComplete:{title:"แคสติ้งสำเร็จ!",description:"เทรนนี่ได้เข้าร่วมกลุ่มแล้ว",viewBoard:"ดูบอร์ดแคสติ้ง"}},na="แชร์",ia="คัดลอก URL แล้ว!",ra={header:"รายชื่อเดบิวต์สุดท้าย",title:"เทรนนี่ที่ฉันเลือก!",subtitle:"แตะเพื่อลบ",browse:"ดูเทรนนี่",confirm:"ยืนยันเดบิวต์!",needMore:"เลือกอีก {{count}} คนเพื่อสร้างกลุ่ม!",delete:"ลบ",deleted:"ลบสำเร็จแล้ว",empty:{title:`รายชื่อเดบิวต์สุดท้าย
33
33
  ยังว่างอยู่`,description:"ไปเลือกเทรนนี่กันเถอะ!"},error:{load:"ไม่สามารถโหลดข้อมูลสมาชิกได้",delete:"ลบไม่สำเร็จ"}},oa={title:"แคสติ้งเทรนนี่เสร็จสิ้น",subtitle:"เพิ่มเข้าสู่รายชื่อผู้สมัครแล้ว",remainingSlots:"สล็อตแคสติ้งที่เหลือ",findNext:"หาเทรนนี่คนถัดไป",viewBoard:"ดูบอร์ดแคสติ้ง",error:{load:"ไม่สามารถโหลดข้อมูลสล็อตได้"}},la={promptPlaceholder:"ไอดอลของคุณพูดยังไง? บรรยายบุคลิกสักนิด...",addMember:"เพิ่มสมาชิก",signed:"เซ็นสัญญาแล้ว 🥺",grade:"เกรด {{grade}}",tab:{profile:"โปรไฟล์",stats:"สถิติ"}},sa={notFound:"ไม่พบกลุ่ม",members:"สมาชิก",noMembers:"ยังไม่มีสมาชิก"},ca={groupName:"ชื่อกลุ่ม",groupNamePlaceholder:"เช่น: DREAMERS, STARLIGHT",memberName:"ชื่อสมาชิก",memberNamePlaceholder:"เช่น: Luna, Kai",concept:"คอนเซ็ปต์",selectConcept:"เลือกคอนเซ็ปต์",personality:"บุคลิกภาพ",concepts:{cute:"น่ารัก",cool:"เท่",elegant:"สง่างาม",powerful:"ทรงพลัง"},personalities:{cheerful:{label:"ร่าเริง",description:"สดใสและเป็นบวกเสมอ"},cool:{label:"เท่",description:"สงบและเยือกเย็น"},tsundere:{label:"ซึนเดเระ",description:"ภายนอกเย็นชา ภายในอบอุ่น"},gentle:{label:"อ่อนโยน",description:"ใจดีและเอาใจใส่"}},step1:"กลุ่ม",step2:"สมาชิก",step3:"เสร็จ",step1Title:"สร้างกลุ่มของคุณ",step2Title:"เพิ่มสมาชิก",groupNameRequired:"กรุณาใส่ชื่อกลุ่ม",memberNameRequired:"กรุณาใส่ชื่อสมาชิก",memberRequired:"กรุณาเพิ่มสมาชิกอย่างน้อย 1 คน",emblem:"ตราสัญลักษณ์",emblemPromptPlaceholder:"อธิบายตราสัญลักษณ์กลุ่ม...",emblemRequired:"กรุณาสร้างภาพตราสัญลักษณ์",generate:"สร้าง",next:"ถัดไป",back:"กลับ",create:"สร้าง",completeTitle:"ไอดอลพร้อมแล้ว!",completeDescription:"เริ่มแชทกับไอดอลในฝันของคุณได้เลย",viewProfile:"ดูโปรไฟล์"},ma={greeting1:"สวัสดี!",greeting2:"เราคือ {{name}}!",greeting3:"ยินดีที่ได้รู้จัก",createAnother:"สร้างกลุ่มใหม่",share:"แชร์โปรไฟล์",banner:"อยากรู้ก้าวต่อไปของกลุ่มนี้ไหม?",error:{load:"ไม่สามารถโหลดข้อมูลกลุ่ม"}},ga={header:"ใส่อีเมล",previewTitle:`เรื่องหอพักเดบิวต์ เรื่องฝึกซ้อม
34
34
  เราจะส่งรีวิวจริงจากสมาชิกให้คุณ!`,previewMessage:"พูดกับพี่แบบนั้นไม่ดีนะ?",title:"รับข่าวสาร",description:"รับข่าวสารและอัปเดตล่าสุดเกี่ยวกับกลุ่มนี้ทางอีเมล",emailLabel:"อีเมล",emailPlaceholder:"example@email.com",submit:"รับข่าวสาร",submitting:"กำลังสมัคร...",success:"สมัครสำเร็จ!",error:"สมัครไม่สำเร็จ กรุณาลองอีกครั้ง"},pa={header:"วางแผนกลุ่ม",step1Title:"ตั้งชื่อกลุ่มของคุณ",step2Title:"สร้างสัญลักษณ์กลุ่ม",namePlaceholder:"เช่น) Dreamers, Starlight",promptPlaceholder:"เช่น)",generate:"สร้างภาพ",regenerate:"สร้างภาพใหม่",emptyImage:`ภาพโปรไฟล์
35
35
  ที่สร้างขึ้นจะ
36
36
  แสดงที่นี่`,next:"ถัดไป",error:{load:"ไม่สามารถโหลดข้อมูลกลุ่ม",generate:"ไม่สามารถสร้างภาพ",update:"ไม่สามารถบันทึกแผนกลุ่ม"}},da={title:"แคสติ้งสมาชิกเดบิวต์",next:"ถัดไป",error:{update:"บันทึกไม่สำเร็จ",generateImage:"สร้างรูปไม่สำเร็จ",load:"ไม่สามารถโหลดรายชื่อเทรนนี่ได้"},gender:{stepTitle:"เลือกเพศ",female:"หญิง",male:"ชาย"},personality:{stepTitle:"บอกเราเกี่ยวกับบุคลิกภาพ",energy:"ทิศทางพลังงาน",energyLeft:"เปิดเผย (E)",energyRight:"เก็บตัว (I)",perception:"รูปแบบการรับรู้",perceptionLeft:"ประสาทสัมผัส (S)",perceptionRight:"สัญชาตญาณ (N)",judgment:"เกณฑ์การตัดสิน",judgmentLeft:"ความคิด (T)",judgmentRight:"ความรู้สึก (F)",lifestyle:"ไลฟ์สไตล์",lifestyleLeft:"ตัดสิน (J)",lifestyleRight:"รับรู้ (P)"},image:{stepTitle:"มาสร้างรูปโปรไฟล์กัน!",promptPlaceholder:"เช่น) สาวผมดำตาโต",generate:"สร้างรูป",regenerate:"สร้างรูปใหม่",placeholder:`รูปโปรไฟล์
37
37
  ที่สร้าง
38
- จะแสดงที่นี่`},complete:{nameStepTitle:"ชื่ออะไร?",bioStepTitle:"เล่าเรื่องราวของไอดอลคนนี้",namePlaceholder:"กรอกชื่อ",bioPlaceholder:"ผ่านช่วงเวลาอะไรมาบ้าง?",button:"แคสติ้งเสร็จสิ้น"}},ua={hero:{title:{line1:"ความเป็นไปได้ไม่จำกัด",line2:"การเกิดของกลุ่มไอดอล"},line1:"แฟนด้อมทั่วโลก",line2:"คัดเลือกและเดบิวต์",line3:"กลุ่มไอดอล AI ใหม่",line4:"โปรเจกต์เจเนอเรชั่นถัดไป",cta:"คัดเลือกเทรนนี่"},error:{create:"เริ่มต้นไม่สำเร็จ"}},ha={title:"เดบิวต์ซีซั่น 1",chemistryButton:"เคมีระหว่างสมาชิก 💭",followButton:"ติดตาม"},ba={common:ea,position:aa,casting:na,share:ta,urlCopied:ia,castingBoard:ra,castingComplete:oa,companion:la,aidol:sa,creation:ca,complete:ma,newsletter:ga,groupPlanning:pa,companionCreate:da,landing:ua,group:ha},ya={close:"Isara"},Ta={header:"Pagtatalaga ng Posisyon",title:"Italaga ang posisyon sa bawat miyembro!",subtitle:"I-tap para magtalaga ng posisyon",unassigned:"Hindi pa naitalaga",mainVocal:"Main Vocal",subVocal:"Sub Vocal",mainDancer:"Main Dancer",subDancer:"Sub Dancer",mainRapper:"Main Rapper",subRapper:"Sub Rapper",assignedTo:"{{position}}({{name}})",assign:"Italaga",confirm:"Planuhin ang Grupo",needMore:"Magtalaga pa ng posisyon sa {{count}} miyembro",error:{load:"Hindi ma-load ang listahan ng miyembro",update:"Hindi maitalaga ang posisyon"}},fa={title:"Debut Member Casting",infoBanner:{title:"I-cast ang iyong mga trainee",description:"Ang mga idol character (mukha, boses, video) sa content na ito ay orihinal na virtual character na ginawa ng AI at hindi batay sa totoong tao, kabilang ang mga idol at celebrity."},tabs:{boy:"Boy Group",girl:"Girl Group",mixed:"Mixed Group"},newMember:{prompt:"Walang trainee na gusto mo?",subPrompt:"Subukan mag-cast ng bagong miyembro"},addMember:"Bagong Miyembro",abilities:{title:"Kakayahan",vocal:"Vocal",dance:"Dance",rap:"Rap",visual:"Visual",stamina:"Stamina",charm:"Charm"},castButton:"I-cast",castComplete:{title:"Tapos na ang Casting!",description:"Sumali na ang trainee sa grupo",viewBoard:"Tingnan ang Casting Board"}},Pa="I-share",Ca="Nakopya ang URL!",Sa={header:"Pinal na Debut Lineup",title:"Mga Trainee na Napili Ko!",subtitle:"I-tap para tanggalin",browse:"Tingnan ang Trainees",confirm:"Kumpirmahin ang Debut!",needMore:"Pumili pa ng {{count}} para makagawa ng grupo!",delete:"Tanggalin",deleted:"Matagumpay na natanggal.",empty:{title:`Walang Laman ang
38
+ จะแสดงที่นี่`},complete:{nameStepTitle:"ชื่ออะไร?",bioStepTitle:"เล่าเรื่องราวของไอดอลคนนี้",namePlaceholder:"กรอกชื่อ",bioPlaceholder:"ผ่านช่วงเวลาอะไรมาบ้าง?",button:"แคสติ้งเสร็จสิ้น"}},ua={hero:{title:{line1:"ความเป็นไปได้ไม่จำกัด",line2:"การเกิดของกลุ่มไอดอล"},line1:"แฟนด้อมทั่วโลก",line2:"คัดเลือกและเดบิวต์",line3:"กลุ่มไอดอล AI ใหม่",line4:"โปรเจกต์เจเนอเรชั่นถัดไป",cta:"คัดเลือกเทรนนี่"},error:{create:"เริ่มต้นไม่สำเร็จ"}},ha={title:"เดบิวต์ซีซั่น 1",chemistryButton:"เคมีระหว่างสมาชิก 💭",followButton:"ติดตาม"},ba={common:ea,position:aa,casting:ta,share:na,urlCopied:ia,castingBoard:ra,castingComplete:oa,companion:la,aidol:sa,creation:ca,complete:ma,newsletter:ga,groupPlanning:pa,companionCreate:da,landing:ua,group:ha},ya={close:"Isara"},Ta={header:"Pagtatalaga ng Posisyon",title:"Italaga ang posisyon sa bawat miyembro!",subtitle:"I-tap para magtalaga ng posisyon",unassigned:"Hindi pa naitalaga",mainVocal:"Main Vocal",subVocal:"Sub Vocal",mainDancer:"Main Dancer",subDancer:"Sub Dancer",mainRapper:"Main Rapper",subRapper:"Sub Rapper",assignedTo:"{{position}}({{name}})",assign:"Italaga",confirm:"Planuhin ang Grupo",needMore:"Magtalaga pa ng posisyon sa {{count}} miyembro",error:{load:"Hindi ma-load ang listahan ng miyembro",update:"Hindi maitalaga ang posisyon"}},fa={title:"Debut Member Casting",infoBanner:{title:"I-cast ang iyong mga trainee",description:"Ang mga idol character (mukha, boses, video) sa content na ito ay orihinal na virtual character na ginawa ng AI at hindi batay sa totoong tao, kabilang ang mga idol at celebrity."},tabs:{boy:"Boy Group",girl:"Girl Group",mixed:"Mixed Group"},newMember:{prompt:"Walang trainee na gusto mo?",subPrompt:"Subukan mag-cast ng bagong miyembro"},addMember:"Bagong Miyembro",abilities:{title:"Kakayahan",vocal:"Vocal",dance:"Dance",rap:"Rap",visual:"Visual",stamina:"Stamina",charm:"Charm"},castButton:"I-cast",castComplete:{title:"Tapos na ang Casting!",description:"Sumali na ang trainee sa grupo",viewBoard:"Tingnan ang Casting Board"}},Pa="I-share",Ca="Nakopya ang URL!",Sa={header:"Pinal na Debut Lineup",title:"Mga Trainee na Napili Ko!",subtitle:"I-tap para tanggalin",browse:"Tingnan ang Trainees",confirm:"Kumpirmahin ang Debut!",needMore:"Pumili pa ng {{count}} para makagawa ng grupo!",delete:"Tanggalin",deleted:"Matagumpay na natanggal.",empty:{title:`Walang Laman ang
39
39
  Pinal na Lineup`,description:"Pumili ka ng trainees!"},error:{load:"Hindi ma-load ang mga miyembro.",delete:"Hindi matanggal."}},Ra={title:"Kumpleto na ang Casting ng Trainee",subtitle:"Naidagdag sa listahan ng kandidato",remainingSlots:"Natitirang Casting Slot",findNext:"Hanapin ang Susunod na Trainee",viewBoard:"Tingnan ang Casting Board",error:{load:"Hindi ma-load ang impormasyon ng slot"}},wa={promptPlaceholder:"Paano nagsasalita ang idol mo? Ilarawan ang personality...",addMember:"Magdagdag ng Miyembro",signed:"Naka-kontrata na 🥺",grade:"Grade {{grade}}",tab:{profile:"Profile",stats:"Stats"}},va={notFound:"Hindi nahanap ang grupo",members:"Miyembro",noMembers:"Wala pang miyembro"},Ma={groupName:"Pangalan ng Grupo",groupNamePlaceholder:"hal: DREAMERS, STARLIGHT",memberName:"Pangalan ng Miyembro",memberNamePlaceholder:"hal: Luna, Kai",concept:"Konsepto",selectConcept:"Pumili ng konsepto",personality:"Personalidad",concepts:{cute:"Cute",cool:"Cool",elegant:"Elegante",powerful:"Malakas"},personalities:{cheerful:{label:"Masayahin",description:"Palaging masaya at positibo"},cool:{label:"Cool",description:"Kalmado at mahinahon"},tsundere:{label:"Tsundere",description:"Malamig sa labas, mainit sa loob"},gentle:{label:"Mabait",description:"Maalalahanin at mapagmahal"}},step1:"Grupo",step2:"Miyembro",step3:"Tapos",step1Title:"Gumawa ng Grupo",step2Title:"Magdagdag ng Miyembro",groupNameRequired:"Maglagay ng pangalan ng grupo",memberNameRequired:"Maglagay ng pangalan ng miyembro",memberRequired:"Magdagdag ng kahit isang miyembro",emblem:"Emblema",emblemPromptPlaceholder:"Ilarawan ang emblema ng grupo...",emblemRequired:"Gumawa ng imahe ng emblema",generate:"Lumikha",next:"Susunod",back:"Bumalik",create:"Gumawa",completeTitle:"Handa na ang Idol Mo!",completeDescription:"Mag-chat na sa iyong dream idol ngayon",viewProfile:"Tingnan ang Profile"},ka={greeting1:"Kumusta!",greeting2:"Kami ang {{name}}!",greeting3:"Masaya kaming makilala ka",createAnother:"Gumawa ng Ibang Grupo",share:"I-share ang Profile",banner:"Curious sa susunod na gagawin ng grupo?",error:{load:"Hindi ma-load ang info ng grupo"}},$a={header:"Ilagay ang Email",previewTitle:`Mga kwento sa dorm ng debut, mga kwento sa practice
40
40
  Ipapadala namin ang mga tunay na review mula sa mga miyembro!`,previewMessage:"Hindi ba dapat mas magalang kang makipag-usap sa kuya mo?",title:"Manatiling Updated",description:"Tumanggap ng pinakabagong balita at updates tungkol sa grupong ito sa email.",emailLabel:"Email",emailPlaceholder:"halimbawa@email.com",submit:"Kumuha ng Updates",submitting:"Nagsu-subscribe...",success:"Matagumpay na naka-subscribe!",error:"Hindi makapag-subscribe. Subukan muli."},Na={header:"Pagpaplano ng Grupo",step1Title:"Pangalanan ang iyong grupo",step2Title:"Gumawa ng emblem ng grupo",namePlaceholder:"hal.) Dreamers, Starlight",promptPlaceholder:"hal.)",generate:"Gumawa ng Larawan",regenerate:"Gumawa Ulit ng Larawan",emptyImage:`Ang nabuong profile
41
41
  na larawan ay
@@ -47,11 +47,11 @@ Chúng tôi sẽ gửi bạn những câu chuyện thật từ các thành viên
47
47
  được tạo sẽ
48
48
  hiển thị ở đây`,next:"Tiếp Theo",error:{load:"Không thể tải thông tin nhóm",generate:"Không thể tạo hình ảnh",update:"Không thể lưu kế hoạch nhóm"}},za={title:"Tuyển Chọn Thành Viên Ra Mắt",next:"Tiếp",error:{update:"Lưu thất bại.",generateImage:"Tạo ảnh thất bại.",load:"Không thể tải danh sách thực tập sinh."},gender:{stepTitle:"Chọn giới tính",female:"Nữ",male:"Nam"},personality:{stepTitle:"Cho chúng tôi biết về tính cách",energy:"Hướng Năng Lượng",energyLeft:"Hướng ngoại (E)",energyRight:"Hướng nội (I)",perception:"Phong Cách Nhận Thức",perceptionLeft:"Cảm giác (S)",perceptionRight:"Trực giác (N)",judgment:"Cơ Sở Đánh Giá",judgmentLeft:"Lý trí (T)",judgmentRight:"Cảm xúc (F)",lifestyle:"Lối Sống",lifestyleLeft:"Phán đoán (J)",lifestyleRight:"Linh hoạt (P)"},image:{stepTitle:"Hãy tạo ảnh đại diện!",promptPlaceholder:"vd) Cô gái tóc đen mắt to",generate:"Tạo Ảnh",regenerate:"Tạo Lại Ảnh",placeholder:`Ảnh đại diện
49
49
  được tạo sẽ
50
- hiển thị ở đây`},complete:{nameStepTitle:"Tên là gì?",bioStepTitle:"Kể cho chúng tôi câu chuyện của idol này",namePlaceholder:"Nhập tên",bioPlaceholder:"Đã trải qua hành trình gì?",button:"Hoàn Thành Tuyển Chọn"}},Xa={hero:{title:{line1:"Khả Năng Vô Hạn",line2:"Sự Ra Đời Của Nhóm Idol"},line1:"Fandom toàn cầu",line2:"tuyển chọn và ra mắt",line3:"Nhóm Idol AI Mới",line4:"Dự Án Thế Hệ Tiếp Theo",cta:"Tuyển Thực Tập Sinh"},error:{create:"Không thể bắt đầu."}},Wa={title:"Ra Mắt Mùa 1",chemistryButton:"Phản Ứng Hóa Học Giữa Thành Viên 💭",followButton:"Theo dõi"},_a={common:Da,position:Aa,casting:Ea,share:xa,urlCopied:Va,castingBoard:Ha,castingComplete:ja,companion:qa,aidol:Ka,creation:Fa,complete:Ua,newsletter:Ja,groupPlanning:Oa,companionCreate:za,landing:Xa,group:Wa},Ya={close:"关闭"},Qa={header:"位置分配",title:"给每位成员分配位置!",subtitle:"点击分配位置",unassigned:"未分配",mainVocal:"主唱",subVocal:"副唱",mainDancer:"主舞",subDancer:"副舞",mainRapper:"主说唱",subRapper:"副说唱",assignedTo:"{{position}}({{name}})",assign:"分配",confirm:"规划组合",needMore:"还需分配{{count}}人的位置",error:{load:"加载成员列表失败",update:"位置分配失败"}},Za={title:"出道成员选拔",infoBanner:{title:"选拔你的练习生",description:"本内容中的偶像角色(面部、声音、视频)是由AI制作的原创虚拟人物,不基于任何真实人物,包括偶像和艺人。"},tabs:{boy:"男团",girl:"女团",mixed:"混合团"},newMember:{prompt:"没有喜欢的练习生吗?",subPrompt:"试试选拔新成员"},addMember:"新成员",abilities:{title:"能力值",vocal:"声乐",dance:"舞蹈",rap:"说唱",visual:"颜值",stamina:"体力",charm:"魅力"},castButton:"选秀",castComplete:{title:"选秀完成!",description:"练习生已加入组合",viewBoard:"查看选秀面板"}},en="分享",an="URL已复制!",nn={header:"最终出道阵容",title:"我选中的练习生!",subtitle:"点击可删除",browse:"浏览练习生",confirm:"确认出道!",needMore:"再选{{count}}人就可以创建组合了!",delete:"删除",deleted:"已删除。",empty:{title:`最终出道阵容
51
- 为空`,description:"去选一些练习生吧!"},error:{load:"加载成员信息失败。",delete:"删除失败。"}},tn={title:"练习生选秀完成",subtitle:"已添加到候选名单",remainingSlots:"剩余选秀名额",findNext:"寻找下一位练习生",viewBoard:"查看选秀板",error:{load:"无法加载名额信息"}},rn={promptPlaceholder:"你的偶像怎么说话?描述一下性格...",addMember:"添加成员",signed:"已签约 🥺",grade:"{{grade}} 级",tab:{profile:"简介",stats:"能力值"}},on={notFound:"找不到该组合",members:"成员",noMembers:"暂无成员"},ln={groupName:"组合名",groupNamePlaceholder:"例如: DREAMERS, STARLIGHT",memberName:"成员名",memberNamePlaceholder:"例如: Luna, Kai",concept:"概念",selectConcept:"选择概念",personality:"性格",concepts:{cute:"可爱",cool:"酷",elegant:"优雅",powerful:"有力"},personalities:{cheerful:{label:"开朗",description:"总是阳光积极"},cool:{label:"冷酷",description:"冷静沉着"},tsundere:{label:"傲娇",description:"外冷内热"},gentle:{label:"温柔",description:"善良体贴"}},step1:"组合",step2:"成员",step3:"完成",step1Title:"创建组合",step2Title:"添加成员",groupNameRequired:"请输入组合名",memberNameRequired:"请输入成员名",memberRequired:"请至少添加一名成员",emblem:"徽章",emblemPromptPlaceholder:"描述你的组合徽章...",emblemRequired:"请生成徽章图片",generate:"生成",next:"下一步",back:"上一步",create:"创建",completeTitle:"偶像创建完成!",completeDescription:"现在就和你的梦想偶像聊天吧",viewProfile:"查看资料"},sn={greeting1:"你好!",greeting2:"我们是{{name}}!",greeting3:"请多多关照",createAnother:"创建其他组合",share:"分享资料",banner:"想知道这个组合的下一步吗?",error:{load:"无法加载组合信息"}},cn={header:"输入邮箱",previewTitle:`出道组宿舍趣事、练习趣事
52
- 成员们的真实后记发送给你!`,previewMessage:"你这样跟哥哥说话不好吧?",title:"获取最新消息",description:"通过邮件接收该组合的最新消息和更新。",emailLabel:"邮箱",emailPlaceholder:"example@email.com",submit:"接收消息",submitting:"订阅中...",success:"订阅成功!",error:"订阅失败,请重试。"},mn={header:"组合规划",step1Title:"为你的组合起名",step2Title:"创建组合徽章",namePlaceholder:"例)Dreamers、Starlight",promptPlaceholder:"例)",generate:"生成图片",regenerate:"重新生成图片",emptyImage:`生成的头像
50
+ hiển thị ở đây`},complete:{nameStepTitle:"Tên là gì?",bioStepTitle:"Kể cho chúng tôi câu chuyện của idol này",namePlaceholder:"Nhập tên",bioPlaceholder:"Đã trải qua hành trình gì?",button:"Hoàn Thành Tuyển Chọn"}},Xa={hero:{title:{line1:"Khả Năng Vô Hạn",line2:"Sự Ra Đời Của Nhóm Idol"},line1:"Fandom toàn cầu",line2:"tuyển chọn và ra mắt",line3:"Nhóm Idol AI Mới",line4:"Dự Án Thế Hệ Tiếp Theo",cta:"Tuyển Thực Tập Sinh"},error:{create:"Không thể bắt đầu."}},Wa={title:"Ra Mắt Mùa 1",chemistryButton:"Phản Ứng Hóa Học Giữa Thành Viên 💭",followButton:"Theo dõi"},_a={common:Da,position:Aa,casting:Ea,share:xa,urlCopied:Va,castingBoard:Ha,castingComplete:ja,companion:qa,aidol:Ka,creation:Fa,complete:Ua,newsletter:Ja,groupPlanning:Oa,companionCreate:za,landing:Xa,group:Wa},Ya={close:"关闭"},Qa={header:"位置分配",title:"给每位成员分配位置!",subtitle:"点击分配位置",unassigned:"未分配",mainVocal:"主唱",subVocal:"副唱",mainDancer:"主舞",subDancer:"副舞",mainRapper:"主说唱",subRapper:"副说唱",assignedTo:"{{position}}({{name}})",assign:"分配",confirm:"规划组合",needMore:"还需分配{{count}}人的位置",error:{load:"加载成员列表失败",update:"位置分配失败"}},Za={title:"出道成员选拔",infoBanner:{title:"选拔你的练习生",description:"本内容中的偶像角色(面部、声音、视频)是由AI制作的原创虚拟人物,不基于任何真实人物,包括偶像和艺人。"},tabs:{boy:"男团",girl:"女团",mixed:"混合团"},newMember:{prompt:"没有喜欢的练习生吗?",subPrompt:"试试选拔新成员"},addMember:"新成员",abilities:{title:"能力值",vocal:"声乐",dance:"舞蹈",rap:"说唱",visual:"颜值",stamina:"体力",charm:"魅力"},castButton:"选秀",castComplete:{title:"选秀完成!",description:"练习生已加入组合",viewBoard:"查看选秀面板"}},et="分享",at="URL已复制!",tt={header:"最终出道阵容",title:"我选中的练习生!",subtitle:"点击可删除",browse:"浏览练习生",confirm:"确认出道!",needMore:"再选{{count}}人就可以创建组合了!",delete:"删除",deleted:"已删除。",empty:{title:`最终出道阵容
51
+ 为空`,description:"去选一些练习生吧!"},error:{load:"加载成员信息失败。",delete:"删除失败。"}},nt={title:"练习生选秀完成",subtitle:"已添加到候选名单",remainingSlots:"剩余选秀名额",findNext:"寻找下一位练习生",viewBoard:"查看选秀板",error:{load:"无法加载名额信息"}},it={promptPlaceholder:"你的偶像怎么说话?描述一下性格...",addMember:"添加成员",signed:"已签约 🥺",grade:"{{grade}} 级",tab:{profile:"简介",stats:"能力值"}},rt={notFound:"找不到该组合",members:"成员",noMembers:"暂无成员"},ot={groupName:"组合名",groupNamePlaceholder:"例如: DREAMERS, STARLIGHT",memberName:"成员名",memberNamePlaceholder:"例如: Luna, Kai",concept:"概念",selectConcept:"选择概念",personality:"性格",concepts:{cute:"可爱",cool:"酷",elegant:"优雅",powerful:"有力"},personalities:{cheerful:{label:"开朗",description:"总是阳光积极"},cool:{label:"冷酷",description:"冷静沉着"},tsundere:{label:"傲娇",description:"外冷内热"},gentle:{label:"温柔",description:"善良体贴"}},step1:"组合",step2:"成员",step3:"完成",step1Title:"创建组合",step2Title:"添加成员",groupNameRequired:"请输入组合名",memberNameRequired:"请输入成员名",memberRequired:"请至少添加一名成员",emblem:"徽章",emblemPromptPlaceholder:"描述你的组合徽章...",emblemRequired:"请生成徽章图片",generate:"生成",next:"下一步",back:"上一步",create:"创建",completeTitle:"偶像创建完成!",completeDescription:"现在就和你的梦想偶像聊天吧",viewProfile:"查看资料"},lt={greeting1:"你好!",greeting2:"我们是{{name}}!",greeting3:"请多多关照",createAnother:"创建其他组合",share:"分享资料",banner:"想知道这个组合的下一步吗?",error:{load:"无法加载组合信息"}},st={header:"输入邮箱",previewTitle:`出道组宿舍趣事、练习趣事
52
+ 成员们的真实后记发送给你!`,previewMessage:"你这样跟哥哥说话不好吧?",title:"获取最新消息",description:"通过邮件接收该组合的最新消息和更新。",emailLabel:"邮箱",emailPlaceholder:"example@email.com",submit:"接收消息",submitting:"订阅中...",success:"订阅成功!",error:"订阅失败,请重试。"},ct={header:"组合规划",step1Title:"为你的组合起名",step2Title:"创建组合徽章",namePlaceholder:"例)Dreamers、Starlight",promptPlaceholder:"例)",generate:"生成图片",regenerate:"重新生成图片",emptyImage:`生成的头像
53
53
  将显示
54
- 在这里`,next:"下一步",error:{load:"无法加载组合信息",generate:"图片生成失败",update:"保存组合规划失败"}},gn={title:"出道成员选角",next:"下一步",error:{update:"保存失败。",generateImage:"生成图片失败。",load:"加载练习生列表失败。"},gender:{stepTitle:"请选择性别",female:"女性",male:"男性"},personality:{stepTitle:"告诉我们性格",energy:"能量方向",energyLeft:"外向 (E)",energyRight:"内向 (I)",perception:"信息感知方式",perceptionLeft:"感觉 (S)",perceptionRight:"直觉 (N)",judgment:"判断标准",judgmentLeft:"思维 (T)",judgmentRight:"情感 (F)",lifestyle:"生活方式",lifestyleLeft:"判断 (J)",lifestyleRight:"感知 (P)"},image:{stepTitle:"来创建一张头像吧!",promptPlaceholder:"例)黑发大眼睛的女孩",generate:"生成图片",regenerate:"重新生成图片",placeholder:`生成的头像
54
+ 在这里`,next:"下一步",error:{load:"无法加载组合信息",generate:"图片生成失败",update:"保存组合规划失败"}},mt={title:"出道成员选角",next:"下一步",error:{update:"保存失败。",generateImage:"生成图片失败。",load:"加载练习生列表失败。"},gender:{stepTitle:"请选择性别",female:"女性",male:"男性"},personality:{stepTitle:"告诉我们性格",energy:"能量方向",energyLeft:"外向 (E)",energyRight:"内向 (I)",perception:"信息感知方式",perceptionLeft:"感觉 (S)",perceptionRight:"直觉 (N)",judgment:"判断标准",judgmentLeft:"思维 (T)",judgmentRight:"情感 (F)",lifestyle:"生活方式",lifestyleLeft:"判断 (J)",lifestyleRight:"感知 (P)"},image:{stepTitle:"来创建一张头像吧!",promptPlaceholder:"例)黑发大眼睛的女孩",generate:"生成图片",regenerate:"重新生成图片",placeholder:`生成的头像
55
55
  将在此
56
- 显示`},complete:{nameStepTitle:"叫什么名字?",bioStepTitle:"告诉我们这位偶像的故事",namePlaceholder:"请输入名字",bioPlaceholder:"经历了怎样的时光?",button:"完成选角"}},pn={hero:{title:{line1:"无限可能性的",line2:"偶像团体诞生"},line1:"全球粉丝",line2:"亲自选角并出道",line3:"新人AI偶像团体",line4:"下一代项目",cta:"选拔练习生"},error:{create:"启动失败。"}},dn={title:"第一季出道组",chemistryButton:"成员间默契 💭",followButton:"关注"},un={common:Ya,position:Qa,casting:Za,share:en,urlCopied:an,castingBoard:nn,castingComplete:tn,companion:rn,aidol:on,creation:ln,complete:sn,newsletter:cn,groupPlanning:mn,companionCreate:gn,landing:pn,group:dn},hn="aidol",bn={en:H,es:ne,id:fe,ja:Ee,ko:Ze,th:ba,tl:Ga,vi:_a,zh:un};exports.SenderType=n.SenderType;exports.aidolSchema=n.aidolSchema;exports.chatroomSchema=n.chatroomSchema;exports.companionSchema=n.companionSchema;exports.imageGenerationResponseSchema=n.imageGenerationResponseSchema;exports.isCompanion=n.isCompanion;exports.isUser=n.isUser;exports.messageSchema=n.messageSchema;exports.AIDOL_NS=hn;exports.AIdolRepository=b;exports.ChatroomRepository=T;exports.CompanionRepository=f;exports.LeadsRepository=P;exports.LocalChatroomIdsRepository=S;exports.aidolTranslations=bn;
56
+ 显示`},complete:{nameStepTitle:"叫什么名字?",bioStepTitle:"告诉我们这位偶像的故事",namePlaceholder:"请输入名字",bioPlaceholder:"经历了怎样的时光?",button:"完成选角"}},gt={hero:{title:{line1:"无限可能性的",line2:"偶像团体诞生"},line1:"全球粉丝",line2:"亲自选角并出道",line3:"新人AI偶像团体",line4:"下一代项目",cta:"选拔练习生"},error:{create:"启动失败。"}},pt={title:"第一季出道组",chemistryButton:"成员间默契 💭",followButton:"关注"},dt={common:Ya,position:Qa,casting:Za,share:et,urlCopied:at,castingBoard:tt,castingComplete:nt,companion:it,aidol:rt,creation:ot,complete:lt,newsletter:st,groupPlanning:ct,companionCreate:mt,landing:gt,group:pt},ut="aidol",ht={en:H,es:te,id:fe,ja:Ee,ko:Ze,th:ba,tl:Ga,vi:_a,zh:dt};exports.SenderType=t.SenderType;exports.aidolSchema=t.aidolSchema;exports.chatroomSchema=t.chatroomSchema;exports.companionSchema=t.companionSchema;exports.imageGenerationResponseSchema=t.imageGenerationResponseSchema;exports.isCompanion=t.isCompanion;exports.isUser=t.isUser;exports.messageSchema=t.messageSchema;exports.AIDOL_NS=ut;exports.AIdolRepository=b;exports.ChatroomRepository=T;exports.CompanionRepository=f;exports.LeadsRepository=P;exports.LocalChatroomIdsRepository=S;exports.aidolTranslations=ht;
57
57
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/lib/assertResourceId.ts","../src/repositories/AIdolRepository.ts","../src/repositories/ChatroomRepository.ts","../src/repositories/CompanionRepository.ts","../src/repositories/LeadsRepository.ts","../src/repositories/LocalChatroomIdsRepository.ts","../src/i18n/translations.ts"],"sourcesContent":["const UUID_RE =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Validates that a resource ID is a well-formed UUID.\n * Prevents client-side path traversal when IDs are interpolated into URLs.\n */\nexport function assertResourceId(id: string | number): void {\n if (typeof id !== \"string\" || !UUID_RE.test(id)) {\n throw new Error(`Invalid resource ID: ${String(id)}`);\n }\n}\n","import { BaseCrudRepository } from \"@aioia/core\";\n\nimport { assertResourceId } from \"../lib/assertResourceId\";\nimport type {\n AIdol,\n AIdolCreate,\n AIdolCreateResponse,\n ImageGenerationRequest,\n ImageGenerationResponse,\n} from \"../schemas\";\nimport {\n aidolCreateResponseSchema,\n aidolSchema,\n imageGenerationResponseSchema,\n} from \"../schemas\";\n\n/** Backend returns unwrapped responses — overrides wrap raw JSON in { data } */\nexport class AIdolRepository extends BaseCrudRepository<AIdol> {\n readonly resource = \"aidols\";\n\n protected getDataSchema() {\n return aidolSchema;\n }\n\n async createAIdol(variables: AIdolCreate): Promise<AIdolCreateResponse> {\n const url = this.apiService.buildUrl(this.resource);\n const raw = await this.apiService.request(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(variables),\n });\n return aidolCreateResponseSchema.parse(raw);\n }\n\n async getOne(\n params: { id: string | number },\n fetchOptions?: RequestInit,\n ): Promise<{ data: AIdol }> {\n assertResourceId(params.id);\n const url = `${this.apiService.buildUrl(this.resource)}/${params.id}`;\n const raw = await this.apiService.request(url, fetchOptions);\n return { data: aidolSchema.parse(raw) };\n }\n\n async update<TVariables = Record<string, unknown>>(params: {\n id: string | number;\n variables: TVariables;\n }): Promise<{ data: AIdol }> {\n assertResourceId(params.id);\n const url = `${this.apiService.buildUrl(this.resource)}/${params.id}`;\n const raw = await this.apiService.request(url, {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(params.variables),\n });\n return { data: aidolSchema.parse(raw) };\n }\n\n async generateImage(\n request: ImageGenerationRequest,\n fetchOptions?: RequestInit,\n ): Promise<ImageGenerationResponse> {\n const url = this.apiService.buildUrl(`${this.resource}/images`);\n const rawResponse = await this.apiService.request(url, {\n ...fetchOptions,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(request),\n });\n\n return this.validateResponse(rawResponse, imageGenerationResponseSchema);\n }\n}\n","import { BaseCrudRepository } from \"@aioia/core\";\nimport { z } from \"zod\";\n\nimport {\n chatroomSchema,\n messageSchema,\n type Chatroom,\n type Message,\n} from \"../schemas\";\n\n/**\n * Response schema for generate AI response endpoint\n */\nconst generateResponseSchema = z.object({\n messageId: z.string(),\n content: z.string(),\n});\n\nexport interface GenerateResponse {\n messageId: string;\n content: string;\n}\n\n/**\n * Repository for Chatroom entities\n * Handles chatroom CRUD and message operations\n */\nexport class ChatroomRepository extends BaseCrudRepository<Chatroom> {\n readonly resource = \"chatrooms\";\n\n protected getDataSchema() {\n return chatroomSchema;\n }\n\n /**\n * Get messages from a chatroom\n * GET /chatrooms/{id}/messages\n */\n async getMessages(\n chatroomId: string,\n options?: { limit?: number; offset?: number },\n fetchOptions?: RequestInit,\n ): Promise<Message[]> {\n const params = new URLSearchParams();\n if (options?.limit !== undefined) {\n params.set(\"limit\", options.limit.toString());\n }\n if (options?.offset !== undefined) {\n params.set(\"offset\", options.offset.toString());\n }\n\n const queryString = params.toString();\n const url = this.apiService.buildUrl(\n `${this.resource}/${chatroomId}/messages${queryString ? `?${queryString}` : \"\"}`,\n );\n\n const rawResponse = await this.apiService.request(url, fetchOptions);\n return this.validateResponse(rawResponse, z.array(messageSchema));\n }\n\n /**\n * Send a message to a chatroom\n * POST /chatrooms/{id}/messages\n *\n * Anonymous ID is automatically sent via httpOnly cookie.\n *\n * @param chatroomId - The chatroom ID\n * @param content - The message content\n * @param fetchOptions - Optional fetch options\n */\n async sendMessage(\n chatroomId: string,\n content: string,\n fetchOptions?: RequestInit,\n ): Promise<Message> {\n const url = this.apiService.buildUrl(\n `${this.resource}/${chatroomId}/messages`,\n );\n\n const rawResponse = await this.apiService.request(url, {\n ...fetchOptions,\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ content, senderType: \"user\" }),\n });\n\n return this.validateResponse(rawResponse, messageSchema);\n }\n\n /**\n * Generate AI response for a chatroom with a specific companion\n * POST /chatrooms/{id}/companions/{companionId}/response\n */\n async generateResponse(\n chatroomId: string,\n companionId: string,\n fetchOptions?: RequestInit,\n ): Promise<GenerateResponse> {\n const url = this.apiService.buildUrl(\n `${this.resource}/${chatroomId}/companions/${companionId}/response`,\n );\n\n const rawResponse = await this.apiService.request(url, {\n ...fetchOptions,\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n });\n\n return this.validateResponse(rawResponse, generateResponseSchema);\n }\n}\n","import { BaseCrudRepository } from \"@aioia/core\";\n\nimport type {\n Companion,\n ImageGenerationRequest,\n ImageGenerationResponse,\n} from \"../schemas\";\nimport { companionSchema, imageGenerationResponseSchema } from \"../schemas\";\n\n/** CRUD operations use BaseCrudRepository (wrapped { data: T } responses) */\nexport class CompanionRepository extends BaseCrudRepository<Companion> {\n readonly resource = \"companions\";\n\n protected getDataSchema() {\n return companionSchema;\n }\n\n async generateImage(\n request: ImageGenerationRequest,\n fetchOptions?: RequestInit,\n ): Promise<ImageGenerationResponse> {\n const url = this.apiService.buildUrl(`${this.resource}/images`);\n const rawResponse = await this.apiService.request(url, {\n ...fetchOptions,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(request),\n });\n\n return this.validateResponse(rawResponse, imageGenerationResponseSchema);\n }\n}\n","import type { LeadRequest, LeadResponse } from \"@/schemas\";\nimport { leadResponseSchema } from \"@/schemas\";\nimport type { ApiService } from \"@/services/ApiService\";\n\nexport class LeadsRepository {\n private readonly resource = \"leads\";\n\n constructor(private readonly apiService: ApiService) {}\n\n async create(request: LeadRequest): Promise<LeadResponse> {\n const url = this.apiService.buildUrl(this.resource);\n const response = await this.apiService.request(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(request),\n });\n\n return leadResponseSchema.parse(response);\n }\n}\n","import { z } from \"zod\";\n\nconst STORAGE_KEY = \"aidol_chatroom_ids\";\nconst schema = z.record(z.string(), z.string());\n\nconst readStorage = (): Record<string, string> => {\n if (typeof window === \"undefined\") return {};\n\n const stored = localStorage.getItem(STORAGE_KEY);\n if (!stored) return {};\n\n try {\n const parsed = JSON.parse(stored);\n const result = schema.safeParse(parsed);\n if (result.success) {\n return result.data;\n }\n console.warn(`[LocalChatroomIdsRepository] Invalid data, resetting`);\n return {};\n } catch (error) {\n if (error instanceof SyntaxError) {\n console.warn(`[LocalChatroomIdsRepository] Failed to parse, resetting`);\n return {};\n }\n throw error;\n }\n};\n\nconst writeStorage = (data: Record<string, string>): void => {\n if (typeof window === \"undefined\") {\n throw new Error(\n \"LocalChatroomIdsRepository write operations can only be called on the client side.\",\n );\n }\n localStorage.setItem(STORAGE_KEY, JSON.stringify(data));\n};\n\n/**\n * Repository for managing chatroom IDs in localStorage.\n * Maps companionId to chatroomId for persistence across sessions.\n */\nexport const LocalChatroomIdsRepository = {\n getChatroomId(companionId: string): string | null {\n const data = readStorage();\n return data[companionId] ?? null;\n },\n\n setChatroomId(companionId: string, chatroomId: string): void {\n const data = readStorage();\n data[companionId] = chatroomId;\n writeStorage(data);\n },\n\n removeChatroomId(companionId: string): void {\n const data = readStorage();\n delete data[companionId];\n writeStorage(data);\n },\n};\n","/**\n * Server-safe exports for aidol translations.\n *\n * This module exports only JSON resources and constants,\n * avoiding React-specific code that would break SSR.\n */\n\nimport en from \"./locales/en/aidol.json\";\nimport es from \"./locales/es/aidol.json\";\nimport id from \"./locales/id/aidol.json\";\nimport ja from \"./locales/ja/aidol.json\";\nimport ko from \"./locales/ko/aidol.json\";\nimport th from \"./locales/th/aidol.json\";\nimport tl from \"./locales/tl/aidol.json\";\nimport vi from \"./locales/vi/aidol.json\";\nimport zh from \"./locales/zh/aidol.json\";\n\n/** AIdol namespace */\nexport const AIDOL_NS = \"aidol\";\n\n/**\n * Translation resources for the aidol namespace.\n * Use with i18next.addResourceBundle(lang, 'aidol', translations)\n *\n * @example\n * import { aidolTranslations, AIDOL_NS } from 'aidol/locale';\n *\n * // Add to existing i18n instance\n * Object.entries(aidolTranslations).forEach(([lang, resources]) => {\n * i18n.addResourceBundle(lang, AIDOL_NS, resources);\n * });\n */\nexport const aidolTranslations = {\n en,\n es,\n id,\n ja,\n ko,\n th,\n tl,\n vi,\n zh,\n};\n"],"names":["UUID_RE","assertResourceId","id","AIdolRepository","BaseCrudRepository","aidolSchema","variables","url","raw","aidolCreateResponseSchema","params","fetchOptions","request","rawResponse","imageGenerationResponseSchema","generateResponseSchema","z","ChatroomRepository","chatroomSchema","chatroomId","options","queryString","messageSchema","content","companionId","CompanionRepository","companionSchema","LeadsRepository","apiService","response","leadResponseSchema","STORAGE_KEY","schema","readStorage","stored","parsed","result","error","writeStorage","data","LocalChatroomIdsRepository","AIDOL_NS","aidolTranslations","en","es","ja","ko","th","tl","vi","zh"],"mappings":"gKAAMA,EACJ,kEAMK,SAASC,EAAiBC,EAA2B,CAC1D,GAAI,OAAOA,GAAO,UAAY,CAACF,EAAQ,KAAKE,CAAE,EAC5C,MAAM,IAAI,MAAM,wBAAwB,OAAOA,CAAE,CAAC,EAAE,CAExD,CCMO,MAAMC,UAAwBC,EAAAA,kBAA0B,CAAxD,aAAA,CAAA,MAAA,GAAA,SAAA,EACL,KAAS,SAAW,QAAA,CAEV,eAAgB,CACxB,OAAOC,EAAAA,WACT,CAEA,MAAM,YAAYC,EAAsD,CACtE,MAAMC,EAAM,KAAK,WAAW,SAAS,KAAK,QAAQ,EAC5CC,EAAM,MAAM,KAAK,WAAW,QAAQD,EAAK,CAC7C,OAAQ,OACR,QAAS,CAAE,eAAgB,kBAAA,EAC3B,KAAM,KAAK,UAAUD,CAAS,CAAA,CAC/B,EACD,OAAOG,EAAAA,0BAA0B,MAAMD,CAAG,CAC5C,CAEA,MAAM,OACJE,EACAC,EAC0B,CAC1BV,EAAiBS,EAAO,EAAE,EAC1B,MAAMH,EAAM,GAAG,KAAK,WAAW,SAAS,KAAK,QAAQ,CAAC,IAAIG,EAAO,EAAE,GAC7DF,EAAM,MAAM,KAAK,WAAW,QAAQD,EAAKI,CAAY,EAC3D,MAAO,CAAE,KAAMN,EAAAA,YAAY,MAAMG,CAAG,CAAA,CACtC,CAEA,MAAM,OAA6CE,EAGtB,CAC3BT,EAAiBS,EAAO,EAAE,EAC1B,MAAMH,EAAM,GAAG,KAAK,WAAW,SAAS,KAAK,QAAQ,CAAC,IAAIG,EAAO,EAAE,GAC7DF,EAAM,MAAM,KAAK,WAAW,QAAQD,EAAK,CAC7C,OAAQ,QACR,QAAS,CAAE,eAAgB,kBAAA,EAC3B,KAAM,KAAK,UAAUG,EAAO,SAAS,CAAA,CACtC,EACD,MAAO,CAAE,KAAML,EAAAA,YAAY,MAAMG,CAAG,CAAA,CACtC,CAEA,MAAM,cACJI,EACAD,EACkC,CAClC,MAAMJ,EAAM,KAAK,WAAW,SAAS,GAAG,KAAK,QAAQ,SAAS,EACxDM,EAAc,MAAM,KAAK,WAAW,QAAQN,EAAK,CACrD,GAAGI,EACH,OAAQ,OACR,QAAS,CACP,eAAgB,kBAAA,EAElB,KAAM,KAAK,UAAUC,CAAO,CAAA,CAC7B,EAED,OAAO,KAAK,iBAAiBC,EAAaC,+BAA6B,CACzE,CACF,CC7DA,MAAMC,EAAyBC,EAAAA,EAAE,OAAO,CACtC,UAAWA,EAAAA,EAAE,OAAA,EACb,QAASA,EAAAA,EAAE,OAAA,CACb,CAAC,EAWM,MAAMC,UAA2Bb,EAAAA,kBAA6B,CAA9D,aAAA,CAAA,MAAA,GAAA,SAAA,EACL,KAAS,SAAW,WAAA,CAEV,eAAgB,CACxB,OAAOc,EAAAA,cACT,CAMA,MAAM,YACJC,EACAC,EACAT,EACoB,CACpB,MAAMD,EAAS,IAAI,gBACfU,GAAS,QAAU,QACrBV,EAAO,IAAI,QAASU,EAAQ,MAAM,UAAU,EAE1CA,GAAS,SAAW,QACtBV,EAAO,IAAI,SAAUU,EAAQ,OAAO,UAAU,EAGhD,MAAMC,EAAcX,EAAO,SAAA,EACrBH,EAAM,KAAK,WAAW,SAC1B,GAAG,KAAK,QAAQ,IAAIY,CAAU,YAAYE,EAAc,IAAIA,CAAW,GAAK,EAAE,EAAA,EAG1ER,EAAc,MAAM,KAAK,WAAW,QAAQN,EAAKI,CAAY,EACnE,OAAO,KAAK,iBAAiBE,EAAaG,EAAAA,EAAE,MAAMM,EAAAA,aAAa,CAAC,CAClE,CAYA,MAAM,YACJH,EACAI,EACAZ,EACkB,CAClB,MAAMJ,EAAM,KAAK,WAAW,SAC1B,GAAG,KAAK,QAAQ,IAAIY,CAAU,WAAA,EAG1BN,EAAc,MAAM,KAAK,WAAW,QAAQN,EAAK,CACrD,GAAGI,EACH,OAAQ,OACR,QAAS,CAAE,eAAgB,kBAAA,EAC3B,KAAM,KAAK,UAAU,CAAE,QAAAY,EAAS,WAAY,OAAQ,CAAA,CACrD,EAED,OAAO,KAAK,iBAAiBV,EAAaS,eAAa,CACzD,CAMA,MAAM,iBACJH,EACAK,EACAb,EAC2B,CAC3B,MAAMJ,EAAM,KAAK,WAAW,SAC1B,GAAG,KAAK,QAAQ,IAAIY,CAAU,eAAeK,CAAW,WAAA,EAGpDX,EAAc,MAAM,KAAK,WAAW,QAAQN,EAAK,CACrD,GAAGI,EACH,OAAQ,OACR,QAAS,CAAE,eAAgB,kBAAA,CAAmB,CAC/C,EAED,OAAO,KAAK,iBAAiBE,EAAaE,CAAsB,CAClE,CACF,CCpGO,MAAMU,UAA4BrB,EAAAA,kBAA8B,CAAhE,aAAA,CAAA,MAAA,GAAA,SAAA,EACL,KAAS,SAAW,YAAA,CAEV,eAAgB,CACxB,OAAOsB,EAAAA,eACT,CAEA,MAAM,cACJd,EACAD,EACkC,CAClC,MAAMJ,EAAM,KAAK,WAAW,SAAS,GAAG,KAAK,QAAQ,SAAS,EACxDM,EAAc,MAAM,KAAK,WAAW,QAAQN,EAAK,CACrD,GAAGI,EACH,OAAQ,OACR,QAAS,CACP,eAAgB,kBAAA,EAElB,KAAM,KAAK,UAAUC,CAAO,CAAA,CAC7B,EAED,OAAO,KAAK,iBAAiBC,EAAaC,+BAA6B,CACzE,CACF,CC7BO,MAAMa,CAAgB,CAG3B,YAA6BC,EAAwB,CAAxB,KAAA,WAAAA,EAF7B,KAAiB,SAAW,OAE0B,CAEtD,MAAM,OAAOhB,EAA6C,CACxD,MAAML,EAAM,KAAK,WAAW,SAAS,KAAK,QAAQ,EAC5CsB,EAAW,MAAM,KAAK,WAAW,QAAQtB,EAAK,CAClD,OAAQ,OACR,QAAS,CAAE,eAAgB,kBAAA,EAC3B,KAAM,KAAK,UAAUK,CAAO,CAAA,CAC7B,EAED,OAAOkB,EAAAA,mBAAmB,MAAMD,CAAQ,CAC1C,CACF,CCjBA,MAAME,EAAc,qBACdC,EAAShB,EAAAA,EAAE,OAAOA,EAAAA,EAAE,SAAUA,EAAAA,EAAE,QAAQ,EAExCiB,EAAc,IAA8B,CAChD,GAAI,OAAO,OAAW,IAAa,MAAO,CAAA,EAE1C,MAAMC,EAAS,aAAa,QAAQH,CAAW,EAC/C,GAAI,CAACG,EAAQ,MAAO,CAAA,EAEpB,GAAI,CACF,MAAMC,EAAS,KAAK,MAAMD,CAAM,EAC1BE,EAASJ,EAAO,UAAUG,CAAM,EACtC,OAAIC,EAAO,QACFA,EAAO,MAEhB,QAAQ,KAAK,sDAAsD,EAC5D,CAAA,EACT,OAASC,EAAO,CACd,GAAIA,aAAiB,YACnB,eAAQ,KAAK,yDAAyD,EAC/D,CAAA,EAET,MAAMA,CACR,CACF,EAEMC,EAAgBC,GAAuC,CAC3D,GAAI,OAAO,OAAW,IACpB,MAAM,IAAI,MACR,oFAAA,EAGJ,aAAa,QAAQR,EAAa,KAAK,UAAUQ,CAAI,CAAC,CACxD,EAMaC,EAA6B,CACxC,cAAchB,EAAoC,CAEhD,OADaS,EAAA,EACDT,CAAW,GAAK,IAC9B,EAEA,cAAcA,EAAqBL,EAA0B,CAC3D,MAAMoB,EAAON,EAAA,EACbM,EAAKf,CAAW,EAAIL,EACpBmB,EAAaC,CAAI,CACnB,EAEA,iBAAiBf,EAA2B,CAC1C,MAAMe,EAAON,EAAA,EACb,OAAOM,EAAKf,CAAW,EACvBc,EAAaC,CAAI,CACnB,CACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yiBCxCaE,GAAW,QAcXC,GAAoB,CAC/B,GAAAC,EACA,GAAAC,GACA,GAAA1C,GACA,GAAA2C,GACA,GAAAC,GACA,GAAAC,GACA,GAAAC,GACA,GAAAC,GACA,GAAAC,EACF"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/lib/assertResourceId.ts","../src/repositories/AIdolRepository.ts","../src/repositories/ChatroomRepository.ts","../src/repositories/CompanionRepository.ts","../src/repositories/LeadsRepository.ts","../src/repositories/LocalChatroomIdsRepository.ts","../src/i18n/translations.ts"],"sourcesContent":["const UUID_RE =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Validates that a resource ID is a well-formed UUID.\n * Prevents client-side path traversal when IDs are interpolated into URLs.\n */\nexport function assertResourceId(id: string | number): void {\n if (typeof id !== \"string\" || !UUID_RE.test(id)) {\n throw new Error(`Invalid resource ID: ${String(id)}`);\n }\n}\n","import { BaseCrudRepository } from \"@aioia/core\";\n\nimport { assertResourceId } from \"../lib/assertResourceId\";\nimport type {\n AIdol,\n AIdolCreate,\n AIdolCreateResponse,\n ImageGenerationRequest,\n ImageGenerationResponse,\n} from \"../schemas\";\nimport {\n aidolCreateResponseSchema,\n aidolSchema,\n imageGenerationResponseSchema,\n} from \"../schemas\";\n\n/** Backend returns unwrapped responses — overrides wrap raw JSON in { data } */\nexport class AIdolRepository extends BaseCrudRepository<AIdol> {\n readonly resource = \"aidols\";\n\n protected getDataSchema() {\n return aidolSchema;\n }\n\n async createAIdol(variables: AIdolCreate): Promise<AIdolCreateResponse> {\n const url = this.apiService.buildUrl(this.resource);\n const raw = await this.apiService.request(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(variables),\n });\n return aidolCreateResponseSchema.parse(raw);\n }\n\n async getOne(\n params: { id: string | number },\n fetchOptions?: RequestInit,\n ): Promise<{ data: AIdol }> {\n assertResourceId(params.id);\n const url = `${this.apiService.buildUrl(this.resource)}/${params.id}`;\n const raw = await this.apiService.request(url, fetchOptions);\n return { data: aidolSchema.parse(raw) };\n }\n\n async update<TVariables = Record<string, unknown>>(params: {\n id: string | number;\n variables: TVariables;\n }): Promise<{ data: AIdol }> {\n assertResourceId(params.id);\n const url = `${this.apiService.buildUrl(this.resource)}/${params.id}`;\n const raw = await this.apiService.request(url, {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(params.variables),\n });\n return { data: aidolSchema.parse(raw) };\n }\n\n async generateImage(\n request: ImageGenerationRequest,\n fetchOptions?: RequestInit,\n ): Promise<ImageGenerationResponse> {\n const url = this.apiService.buildUrl(`${this.resource}/images`);\n const rawResponse = await this.apiService.request(url, {\n ...fetchOptions,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(request),\n });\n\n return this.validateResponse(rawResponse, imageGenerationResponseSchema);\n }\n}\n","import { BaseCrudRepository } from \"@aioia/core\";\nimport { z } from \"zod\";\n\nimport {\n chatroomSchema,\n messageSchema,\n type Chatroom,\n type Message,\n} from \"../schemas\";\n\n/**\n * Response schema for generate AI response endpoint\n */\nconst generateResponseSchema = z.object({\n messageId: z.string(),\n content: z.string(),\n});\n\nexport interface GenerateResponse {\n messageId: string;\n content: string;\n}\n\n/**\n * Repository for Chatroom entities\n * Handles chatroom CRUD and message operations\n */\nexport class ChatroomRepository extends BaseCrudRepository<Chatroom> {\n readonly resource = \"chatrooms\";\n\n protected getDataSchema() {\n return chatroomSchema;\n }\n\n /**\n * Get messages from a chatroom\n * GET /chatrooms/{id}/messages\n */\n async getMessages(\n chatroomId: string,\n options?: { limit?: number; offset?: number },\n fetchOptions?: RequestInit,\n ): Promise<Message[]> {\n const params = new URLSearchParams();\n if (options?.limit !== undefined) {\n params.set(\"limit\", options.limit.toString());\n }\n if (options?.offset !== undefined) {\n params.set(\"offset\", options.offset.toString());\n }\n\n const queryString = params.toString();\n const url = this.apiService.buildUrl(\n `${this.resource}/${chatroomId}/messages${queryString ? `?${queryString}` : \"\"}`,\n );\n\n const rawResponse = await this.apiService.request(url, fetchOptions);\n return this.validateResponse(rawResponse, z.array(messageSchema));\n }\n\n /**\n * Send a message to a chatroom\n * POST /chatrooms/{id}/messages\n *\n * Anonymous ID is automatically sent via httpOnly cookie.\n *\n * @param chatroomId - The chatroom ID\n * @param content - The message content\n * @param fetchOptions - Optional fetch options\n */\n async sendMessage(\n chatroomId: string,\n content: string,\n fetchOptions?: RequestInit,\n ): Promise<Message> {\n const url = this.apiService.buildUrl(\n `${this.resource}/${chatroomId}/messages`,\n );\n\n const rawResponse = await this.apiService.request(url, {\n ...fetchOptions,\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ content, senderType: \"user\" }),\n });\n\n return this.validateResponse(rawResponse, messageSchema);\n }\n\n /**\n * Generate AI response for a chatroom with a specific companion\n * POST /chatrooms/{id}/companions/{companionId}/response\n */\n async generateResponse(\n chatroomId: string,\n companionId: string,\n fetchOptions?: RequestInit,\n ): Promise<GenerateResponse> {\n const url = this.apiService.buildUrl(\n `${this.resource}/${chatroomId}/companions/${companionId}/response`,\n );\n\n const rawResponse = await this.apiService.request(url, {\n ...fetchOptions,\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n });\n\n return this.validateResponse(rawResponse, generateResponseSchema);\n }\n}\n","import { BaseCrudRepository } from \"@aioia/core\";\n\nimport type {\n Companion,\n ImageGenerationRequest,\n ImageGenerationResponse,\n} from \"../schemas\";\nimport { companionSchema, imageGenerationResponseSchema } from \"../schemas\";\n\n/** CRUD operations use BaseCrudRepository (wrapped { data: T } responses) */\nexport class CompanionRepository extends BaseCrudRepository<Companion> {\n readonly resource = \"companions\";\n\n protected getDataSchema() {\n return companionSchema;\n }\n\n async generateImage(\n request: ImageGenerationRequest,\n fetchOptions?: RequestInit,\n ): Promise<ImageGenerationResponse> {\n const url = this.apiService.buildUrl(`${this.resource}/images`);\n const rawResponse = await this.apiService.request(url, {\n ...fetchOptions,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(request),\n });\n\n return this.validateResponse(rawResponse, imageGenerationResponseSchema);\n }\n}\n","import { z } from \"zod\";\n\nimport type { LeadRequest, LeadResponse } from \"@/schemas\";\nimport { leadResponseSchema } from \"@/schemas\";\nimport type { ApiService } from \"@/services/ApiService\";\n\nexport class LeadsRepository {\n private readonly resource = \"leads\";\n\n constructor(private readonly apiService: ApiService) {}\n\n async create(request: LeadRequest): Promise<LeadResponse> {\n const url = this.apiService.buildUrl(this.resource);\n const response = await this.apiService.request(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(request),\n });\n\n const wrapped = z.object({ data: leadResponseSchema }).parse(response);\n return wrapped.data;\n }\n}\n","import { z } from \"zod\";\n\nconst STORAGE_KEY = \"aidol_chatroom_ids\";\nconst schema = z.record(z.string(), z.string());\n\nconst readStorage = (): Record<string, string> => {\n if (typeof window === \"undefined\") return {};\n\n const stored = localStorage.getItem(STORAGE_KEY);\n if (!stored) return {};\n\n try {\n const parsed = JSON.parse(stored);\n const result = schema.safeParse(parsed);\n if (result.success) {\n return result.data;\n }\n console.warn(`[LocalChatroomIdsRepository] Invalid data, resetting`);\n return {};\n } catch (error) {\n if (error instanceof SyntaxError) {\n console.warn(`[LocalChatroomIdsRepository] Failed to parse, resetting`);\n return {};\n }\n throw error;\n }\n};\n\nconst writeStorage = (data: Record<string, string>): void => {\n if (typeof window === \"undefined\") {\n throw new Error(\n \"LocalChatroomIdsRepository write operations can only be called on the client side.\",\n );\n }\n localStorage.setItem(STORAGE_KEY, JSON.stringify(data));\n};\n\n/**\n * Repository for managing chatroom IDs in localStorage.\n * Maps companionId to chatroomId for persistence across sessions.\n */\nexport const LocalChatroomIdsRepository = {\n getChatroomId(companionId: string): string | null {\n const data = readStorage();\n return data[companionId] ?? null;\n },\n\n setChatroomId(companionId: string, chatroomId: string): void {\n const data = readStorage();\n data[companionId] = chatroomId;\n writeStorage(data);\n },\n\n removeChatroomId(companionId: string): void {\n const data = readStorage();\n delete data[companionId];\n writeStorage(data);\n },\n};\n","/**\n * Server-safe exports for aidol translations.\n *\n * This module exports only JSON resources and constants,\n * avoiding React-specific code that would break SSR.\n */\n\nimport en from \"./locales/en/aidol.json\";\nimport es from \"./locales/es/aidol.json\";\nimport id from \"./locales/id/aidol.json\";\nimport ja from \"./locales/ja/aidol.json\";\nimport ko from \"./locales/ko/aidol.json\";\nimport th from \"./locales/th/aidol.json\";\nimport tl from \"./locales/tl/aidol.json\";\nimport vi from \"./locales/vi/aidol.json\";\nimport zh from \"./locales/zh/aidol.json\";\n\n/** AIdol namespace */\nexport const AIDOL_NS = \"aidol\";\n\n/**\n * Translation resources for the aidol namespace.\n * Use with i18next.addResourceBundle(lang, 'aidol', translations)\n *\n * @example\n * import { aidolTranslations, AIDOL_NS } from 'aidol/locale';\n *\n * // Add to existing i18n instance\n * Object.entries(aidolTranslations).forEach(([lang, resources]) => {\n * i18n.addResourceBundle(lang, AIDOL_NS, resources);\n * });\n */\nexport const aidolTranslations = {\n en,\n es,\n id,\n ja,\n ko,\n th,\n tl,\n vi,\n zh,\n};\n"],"names":["UUID_RE","assertResourceId","id","AIdolRepository","BaseCrudRepository","aidolSchema","variables","url","raw","aidolCreateResponseSchema","params","fetchOptions","request","rawResponse","imageGenerationResponseSchema","generateResponseSchema","z","ChatroomRepository","chatroomSchema","chatroomId","options","queryString","messageSchema","content","companionId","CompanionRepository","companionSchema","LeadsRepository","apiService","response","leadResponseSchema","STORAGE_KEY","schema","readStorage","stored","parsed","result","error","writeStorage","data","LocalChatroomIdsRepository","AIDOL_NS","aidolTranslations","en","es","ja","ko","th","tl","vi","zh"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -136,7 +136,7 @@ class Ct {
136
136
  headers: { "Content-Type": "application/json" },
137
137
  body: JSON.stringify(e)
138
138
  });
139
- return P.parse(n);
139
+ return r.object({ data: P }).parse(n).data;
140
140
  }
141
141
  }
142
142
  const u = "aidol_chatroom_ids", R = r.record(r.string(), r.string()), s = () => {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/lib/assertResourceId.ts","../src/repositories/AIdolRepository.ts","../src/repositories/ChatroomRepository.ts","../src/repositories/CompanionRepository.ts","../src/repositories/LeadsRepository.ts","../src/repositories/LocalChatroomIdsRepository.ts","../src/i18n/translations.ts"],"sourcesContent":["const UUID_RE =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Validates that a resource ID is a well-formed UUID.\n * Prevents client-side path traversal when IDs are interpolated into URLs.\n */\nexport function assertResourceId(id: string | number): void {\n if (typeof id !== \"string\" || !UUID_RE.test(id)) {\n throw new Error(`Invalid resource ID: ${String(id)}`);\n }\n}\n","import { BaseCrudRepository } from \"@aioia/core\";\n\nimport { assertResourceId } from \"../lib/assertResourceId\";\nimport type {\n AIdol,\n AIdolCreate,\n AIdolCreateResponse,\n ImageGenerationRequest,\n ImageGenerationResponse,\n} from \"../schemas\";\nimport {\n aidolCreateResponseSchema,\n aidolSchema,\n imageGenerationResponseSchema,\n} from \"../schemas\";\n\n/** Backend returns unwrapped responses — overrides wrap raw JSON in { data } */\nexport class AIdolRepository extends BaseCrudRepository<AIdol> {\n readonly resource = \"aidols\";\n\n protected getDataSchema() {\n return aidolSchema;\n }\n\n async createAIdol(variables: AIdolCreate): Promise<AIdolCreateResponse> {\n const url = this.apiService.buildUrl(this.resource);\n const raw = await this.apiService.request(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(variables),\n });\n return aidolCreateResponseSchema.parse(raw);\n }\n\n async getOne(\n params: { id: string | number },\n fetchOptions?: RequestInit,\n ): Promise<{ data: AIdol }> {\n assertResourceId(params.id);\n const url = `${this.apiService.buildUrl(this.resource)}/${params.id}`;\n const raw = await this.apiService.request(url, fetchOptions);\n return { data: aidolSchema.parse(raw) };\n }\n\n async update<TVariables = Record<string, unknown>>(params: {\n id: string | number;\n variables: TVariables;\n }): Promise<{ data: AIdol }> {\n assertResourceId(params.id);\n const url = `${this.apiService.buildUrl(this.resource)}/${params.id}`;\n const raw = await this.apiService.request(url, {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(params.variables),\n });\n return { data: aidolSchema.parse(raw) };\n }\n\n async generateImage(\n request: ImageGenerationRequest,\n fetchOptions?: RequestInit,\n ): Promise<ImageGenerationResponse> {\n const url = this.apiService.buildUrl(`${this.resource}/images`);\n const rawResponse = await this.apiService.request(url, {\n ...fetchOptions,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(request),\n });\n\n return this.validateResponse(rawResponse, imageGenerationResponseSchema);\n }\n}\n","import { BaseCrudRepository } from \"@aioia/core\";\nimport { z } from \"zod\";\n\nimport {\n chatroomSchema,\n messageSchema,\n type Chatroom,\n type Message,\n} from \"../schemas\";\n\n/**\n * Response schema for generate AI response endpoint\n */\nconst generateResponseSchema = z.object({\n messageId: z.string(),\n content: z.string(),\n});\n\nexport interface GenerateResponse {\n messageId: string;\n content: string;\n}\n\n/**\n * Repository for Chatroom entities\n * Handles chatroom CRUD and message operations\n */\nexport class ChatroomRepository extends BaseCrudRepository<Chatroom> {\n readonly resource = \"chatrooms\";\n\n protected getDataSchema() {\n return chatroomSchema;\n }\n\n /**\n * Get messages from a chatroom\n * GET /chatrooms/{id}/messages\n */\n async getMessages(\n chatroomId: string,\n options?: { limit?: number; offset?: number },\n fetchOptions?: RequestInit,\n ): Promise<Message[]> {\n const params = new URLSearchParams();\n if (options?.limit !== undefined) {\n params.set(\"limit\", options.limit.toString());\n }\n if (options?.offset !== undefined) {\n params.set(\"offset\", options.offset.toString());\n }\n\n const queryString = params.toString();\n const url = this.apiService.buildUrl(\n `${this.resource}/${chatroomId}/messages${queryString ? `?${queryString}` : \"\"}`,\n );\n\n const rawResponse = await this.apiService.request(url, fetchOptions);\n return this.validateResponse(rawResponse, z.array(messageSchema));\n }\n\n /**\n * Send a message to a chatroom\n * POST /chatrooms/{id}/messages\n *\n * Anonymous ID is automatically sent via httpOnly cookie.\n *\n * @param chatroomId - The chatroom ID\n * @param content - The message content\n * @param fetchOptions - Optional fetch options\n */\n async sendMessage(\n chatroomId: string,\n content: string,\n fetchOptions?: RequestInit,\n ): Promise<Message> {\n const url = this.apiService.buildUrl(\n `${this.resource}/${chatroomId}/messages`,\n );\n\n const rawResponse = await this.apiService.request(url, {\n ...fetchOptions,\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ content, senderType: \"user\" }),\n });\n\n return this.validateResponse(rawResponse, messageSchema);\n }\n\n /**\n * Generate AI response for a chatroom with a specific companion\n * POST /chatrooms/{id}/companions/{companionId}/response\n */\n async generateResponse(\n chatroomId: string,\n companionId: string,\n fetchOptions?: RequestInit,\n ): Promise<GenerateResponse> {\n const url = this.apiService.buildUrl(\n `${this.resource}/${chatroomId}/companions/${companionId}/response`,\n );\n\n const rawResponse = await this.apiService.request(url, {\n ...fetchOptions,\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n });\n\n return this.validateResponse(rawResponse, generateResponseSchema);\n }\n}\n","import { BaseCrudRepository } from \"@aioia/core\";\n\nimport type {\n Companion,\n ImageGenerationRequest,\n ImageGenerationResponse,\n} from \"../schemas\";\nimport { companionSchema, imageGenerationResponseSchema } from \"../schemas\";\n\n/** CRUD operations use BaseCrudRepository (wrapped { data: T } responses) */\nexport class CompanionRepository extends BaseCrudRepository<Companion> {\n readonly resource = \"companions\";\n\n protected getDataSchema() {\n return companionSchema;\n }\n\n async generateImage(\n request: ImageGenerationRequest,\n fetchOptions?: RequestInit,\n ): Promise<ImageGenerationResponse> {\n const url = this.apiService.buildUrl(`${this.resource}/images`);\n const rawResponse = await this.apiService.request(url, {\n ...fetchOptions,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(request),\n });\n\n return this.validateResponse(rawResponse, imageGenerationResponseSchema);\n }\n}\n","import type { LeadRequest, LeadResponse } from \"@/schemas\";\nimport { leadResponseSchema } from \"@/schemas\";\nimport type { ApiService } from \"@/services/ApiService\";\n\nexport class LeadsRepository {\n private readonly resource = \"leads\";\n\n constructor(private readonly apiService: ApiService) {}\n\n async create(request: LeadRequest): Promise<LeadResponse> {\n const url = this.apiService.buildUrl(this.resource);\n const response = await this.apiService.request(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(request),\n });\n\n return leadResponseSchema.parse(response);\n }\n}\n","import { z } from \"zod\";\n\nconst STORAGE_KEY = \"aidol_chatroom_ids\";\nconst schema = z.record(z.string(), z.string());\n\nconst readStorage = (): Record<string, string> => {\n if (typeof window === \"undefined\") return {};\n\n const stored = localStorage.getItem(STORAGE_KEY);\n if (!stored) return {};\n\n try {\n const parsed = JSON.parse(stored);\n const result = schema.safeParse(parsed);\n if (result.success) {\n return result.data;\n }\n console.warn(`[LocalChatroomIdsRepository] Invalid data, resetting`);\n return {};\n } catch (error) {\n if (error instanceof SyntaxError) {\n console.warn(`[LocalChatroomIdsRepository] Failed to parse, resetting`);\n return {};\n }\n throw error;\n }\n};\n\nconst writeStorage = (data: Record<string, string>): void => {\n if (typeof window === \"undefined\") {\n throw new Error(\n \"LocalChatroomIdsRepository write operations can only be called on the client side.\",\n );\n }\n localStorage.setItem(STORAGE_KEY, JSON.stringify(data));\n};\n\n/**\n * Repository for managing chatroom IDs in localStorage.\n * Maps companionId to chatroomId for persistence across sessions.\n */\nexport const LocalChatroomIdsRepository = {\n getChatroomId(companionId: string): string | null {\n const data = readStorage();\n return data[companionId] ?? null;\n },\n\n setChatroomId(companionId: string, chatroomId: string): void {\n const data = readStorage();\n data[companionId] = chatroomId;\n writeStorage(data);\n },\n\n removeChatroomId(companionId: string): void {\n const data = readStorage();\n delete data[companionId];\n writeStorage(data);\n },\n};\n","/**\n * Server-safe exports for aidol translations.\n *\n * This module exports only JSON resources and constants,\n * avoiding React-specific code that would break SSR.\n */\n\nimport en from \"./locales/en/aidol.json\";\nimport es from \"./locales/es/aidol.json\";\nimport id from \"./locales/id/aidol.json\";\nimport ja from \"./locales/ja/aidol.json\";\nimport ko from \"./locales/ko/aidol.json\";\nimport th from \"./locales/th/aidol.json\";\nimport tl from \"./locales/tl/aidol.json\";\nimport vi from \"./locales/vi/aidol.json\";\nimport zh from \"./locales/zh/aidol.json\";\n\n/** AIdol namespace */\nexport const AIDOL_NS = \"aidol\";\n\n/**\n * Translation resources for the aidol namespace.\n * Use with i18next.addResourceBundle(lang, 'aidol', translations)\n *\n * @example\n * import { aidolTranslations, AIDOL_NS } from 'aidol/locale';\n *\n * // Add to existing i18n instance\n * Object.entries(aidolTranslations).forEach(([lang, resources]) => {\n * i18n.addResourceBundle(lang, AIDOL_NS, resources);\n * });\n */\nexport const aidolTranslations = {\n en,\n es,\n id,\n ja,\n ko,\n th,\n tl,\n vi,\n zh,\n};\n"],"names":["UUID_RE","assertResourceId","id","AIdolRepository","BaseCrudRepository","aidolSchema","variables","url","raw","aidolCreateResponseSchema","params","fetchOptions","request","rawResponse","imageGenerationResponseSchema","generateResponseSchema","z","ChatroomRepository","chatroomSchema","chatroomId","options","queryString","messageSchema","content","companionId","CompanionRepository","companionSchema","LeadsRepository","apiService","response","leadResponseSchema","STORAGE_KEY","schema","readStorage","stored","parsed","result","error","writeStorage","data","LocalChatroomIdsRepository","AIDOL_NS","aidolTranslations","en","es","ja","ko","th","tl","vi","zh"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/lib/assertResourceId.ts","../src/repositories/AIdolRepository.ts","../src/repositories/ChatroomRepository.ts","../src/repositories/CompanionRepository.ts","../src/repositories/LeadsRepository.ts","../src/repositories/LocalChatroomIdsRepository.ts","../src/i18n/translations.ts"],"sourcesContent":["const UUID_RE =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Validates that a resource ID is a well-formed UUID.\n * Prevents client-side path traversal when IDs are interpolated into URLs.\n */\nexport function assertResourceId(id: string | number): void {\n if (typeof id !== \"string\" || !UUID_RE.test(id)) {\n throw new Error(`Invalid resource ID: ${String(id)}`);\n }\n}\n","import { BaseCrudRepository } from \"@aioia/core\";\n\nimport { assertResourceId } from \"../lib/assertResourceId\";\nimport type {\n AIdol,\n AIdolCreate,\n AIdolCreateResponse,\n ImageGenerationRequest,\n ImageGenerationResponse,\n} from \"../schemas\";\nimport {\n aidolCreateResponseSchema,\n aidolSchema,\n imageGenerationResponseSchema,\n} from \"../schemas\";\n\n/** Backend returns unwrapped responses — overrides wrap raw JSON in { data } */\nexport class AIdolRepository extends BaseCrudRepository<AIdol> {\n readonly resource = \"aidols\";\n\n protected getDataSchema() {\n return aidolSchema;\n }\n\n async createAIdol(variables: AIdolCreate): Promise<AIdolCreateResponse> {\n const url = this.apiService.buildUrl(this.resource);\n const raw = await this.apiService.request(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(variables),\n });\n return aidolCreateResponseSchema.parse(raw);\n }\n\n async getOne(\n params: { id: string | number },\n fetchOptions?: RequestInit,\n ): Promise<{ data: AIdol }> {\n assertResourceId(params.id);\n const url = `${this.apiService.buildUrl(this.resource)}/${params.id}`;\n const raw = await this.apiService.request(url, fetchOptions);\n return { data: aidolSchema.parse(raw) };\n }\n\n async update<TVariables = Record<string, unknown>>(params: {\n id: string | number;\n variables: TVariables;\n }): Promise<{ data: AIdol }> {\n assertResourceId(params.id);\n const url = `${this.apiService.buildUrl(this.resource)}/${params.id}`;\n const raw = await this.apiService.request(url, {\n method: \"PATCH\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(params.variables),\n });\n return { data: aidolSchema.parse(raw) };\n }\n\n async generateImage(\n request: ImageGenerationRequest,\n fetchOptions?: RequestInit,\n ): Promise<ImageGenerationResponse> {\n const url = this.apiService.buildUrl(`${this.resource}/images`);\n const rawResponse = await this.apiService.request(url, {\n ...fetchOptions,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(request),\n });\n\n return this.validateResponse(rawResponse, imageGenerationResponseSchema);\n }\n}\n","import { BaseCrudRepository } from \"@aioia/core\";\nimport { z } from \"zod\";\n\nimport {\n chatroomSchema,\n messageSchema,\n type Chatroom,\n type Message,\n} from \"../schemas\";\n\n/**\n * Response schema for generate AI response endpoint\n */\nconst generateResponseSchema = z.object({\n messageId: z.string(),\n content: z.string(),\n});\n\nexport interface GenerateResponse {\n messageId: string;\n content: string;\n}\n\n/**\n * Repository for Chatroom entities\n * Handles chatroom CRUD and message operations\n */\nexport class ChatroomRepository extends BaseCrudRepository<Chatroom> {\n readonly resource = \"chatrooms\";\n\n protected getDataSchema() {\n return chatroomSchema;\n }\n\n /**\n * Get messages from a chatroom\n * GET /chatrooms/{id}/messages\n */\n async getMessages(\n chatroomId: string,\n options?: { limit?: number; offset?: number },\n fetchOptions?: RequestInit,\n ): Promise<Message[]> {\n const params = new URLSearchParams();\n if (options?.limit !== undefined) {\n params.set(\"limit\", options.limit.toString());\n }\n if (options?.offset !== undefined) {\n params.set(\"offset\", options.offset.toString());\n }\n\n const queryString = params.toString();\n const url = this.apiService.buildUrl(\n `${this.resource}/${chatroomId}/messages${queryString ? `?${queryString}` : \"\"}`,\n );\n\n const rawResponse = await this.apiService.request(url, fetchOptions);\n return this.validateResponse(rawResponse, z.array(messageSchema));\n }\n\n /**\n * Send a message to a chatroom\n * POST /chatrooms/{id}/messages\n *\n * Anonymous ID is automatically sent via httpOnly cookie.\n *\n * @param chatroomId - The chatroom ID\n * @param content - The message content\n * @param fetchOptions - Optional fetch options\n */\n async sendMessage(\n chatroomId: string,\n content: string,\n fetchOptions?: RequestInit,\n ): Promise<Message> {\n const url = this.apiService.buildUrl(\n `${this.resource}/${chatroomId}/messages`,\n );\n\n const rawResponse = await this.apiService.request(url, {\n ...fetchOptions,\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ content, senderType: \"user\" }),\n });\n\n return this.validateResponse(rawResponse, messageSchema);\n }\n\n /**\n * Generate AI response for a chatroom with a specific companion\n * POST /chatrooms/{id}/companions/{companionId}/response\n */\n async generateResponse(\n chatroomId: string,\n companionId: string,\n fetchOptions?: RequestInit,\n ): Promise<GenerateResponse> {\n const url = this.apiService.buildUrl(\n `${this.resource}/${chatroomId}/companions/${companionId}/response`,\n );\n\n const rawResponse = await this.apiService.request(url, {\n ...fetchOptions,\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n });\n\n return this.validateResponse(rawResponse, generateResponseSchema);\n }\n}\n","import { BaseCrudRepository } from \"@aioia/core\";\n\nimport type {\n Companion,\n ImageGenerationRequest,\n ImageGenerationResponse,\n} from \"../schemas\";\nimport { companionSchema, imageGenerationResponseSchema } from \"../schemas\";\n\n/** CRUD operations use BaseCrudRepository (wrapped { data: T } responses) */\nexport class CompanionRepository extends BaseCrudRepository<Companion> {\n readonly resource = \"companions\";\n\n protected getDataSchema() {\n return companionSchema;\n }\n\n async generateImage(\n request: ImageGenerationRequest,\n fetchOptions?: RequestInit,\n ): Promise<ImageGenerationResponse> {\n const url = this.apiService.buildUrl(`${this.resource}/images`);\n const rawResponse = await this.apiService.request(url, {\n ...fetchOptions,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(request),\n });\n\n return this.validateResponse(rawResponse, imageGenerationResponseSchema);\n }\n}\n","import { z } from \"zod\";\n\nimport type { LeadRequest, LeadResponse } from \"@/schemas\";\nimport { leadResponseSchema } from \"@/schemas\";\nimport type { ApiService } from \"@/services/ApiService\";\n\nexport class LeadsRepository {\n private readonly resource = \"leads\";\n\n constructor(private readonly apiService: ApiService) {}\n\n async create(request: LeadRequest): Promise<LeadResponse> {\n const url = this.apiService.buildUrl(this.resource);\n const response = await this.apiService.request(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(request),\n });\n\n const wrapped = z.object({ data: leadResponseSchema }).parse(response);\n return wrapped.data;\n }\n}\n","import { z } from \"zod\";\n\nconst STORAGE_KEY = \"aidol_chatroom_ids\";\nconst schema = z.record(z.string(), z.string());\n\nconst readStorage = (): Record<string, string> => {\n if (typeof window === \"undefined\") return {};\n\n const stored = localStorage.getItem(STORAGE_KEY);\n if (!stored) return {};\n\n try {\n const parsed = JSON.parse(stored);\n const result = schema.safeParse(parsed);\n if (result.success) {\n return result.data;\n }\n console.warn(`[LocalChatroomIdsRepository] Invalid data, resetting`);\n return {};\n } catch (error) {\n if (error instanceof SyntaxError) {\n console.warn(`[LocalChatroomIdsRepository] Failed to parse, resetting`);\n return {};\n }\n throw error;\n }\n};\n\nconst writeStorage = (data: Record<string, string>): void => {\n if (typeof window === \"undefined\") {\n throw new Error(\n \"LocalChatroomIdsRepository write operations can only be called on the client side.\",\n );\n }\n localStorage.setItem(STORAGE_KEY, JSON.stringify(data));\n};\n\n/**\n * Repository for managing chatroom IDs in localStorage.\n * Maps companionId to chatroomId for persistence across sessions.\n */\nexport const LocalChatroomIdsRepository = {\n getChatroomId(companionId: string): string | null {\n const data = readStorage();\n return data[companionId] ?? null;\n },\n\n setChatroomId(companionId: string, chatroomId: string): void {\n const data = readStorage();\n data[companionId] = chatroomId;\n writeStorage(data);\n },\n\n removeChatroomId(companionId: string): void {\n const data = readStorage();\n delete data[companionId];\n writeStorage(data);\n },\n};\n","/**\n * Server-safe exports for aidol translations.\n *\n * This module exports only JSON resources and constants,\n * avoiding React-specific code that would break SSR.\n */\n\nimport en from \"./locales/en/aidol.json\";\nimport es from \"./locales/es/aidol.json\";\nimport id from \"./locales/id/aidol.json\";\nimport ja from \"./locales/ja/aidol.json\";\nimport ko from \"./locales/ko/aidol.json\";\nimport th from \"./locales/th/aidol.json\";\nimport tl from \"./locales/tl/aidol.json\";\nimport vi from \"./locales/vi/aidol.json\";\nimport zh from \"./locales/zh/aidol.json\";\n\n/** AIdol namespace */\nexport const AIDOL_NS = \"aidol\";\n\n/**\n * Translation resources for the aidol namespace.\n * Use with i18next.addResourceBundle(lang, 'aidol', translations)\n *\n * @example\n * import { aidolTranslations, AIDOL_NS } from 'aidol/locale';\n *\n * // Add to existing i18n instance\n * Object.entries(aidolTranslations).forEach(([lang, resources]) => {\n * i18n.addResourceBundle(lang, AIDOL_NS, resources);\n * });\n */\nexport const aidolTranslations = {\n en,\n es,\n id,\n ja,\n ko,\n th,\n tl,\n vi,\n zh,\n};\n"],"names":["UUID_RE","assertResourceId","id","AIdolRepository","BaseCrudRepository","aidolSchema","variables","url","raw","aidolCreateResponseSchema","params","fetchOptions","request","rawResponse","imageGenerationResponseSchema","generateResponseSchema","z","ChatroomRepository","chatroomSchema","chatroomId","options","queryString","messageSchema","content","companionId","CompanionRepository","companionSchema","LeadsRepository","apiService","response","leadResponseSchema","STORAGE_KEY","schema","readStorage","stored","parsed","result","error","writeStorage","data","LocalChatroomIdsRepository","AIDOL_NS","aidolTranslations","en","es","ja","ko","th","tl","vi","zh"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"LeadsRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/LeadsRepository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,qBAAa,eAAe;IAGd,OAAO,CAAC,QAAQ,CAAC,UAAU;IAFvC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;gBAEP,UAAU,EAAE,UAAU;IAE7C,MAAM,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;CAU1D"}
1
+ {"version":3,"file":"LeadsRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/LeadsRepository.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,qBAAa,eAAe;IAGd,OAAO,CAAC,QAAQ,CAAC,UAAU;IAFvC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;gBAEP,UAAU,EAAE,UAAU;IAE7C,MAAM,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;CAW1D"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aidol",
3
- "version": "1.4.0",
3
+ "version": "1.4.1",
4
4
  "description": "React components for AIdol - Create and chat with your own AI idol group",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",