isaikr 0.0.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/README.md +35 -0
- package/cdn/api.js +19 -0
- package/cdn/character.js +254 -0
- package/cdn/chat.js +33 -0
- package/cdn/code-editor.js +1131 -0
- package/cdn/community-compose.js +270 -0
- package/cdn/games/2048/index.html +12 -0
- package/cdn/games/breakout/index.html +13 -0
- package/cdn/games/clicker/index.html +26 -0
- package/cdn/games/flappy/index.html +11 -0
- package/cdn/games/memory/index.html +34 -0
- package/cdn/games/pong/index.html +13 -0
- package/cdn/games/reaction/index.html +38 -0
- package/cdn/games/runner/index.html +11 -0
- package/cdn/games/snake/index.html +11 -0
- package/cdn/games/tetris/index.html +14 -0
- package/cdn/games/whack/index.html +8 -0
- package/cdn/go.js +126 -0
- package/cdn/go2.js +127 -0
- package/cdn/header3_behavior.js +1167 -0
- package/cdn/header3_layout.js +1004 -0
- package/cdn/header3_layout.js.bak +1004 -0
- package/cdn/header3_style.css +3524 -0
- package/cdn/header3_style.css.bak +3514 -0
- package/cdn/lang.js +198 -0
- package/cdn/loading.js +143 -0
- package/cdn/loading2.js +144 -0
- package/cdn/local-model.js +2941 -0
- package/cdn/main.js +4 -0
- package/cdn/main_asset.js +1849 -0
- package/cdn/main_asset.js.bak +6999 -0
- package/cdn/main_index.css +287 -0
- package/cdn/re_board3.css +733 -0
- package/cdn/re_board3.js +734 -0
- package/cdn/re_chat_tts.js +652 -0
- package/cdn/re_local_runtime.js +2246 -0
- package/cdn/re_local_runtime.js.bak +2246 -0
- package/cdn/re_share.js +577 -0
- package/cdn/re_voice.js +542 -0
- package/cdn/utils.js +36 -0
- package/cdn/view.js +321 -0
- package/header3_behavior.js +804 -0
- package/header3_layout.js +998 -0
- package/header3_style.css +2740 -0
- package/index.js +0 -0
- package/lang.js +179 -0
- package/main_asset.js +2416 -0
- package/main_index.css +274 -0
- package/package.json +14 -0
- package/re_chat_tts.js +1419 -0
- package/re_voice.js +430 -0
package/cdn/re_board3.js
ADDED
|
@@ -0,0 +1,734 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RE:BOARD 3.0 - Global Advanced AI Recommendation Engine
|
|
3
|
+
* Feature: Absolute Tier System, Gender Banishment, Context Pattern Matching
|
|
4
|
+
* Default Tab Force: World News + Stable Random Advertisement & Infinite Scroll
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// =========================================================================
|
|
8
|
+
// [1] 전역 헬퍼 및 DOM 유틸리티
|
|
9
|
+
// =========================================================================
|
|
10
|
+
let macyInstances = { store: null, gallery: null, forum: null, code: null, news: null };
|
|
11
|
+
|
|
12
|
+
function toggleSearchMode(a){const n=document.getElementById('main-nav-bar'),i=document.getElementById('store-search-input');if(n&&i){a?(n.classList.add('search-mode'),setTimeout(()=>i.focus(),50)):(n.classList.remove('search-mode'),i.value='',i.blur(),window.$boardApp&&window.$boardApp.searchQuery!==''&&window.$boardApp.execSearch(''));}}
|
|
13
|
+
function toggleNewsSearchMode(a){const n=document.getElementById('board-news-nav-bar'),i=document.getElementById('news-search-input');if(n&&i){a?(n.classList.add('search-mode'),setTimeout(()=>i.focus(),50)):(n.classList.remove('search-mode'),i.blur());}}
|
|
14
|
+
function toggleCodeSearchMode(a){const n=document.getElementById('board-code-nav-bar'),i=document.getElementById('code-search-input');if(n&&i){a?(n.classList.add('search-mode'),setTimeout(()=>i.focus(),50)):(n.classList.remove('search-mode'),i.blur());}}
|
|
15
|
+
function toggleGallerySearchMode(a){const n=document.getElementById('board-gallery-nav-bar'),i=document.getElementById('gallery-search-input');if(n&&i){a?(n.classList.add('search-mode'),setTimeout(()=>i.focus(),50)):(n.classList.remove('search-mode'),i.blur());}}
|
|
16
|
+
function toggleForumSearchMode(a){const n=document.getElementById('board-forum-nav-bar'),i=document.getElementById('forum-search-input');if(n&&i){a?(n.classList.add('search-mode'),setTimeout(()=>i.focus(),50)):(n.classList.remove('search-mode'),i.blur());}}
|
|
17
|
+
function toggleStoreSearchMode(a){const n=document.getElementById('board-store-nav-bar'),i=document.getElementById('store-search-input');if(n&&i){a?(n.classList.add('search-mode'),setTimeout(()=>i.focus(),50)):(n.classList.remove('search-mode'),i.blur());}}
|
|
18
|
+
|
|
19
|
+
function doSearch(k){if(window.$boardApp)window.$boardApp.execSearch(k);}
|
|
20
|
+
function previewAppIcon(i){if(i.files&&i.files[0]){const r=new FileReader();r.onload=e=>{const pv=document.getElementById('app-icon-preview'),ph=document.getElementById('app-icon-placeholder');if(pv){pv.src=e.target.result;pv.classList.remove('hidden');}if(ph)ph.classList.add('hidden');};r.readAsDataURL(i.files[0]);}}
|
|
21
|
+
async function runApp(id){window.scrollTo({top:0,behavior:'smooth'});if(typeof showToast==='function')showToast("Loading...");if(typeof loadAppDetails==='function')await loadAppDetails(id);}
|
|
22
|
+
window.runApp = runApp;
|
|
23
|
+
|
|
24
|
+
(function initNotice(){const e=document.getElementById('board-spirit-notice');if(!e)return;const n=(window.__RE_BOARD3_CONFIG__&&window.__RE_BOARD3_CONFIG__.boardSpiritNoticeItems)||[];const s=n[Math.floor(Math.random()*n.length)];if(s){if(s.text)e.textContent=s.text;if(s.url)e.dataset.href=s.url;}const g=()=>{if(e.dataset.href)window.location.href=e.dataset.href;};e.addEventListener('click',g);setTimeout(()=>{e.classList.add('show');setTimeout(()=>{e.classList.add('fade-out');setTimeout(()=>{e.classList.remove('show','fade-out');e.style.display='none';},600);},2000);},500);})();
|
|
25
|
+
|
|
26
|
+
function buildGalleryShareContext(item) {
|
|
27
|
+
const url = new URL(window.location.href);
|
|
28
|
+
const shareId = item && (item.share_id || item.id) ? String(item.share_id || item.id) : '';
|
|
29
|
+
if (shareId) url.searchParams.set('v', shareId);
|
|
30
|
+
return {
|
|
31
|
+
url: url.toString(),
|
|
32
|
+
image: String(item && item.image_url ? item.image_url : ''),
|
|
33
|
+
title: String((item && (item.nickname || item.safeIp || item.title || item.prompt)) || 'Gallery image'),
|
|
34
|
+
description: String((item && (item.content || item.prompt || item.title)) || '').trim(),
|
|
35
|
+
source: String((item && (item.nickname || item.safeIp)) || 'User')
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function shareGalleryToSocial(network) {
|
|
39
|
+
const ctx = window.__RE_BOARD3_SHARE_CONTEXT__ || {};
|
|
40
|
+
const pageUrl = encodeURIComponent(ctx.url || window.location.href);
|
|
41
|
+
const title = encodeURIComponent(ctx.title || 'Gallery image');
|
|
42
|
+
const image = encodeURIComponent(ctx.image || '');
|
|
43
|
+
const description = encodeURIComponent(ctx.description || ctx.title || 'Gallery image');
|
|
44
|
+
const shareUrls = {
|
|
45
|
+
x: `https://twitter.com/intent/tweet?url=${pageUrl}&text=${title}`,
|
|
46
|
+
facebook: `https://www.facebook.com/sharer/sharer.php?u=${pageUrl}`,
|
|
47
|
+
pinterest: `https://pinterest.com/pin/create/button/?url=${pageUrl}&media=${image}&description=${description}`,
|
|
48
|
+
tumblr: `https://www.tumblr.com/widgets/share/tool?canonicalUrl=${pageUrl}&title=${title}&caption=${description}&content=${image}`
|
|
49
|
+
};
|
|
50
|
+
const target = shareUrls[String(network || '').toLowerCase()];
|
|
51
|
+
if (!target) return;
|
|
52
|
+
window.open(target, '_blank', 'noopener,noreferrer,width=640,height=720');
|
|
53
|
+
}
|
|
54
|
+
window.shareGalleryToSocial = shareGalleryToSocial;
|
|
55
|
+
|
|
56
|
+
// =========================================================================
|
|
57
|
+
// [2] Alpine.js 애플리케이션
|
|
58
|
+
// =========================================================================
|
|
59
|
+
document.addEventListener('alpine:init', () => {
|
|
60
|
+
Alpine.data('boardApp', () => ({
|
|
61
|
+
curTab: (window.__RE_BOARD3_CONFIG__&&window.__RE_BOARD3_CONFIG__.initialBoardTab)||'news',
|
|
62
|
+
newsCategories: (window.__RE_BOARD3_CONFIG__&&window.__RE_BOARD3_CONFIG__.newsCategories)||[],
|
|
63
|
+
|
|
64
|
+
galleryCategories:[
|
|
65
|
+
{key:'all', name:'All', icon:'ri-apps-line', q:''},
|
|
66
|
+
{key:'anime', name:'Anime', icon:'ri-user-smile-line', q:'anime'},
|
|
67
|
+
{key:'realistic', name:'Realistic', icon:'ri-camera-line', q:'realistic'},
|
|
68
|
+
{key:'girl', name:'Girl', icon:'ri-women-line', q:'girl'},
|
|
69
|
+
{key:'boy', name:'Boy', icon:'ri-men-line', q:'boy'},
|
|
70
|
+
{key:'fantasy', name:'Fantasy', icon:'ri-magic-line', q:'fantasy'},
|
|
71
|
+
{key:'scifi', name:'Sci-Fi', icon:'ri-rocket-line', q:'sci-fi'},
|
|
72
|
+
{key:'cyberpunk', name:'Cyberpunk', icon:'ri-cpu-line', q:'cyberpunk'},
|
|
73
|
+
{key:'landscape', name:'Landscape', icon:'ri-landscape-line', q:'landscape'},
|
|
74
|
+
{key:'animal', name:'Animal', icon:'ri-bear-smile-line', q:'animal'},
|
|
75
|
+
{key:'mecha', name:'Mecha', icon:'ri-robot-2-line', q:'mecha robot'},
|
|
76
|
+
{key:'3d', name:'3D', icon:'ri-box-3-line', q:'3d'},
|
|
77
|
+
{key:'sketch', name:'Sketch', icon:'ri-pencil-line', q:'sketch'}
|
|
78
|
+
],
|
|
79
|
+
forumCategories:[
|
|
80
|
+
{key:'all', name:'All', icon:'ri-apps-line', q:''},
|
|
81
|
+
{key:'general', name:'General', icon:'ri-chat-1-line', q:'general'},
|
|
82
|
+
{key:'humor', name:'Humor', icon:'ri-emotion-laugh-line', q:'humor'},
|
|
83
|
+
{key:'info', name:'Info', icon:'ri-information-line', q:'info'},
|
|
84
|
+
{key:'review', name:'Review', icon:'ri-star-line', q:'review'},
|
|
85
|
+
{key:'qa', name:'Q&A', icon:'ri-question-answer-line', q:'question'},
|
|
86
|
+
{key:'tip', name:'Tip', icon:'ri-lightbulb-flash-line', q:'tip'},
|
|
87
|
+
{key:'news', name:'News', icon:'ri-newspaper-line', q:'news'},
|
|
88
|
+
{key:'discussion', name:'Discussion', icon:'ri-discuss-line', q:'discussion'},
|
|
89
|
+
{key:'feedback', name:'Feedback', icon:'ri-feedback-line', q:'feedback'},
|
|
90
|
+
{key:'event', name:'Event', icon:'ri-calendar-event-line', q:'event'},
|
|
91
|
+
{key:'notice', name:'Notice', icon:'ri-pushpin-line', q:'notice'},
|
|
92
|
+
{key:'suggestion', name:'Suggestion', icon:'ri-mail-send-line', q:'suggestion'}
|
|
93
|
+
],
|
|
94
|
+
storeCategories:[
|
|
95
|
+
{key:'all', name:'All', icon:'ri-apps-line', q:''},
|
|
96
|
+
{key:'general', name:'General', icon:'ri-apps-ai-line', q:'general'},
|
|
97
|
+
{key:'character', name:'Character', icon:'ri-chat-ai-line', q:'character'},
|
|
98
|
+
{key:'image', name:'Image', icon:'ri-image-edit-line', q:'image'},
|
|
99
|
+
{key:'writing', name:'Writing', icon:'ri-quill-pen-line', q:'writing'},
|
|
100
|
+
{key:'music', name:'Music', icon:'ri-music-line', q:'music'},
|
|
101
|
+
{key:'video', name:'Video', icon:'ri-video-ai-line', q:'video'},
|
|
102
|
+
{key:'code', name:'Code', icon:'ri-code-box-line', q:'code'},
|
|
103
|
+
{key:'game', name:'Game', icon:'ri-gamepad-line', q:'game'},
|
|
104
|
+
{key:'finance', name:'Finance', icon:'ri-money-dollar-circle-line', q:'finance'},
|
|
105
|
+
{key:'education', name:'Education', icon:'ri-book-open-line', q:'education'},
|
|
106
|
+
{key:'design', name:'Design', icon:'ri-brush-line', q:'design'},
|
|
107
|
+
{key:'tool', name:'Tool', icon:'ri-tools-line', q:'tool'}
|
|
108
|
+
],
|
|
109
|
+
codeCategories:[
|
|
110
|
+
{key:'all', name:'All', icon:'ri-apps-line', q:''},
|
|
111
|
+
{key:'frontend', name:'Frontend', icon:'ri-layout-masonry-line', q:'frontend'},
|
|
112
|
+
{key:'backend', name:'Backend', icon:'ri-server-line', q:'backend'},
|
|
113
|
+
{key:'ai', name:'AI', icon:'ri-brain-line', q:'ai'},
|
|
114
|
+
{key:'database', name:'Database', icon:'ri-database-2-line', q:'database'},
|
|
115
|
+
{key:'script', name:'Script', icon:'ri-file-code-line', q:'script'},
|
|
116
|
+
{key:'tool', name:'Tool', icon:'ri-tools-line', q:'tool'},
|
|
117
|
+
{key:'snippet', name:'Snippet', icon:'ri-scissors-cut-line', q:'snippet'},
|
|
118
|
+
{key:'library', name:'Library', icon:'ri-book-3-line', q:'library'},
|
|
119
|
+
{key:'framework', name:'Framework', icon:'ri-layout-4-line', q:'framework'},
|
|
120
|
+
{key:'game', name:'Game', icon:'ri-gamepad-line', q:'game'},
|
|
121
|
+
{key:'config', name:'Config', icon:'ri-settings-4-line', q:'config'},
|
|
122
|
+
{key:'other', name:'Other', icon:'ri-more-2-line', q:'other'}
|
|
123
|
+
],
|
|
124
|
+
|
|
125
|
+
newsQuery: '',
|
|
126
|
+
selectedNewsCategoryQ: '',
|
|
127
|
+
hasInitialNewsSearch: !!(window.__RE_BOARD3_CONFIG__&&window.__RE_BOARD3_CONFIG__.hasInitialNewsSearch),
|
|
128
|
+
|
|
129
|
+
marketMode: '', selectedMarketSymbol: '', marketHeatmapActive: false,
|
|
130
|
+
marketHeatmapLocale: (window.__RE_BOARD3_CONFIG__&&window.__RE_BOARD3_CONFIG__.marketHeatmapLocale)||'en',
|
|
131
|
+
cryptoHeatmapLocale: 'en', marketHeatmapSource: 'SPX500',
|
|
132
|
+
marketSymbolCatalog: {
|
|
133
|
+
stocks:[{name:'AAPL',symbol:'NASDAQ:AAPL'},{name:'MSFT',symbol:'NASDAQ:MSFT'},{name:'AMZN',symbol:'NASDAQ:AMZN'},{name:'GOOGL',symbol:'NASDAQ:GOOGL'},{name:'NVDA',symbol:'NASDAQ:NVDA'},{name:'META',symbol:'NASDAQ:META'},{name:'TSLA',symbol:'NASDAQ:TSLA'}],
|
|
134
|
+
gold:[{name:'XAUUSD',symbol:'OANDA:XAUUSD'},{name:'XAGUSD',symbol:'OANDA:XAGUSD'},{name:'GLD',symbol:'AMEX:GLD'}],
|
|
135
|
+
crypto:[{name:'BTCUSDT',symbol:'BINANCE:BTCUSDT'},{name:'ETHUSDT',symbol:'BINANCE:ETHUSDT'},{name:'SOLUSDT',symbol:'BINANCE:SOLUSDT'},{name:'XRPUSDT',symbol:'BINANCE:XRPUSDT'},{name:'DOGEUSDT',symbol:'BINANCE:DOGEUSDT'}],
|
|
136
|
+
commodities:[{name:'USOIL',symbol:'TVC:USOIL'},{name:'UKOIL',symbol:'TVC:UKOIL'},{name:'NATGAS',symbol:'NYMEX:NG1!'}]
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
selectedCodeCategory: 'all', selectedGalleryCategory: 'all', selectedForumCategory: 'all', selectedStoreCategory: 'all',
|
|
140
|
+
currentTag: new URLSearchParams(window.location.search).get('tag')||'',
|
|
141
|
+
searchQuery: '', storeQuery: '', galleryQuery: '', forumQuery: '', codeQuery: '',
|
|
142
|
+
|
|
143
|
+
items: { widgets:[], store:[], gallery:[], forum:[], code:[], news:[], products:[] },
|
|
144
|
+
page: { store:1, gallery:1, forum:1, code:1, news:1 },
|
|
145
|
+
loading: { store:false, gallery:false, forum:false, code:false, news:false },
|
|
146
|
+
end: { store:false, gallery:false, forum:false, code:false, news:false },
|
|
147
|
+
warmupDone: { store:false, gallery:false, forum:false, code:false, news:false },
|
|
148
|
+
initialInterestSortDone: { store:false, gallery:false, forum:false, code:false, news:false },
|
|
149
|
+
|
|
150
|
+
interestProfile: {}, productProfile: {}, marketProfile: {}, itemProfile: {},
|
|
151
|
+
galleryModal: { open: false, item: null },
|
|
152
|
+
|
|
153
|
+
decodeEscapedText(val) {
|
|
154
|
+
if(typeof val!=='string'||!val)return val||''; if(!val.includes('\\u'))return val;
|
|
155
|
+
let v=val.replace(/\\\\u/g,'\\u').replace(/\\u([0-9a-fA-F]{4})/g,(_,x)=>String.fromCharCode(parseInt(x,16)));
|
|
156
|
+
return v.replace(/\\n/g,'\n').replace(/\\"/g,'"').replace(/\\\\/g,'\\');
|
|
157
|
+
},
|
|
158
|
+
normalizeCategoryStrings() {
|
|
159
|
+
if(Array.isArray(this.newsCategories)) this.newsCategories=this.newsCategories.map(c=>({...c,name:this.decodeEscapedText(c.name),q:this.decodeEscapedText(c.q)}));
|
|
160
|
+
if(Array.isArray(this.codeCategories)) this.codeCategories=this.codeCategories.map(c=>({...c,name:this.decodeEscapedText(c.name)}));
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
// --- Market Widget ---
|
|
164
|
+
getNewsMarketMode(q) {
|
|
165
|
+
q=String(q||'').toLowerCase(); if(!q)return'';
|
|
166
|
+
if(/(crypto|bitcoin|btc|ethereum|eth|xrp|sol|doge|코인|가상화폐|암호화폐)/i.test(q))return'crypto';
|
|
167
|
+
if(/(금|골드|은|xau|gold|xag|silver)/i.test(q))return'gold';
|
|
168
|
+
if(/(원자재|원유|wti|brent|천연가스|구리|oil|gas|copper)/i.test(q))return'commodities';
|
|
169
|
+
if(/(주식|증시|코스피|나스닥|s&p|sp500|stock|market|nasdaq|aapl|nvda|msft|tsla)/i.test(q))return'stocks';
|
|
170
|
+
return '';
|
|
171
|
+
},
|
|
172
|
+
syncMarketWidgetMode(fReset=false) {
|
|
173
|
+
const pM=this.marketMode; this.marketMode=this.getNewsMarketMode(this.newsQuery||this.selectedNewsCategoryQ);
|
|
174
|
+
if(!this.marketMode){ this.selectedMarketSymbol=''; this.marketHeatmapActive=false; return; }
|
|
175
|
+
let syms=this.marketSymbolCatalog[this.marketMode]||[];
|
|
176
|
+
if(!this.supportsHeatmapMode()) this.marketHeatmapActive=false;
|
|
177
|
+
if(fReset||!syms.some(s=>s.symbol===this.selectedMarketSymbol)){
|
|
178
|
+
const sortedSyms = syms.slice().sort((a,b) => (this.marketProfile[b.symbol]||0) - (this.marketProfile[a.symbol]||0));
|
|
179
|
+
this.selectedMarketSymbol=sortedSyms.length?sortedSyms[0].symbol:'';
|
|
180
|
+
}
|
|
181
|
+
if(this.supportsHeatmapMode()){ this.marketHeatmapActive=true; this.$nextTick(()=>this.renderMarketHeatmapWidget(fReset||pM!==this.marketMode)); }
|
|
182
|
+
},
|
|
183
|
+
shouldShowMarketWidget(){ return false; },
|
|
184
|
+
supportsHeatmapMode(m=null){ return (m||this.marketMode)==='stocks'||(m||this.marketMode)==='crypto'; },
|
|
185
|
+
shouldShowHeatmapWidget(){ return this.supportsHeatmapMode()&&this.marketHeatmapActive; },
|
|
186
|
+
getActiveMarketSymbols(){ return this.marketSymbolCatalog[this.marketMode]||[]; },
|
|
187
|
+
getMarketSymbolToken(s){ return String(s?.name||'').slice(0,3).toUpperCase() || 'TV'; },
|
|
188
|
+
getMarketSymbolAvatar(s){ const t=this.getMarketSymbolToken(s); return `https://ui-avatars.com/api/?name=${encodeURIComponent(t)}&background=111827&color=fff&bold=true&format=svg&size=64`; },
|
|
189
|
+
showMarketHeatmap(){ if(this.supportsHeatmapMode()){ this.marketHeatmapActive=true; this.$nextTick(()=>this.renderMarketHeatmapWidget(true)); } },
|
|
190
|
+
setMarketSymbol(s){
|
|
191
|
+
if(s){
|
|
192
|
+
this.selectedMarketSymbol=s; if(this.supportsHeatmapMode())this.marketHeatmapActive=false;
|
|
193
|
+
this.marketProfile[s] = Math.min(1000, (this.marketProfile[s] || 0) + 1);
|
|
194
|
+
localStorage.setItem('isai_market_profile_v1', JSON.stringify(this.marketProfile));
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
isActiveMarketSymbol(s){ return !this.marketHeatmapActive&&this.selectedMarketSymbol===s; },
|
|
198
|
+
renderMarketHeatmapWidget(f=false){
|
|
199
|
+
if(!this.supportsHeatmapMode())return; const h=this.$refs.marketHeatmapWidget, m=String(this.marketMode||''); if(!h||(!f&&h.dataset.loaded==='1'&&h.dataset.mode===m))return;
|
|
200
|
+
h.innerHTML=''; h.dataset.loaded='0'; h.dataset.mode=m;
|
|
201
|
+
const iframe = document.createElement('iframe');
|
|
202
|
+
const params = new URLSearchParams({
|
|
203
|
+
mode: 'heatmap',
|
|
204
|
+
marketMode: m,
|
|
205
|
+
locale: this.marketHeatmapLocale || (m === 'crypto' ? this.cryptoHeatmapLocale || 'en' : 'en'),
|
|
206
|
+
source: this.marketHeatmapSource || 'SPX500'
|
|
207
|
+
});
|
|
208
|
+
iframe.src = `/tradingview_frame.php?${params.toString()}`;
|
|
209
|
+
iframe.loading = 'lazy';
|
|
210
|
+
iframe.title = 'TradingView heatmap';
|
|
211
|
+
iframe.referrerPolicy = 'no-referrer-when-downgrade';
|
|
212
|
+
iframe.style.width = '100%';
|
|
213
|
+
iframe.style.height = '100%';
|
|
214
|
+
iframe.style.minHeight = '100%';
|
|
215
|
+
iframe.style.border = '0';
|
|
216
|
+
iframe.style.display = 'block';
|
|
217
|
+
iframe.onload=()=>{h.dataset.loaded='1'; h.dataset.mode=m; this.$nextTick(()=>this.fixMarketHeatmapHeight());};
|
|
218
|
+
h.appendChild(iframe);
|
|
219
|
+
setTimeout(()=>this.fixMarketHeatmapHeight(), 600);
|
|
220
|
+
setTimeout(()=>this.fixMarketHeatmapHeight(), 1600);
|
|
221
|
+
},
|
|
222
|
+
fixMarketHeatmapHeight(){
|
|
223
|
+
const h=this.$refs.marketHeatmapWidget;
|
|
224
|
+
if(!h)return;
|
|
225
|
+
h.querySelectorAll('.tradingview-widget-container,.tradingview-widget-container__widget,iframe').forEach(el=>{
|
|
226
|
+
el.style.width='100%';
|
|
227
|
+
el.style.height='100%';
|
|
228
|
+
el.style.minHeight='100%';
|
|
229
|
+
el.style.display='block';
|
|
230
|
+
});
|
|
231
|
+
},
|
|
232
|
+
getTradingViewChartUrl(){ const fb={stocks:'SP:SPX',gold:'OANDA:XAUUSD',crypto:'BINANCE:BTCUSDT',commodities:'TVC:USOIL'}[this.marketMode]||'SP:SPX'; const params=new URLSearchParams({mode:'chart',marketMode:this.marketMode||'stocks',symbol:this.selectedMarketSymbol||fb}); return `/tradingview_frame.php?${params.toString()}`; },
|
|
233
|
+
|
|
234
|
+
// =========================================================================
|
|
235
|
+
// [강력 최적화] AI 절대 티어(Tier) 시스템
|
|
236
|
+
// =========================================================================
|
|
237
|
+
calculateItemTier(tb, i, targetKey) {
|
|
238
|
+
if (!targetKey || targetKey === 'all') return 2;
|
|
239
|
+
|
|
240
|
+
const itemText = String(`${i?.title||''} ${i?.cleanTitle||''} ${i?.content||''} ${i?.snippet||''} ${i?.prompt||''} ${i?.keywords||''} ${i?.listText||''} ${i?.category||''}`).toLowerCase();
|
|
241
|
+
|
|
242
|
+
let isMatch = i?.category ? i.category.toLowerCase().includes(targetKey) : false;
|
|
243
|
+
let isConflict = false;
|
|
244
|
+
|
|
245
|
+
const regexes = {
|
|
246
|
+
boy: /\b(1boy|boys?|man|men|male|guy|handsome|chico|ni[ñn]o|hombre|var[oó]n|masculino|guapo)\b|(남자|소년|남성|남캐|男|男子|少年|男性|イケメン|लड़का|आदमी|पुरुष)/i,
|
|
247
|
+
girl: /\b(1girl|girls?|woman|women|female|lady|beautiful|maid|waifu|chica|ni[ñn]a|mujer|hembra|femenina|hermosa|linda)\b|(여자|소녀|여성|여캐|女|女子|少女|女性|美少女|メイド|かわいい|लड़की|महिला|औरत|सुंदर|प्यारी)/i,
|
|
248
|
+
anime: /\b(anime|manga|toon|waifu|2d|illustration|dibujo animado)\b|(애니|만화|일러스트|アニメ|マンガ|イラスト|二次元|एनिमे|मंगा|कार्टून)/i,
|
|
249
|
+
realistic: /\b(realistic|photo|cinematic|real|raw|photorealistic|realista|fotograf[ií]a)\b|(실사|사진|포토|리얼|リアル|写真|実写|シネマティック|यथार्थवादी|फोटो|असली)/i,
|
|
250
|
+
fantasy: /\b(fantasy|magic|dragon|elf|fantas[ií]a)\b|(판타지|마법|엘프|ファンタジー|魔法|कल्पना|जादू)/i,
|
|
251
|
+
scifi: /\b(sci-fi|cyberpunk|space|robot|mecha)\b|(sf|사이버펑크|우주|메카|로봇|サイバーパンク|宇宙|ロボット|अंतरिक्ष|रोबोट)/i,
|
|
252
|
+
landscape: /\b(landscape|scenery|background|nature|paisaje|naturaleza)\b|(풍경|배경|자연|風景|背景|自然|परिदृश्य|प्रकृति)/i,
|
|
253
|
+
cyberpunk: /\b(cyberpunk|neon|synthwave|futuristic|ciberpunk)\b|(사이버펑크|네온|미래|サイバーパンク|ネオン|미래도시)/i,
|
|
254
|
+
animal: /\b(animal|pet|cat|dog|bird|animales)\b|(동물|고양이|강아지|개|새|펫|動物|ペット|猫|犬|जानवर|पालतू)/i,
|
|
255
|
+
mecha: /\b(mecha|robot|cyborg|meca)\b|(메카|로봇|사이보그|メカ|ロボット|サイボーグ|मेचा|रोबोट)/i,
|
|
256
|
+
'3d': /\b(3d|render|octane|unreal|blender|c4d)\b|(3디|렌더링|3DCG)/i,
|
|
257
|
+
sketch: /\b(sketch|drawing|pencil|boceto|dibujo)\b|(스케치|드로잉|연필|데생|スケッチ|デッサン|鉛筆|स्केच|ड्राइंग)/i,
|
|
258
|
+
qa: /\b(qna|q&a|how to|help|question|pregunta|ayuda|duda)\b|\?|(질문|도움|어떻게|궁금|알려|質問|助けて|教えて|प्रश्न|सवाल|मदद)/i,
|
|
259
|
+
humor: /\b(humor|funny|meme|lol|haha|gracioso|jaja|divertido)\b|(유머|웃긴|밈|ㅋㅋㅋ|ㅎㅎㅎ|재밌|폭소|ユーモア|面白い|笑|草|www|हास्य|मज़ाकिया|मीम)/i,
|
|
260
|
+
review: /\b(review|evaluate|recommend|bought|rese[ñn]a|opini[oó]n)\b|(리뷰|후기|평가|추천|사용기|써본|샀|구매|장단점|レビュー|感想|評価|おすすめ|購入|समीक्षा|राय)/i,
|
|
261
|
+
info: /\b(info|tip|guide|tutorial|consejo|gu[ií]a)\b|(팁|정보|지식|꿀팁|알아보기|방법|가이드|정리|情報|ヒント|ガイド|コツ|जानकारी|सुझाव)/i,
|
|
262
|
+
discussion: /\b(discussion|debate|opinion|discusión)\b|(토론|의견|생각|논의|議論|意見|चर्चा|बहस)/i,
|
|
263
|
+
news: /\b(news|article|press|journal|noticias)\b|(뉴스|기사|보도|소식|속보|ニュース|記事|報道|समाचार)/i,
|
|
264
|
+
feedback: /\b(feedback|report|comentarios)\b|(피드백|건의|리포트|오류|피드백|フィードバック|보고|प्रतिक्रिया)/i,
|
|
265
|
+
event: /\b(event|contest|giveaway|evento)\b|(이벤트|행사|대회|참여|공모전|イベント|コンテスト|행사|आयोजन)/i,
|
|
266
|
+
notice: /\b(notice|announcement|update|aviso)\b|(공지|안내|업데이트|공지사항|알림|お知らせ|通知|सूचना)/i,
|
|
267
|
+
suggestion: /\b(suggest|idea|proposal|sugerencia)\b|(건의|제안|아이디어|건의사항|提案|アイデア|सुझाव)/i,
|
|
268
|
+
character: /\b(character|bot|ai_chat|chat|persona|personaje)\b|(챗봇|캐릭터|페르소나|대화|キャラクター|ボット|チャット|चरित्र|बॉट|चैट)/i,
|
|
269
|
+
image: /\b(image|art|photo|draw|generate|imagen|arte)\b|(이미지|그림|사진|생성|합성|画像|アート|描く|छवि|चित्र)/i,
|
|
270
|
+
writing: /\b(write|blog|story|novel|text|escritura)\b|(글쓰기|블로그|소설|작성|글|스토리|執筆|ブログ|小説|लिखना|कहानी)/i,
|
|
271
|
+
music: /\b(music|audio|song|sound|suno|m[uú]sica)\b|(음악|노래|오디오|사운드|수노|音楽|曲|オーディオ|संगीत|गाना)/i,
|
|
272
|
+
video: /\b(video|motion|animate|v[ií]deo)\b|(비디오|영상|동영상|모션|애니메이트|ビデオ|動画|वीडियो)/i,
|
|
273
|
+
education: /\b(edu|learn|study|school|educaci[oó]n)\b|(교육|공부|학습|강의|배우기|학교|教育|学習|学ぶ|शिक्षा|अध्ययन)/i,
|
|
274
|
+
design: /\b(design|ui|ux|layout|dise[ñn]o)\b|(디자인|디자이너|레이아웃|꾸미기|デザイン|レイアウト|डिज़ाइन)/i,
|
|
275
|
+
code: /\b(code|dev|script|program|c[oó]digo|desarrollo)\b|(개발|코드|프로그래밍|코드생성|코딩|コード|開発|スクリプト|プログラム|कोड|विकास)/i,
|
|
276
|
+
database: /\b(db|sql|mysql|nosql|mongo|postgres|database)\b|(데이터베이스|디비|데이터|データベース|डेटाबेस)/i,
|
|
277
|
+
library: /\b(lib|library|package|npm|pip|librer[ií]a)\b|(라이브러리|패키지|모듈|ライブラリ|パッケージ|पुस्तकालय)/i,
|
|
278
|
+
framework: /\b(framework|react|vue|angular|django|spring|laravel)\b|(프레임워크|프레임웍|フレームワーク|फ्रेमवर्क)/i,
|
|
279
|
+
config: /\b(config|setup|setting|json|yaml|env|configuraci[oó]n)\b|(설정|환경|세팅|구성|設定|コンフィグ|सेटिंग)/i,
|
|
280
|
+
finance: /\b(finance|money|stock|crypto|finanzas)\b|(금융|주식|돈|코인|경제|비트코인|증시|金融|株|仮想通貨|वित्त|पैसा)/i,
|
|
281
|
+
game: /\b(game|play|gaming|juego)\b|(게임|오락|플레이|ゲーム|プレイ|खेल)/i,
|
|
282
|
+
tool: /\b(tool|utility|herramienta|utilidad)\b|(도구|유틸|툴|ツール|ユーティリティ|उपकरण)/i,
|
|
283
|
+
other: /\b(other|misc|etc|otros)\b|(기타|잡동사니|그외|その他|기타등등|अन्य)/i
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
if (regexes[targetKey] && regexes[targetKey].test(itemText)) isMatch = true;
|
|
287
|
+
|
|
288
|
+
if (tb === 'gallery') {
|
|
289
|
+
const hasBoy = regexes.boy.test(itemText);
|
|
290
|
+
const hasGirl = regexes.girl.test(itemText);
|
|
291
|
+
if (targetKey === 'boy' && hasGirl) isConflict = true;
|
|
292
|
+
if (targetKey === 'girl' && hasBoy) isConflict = true;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (isConflict) return 0;
|
|
296
|
+
if (isMatch) return 2;
|
|
297
|
+
return 1;
|
|
298
|
+
},
|
|
299
|
+
|
|
300
|
+
scoreByInterestBase(tb, i) {
|
|
301
|
+
let s = 0;
|
|
302
|
+
const itemText = String(`${i?.title||''} ${i?.cleanTitle||''} ${i?.content||''} ${i?.snippet||''} ${i?.listText||''} ${i?.prompt||''} ${i?.category||''} ${i?.keywords||''} ${i?.description||''} ${i?.source||''}`).toLowerCase();
|
|
303
|
+
Object.keys(this.interestProfile).forEach(k => {
|
|
304
|
+
if(k.startsWith('kw_') && itemText.includes(k.replace('kw_',''))) {
|
|
305
|
+
s += (this.interestProfile[k] * 10);
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
const uid = i?.id || i?.link;
|
|
309
|
+
if (uid && this.itemProfile[`${tb}_${uid}`]) {
|
|
310
|
+
s += (Number(this.itemProfile[`${tb}_${uid}`]) * 100);
|
|
311
|
+
}
|
|
312
|
+
return s;
|
|
313
|
+
},
|
|
314
|
+
|
|
315
|
+
getInterestSortTargetKey(tb) {
|
|
316
|
+
if (tb === 'gallery') return this.selectedGalleryCategory || 'all';
|
|
317
|
+
if (tb === 'forum') return this.selectedForumCategory || 'all';
|
|
318
|
+
if (tb === 'store') return this.selectedStoreCategory || 'all';
|
|
319
|
+
if (tb === 'code') return this.selectedCodeCategory || 'all';
|
|
320
|
+
if (tb === 'news') return this.selectedNewsCategoryQ || '';
|
|
321
|
+
return 'all';
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
sortItemsByInterest(tb, l) {
|
|
325
|
+
if (!Array.isArray(l) || l.length < 2) return l;
|
|
326
|
+
|
|
327
|
+
const targetKey = this.getInterestSortTargetKey(tb);
|
|
328
|
+
return [...l].sort((a, b) => {
|
|
329
|
+
const tierA = this.calculateItemTier(tb, a, targetKey);
|
|
330
|
+
const tierB = this.calculateItemTier(tb, b, targetKey);
|
|
331
|
+
if (tierA !== tierB) return tierB - tierA;
|
|
332
|
+
|
|
333
|
+
const scoreA = this.scoreByInterestBase(tb, a) + Number(a?.sort_order || 0);
|
|
334
|
+
const scoreB = this.scoreByInterestBase(tb, b) + Number(b?.sort_order || 0);
|
|
335
|
+
if (scoreA !== scoreB) return scoreB - scoreA;
|
|
336
|
+
|
|
337
|
+
const timeA = Date.parse(a?.date || a?.created_at || '') || 0;
|
|
338
|
+
const timeB = Date.parse(b?.date || b?.created_at || '') || 0;
|
|
339
|
+
if (timeA !== timeB) return timeB - timeA;
|
|
340
|
+
|
|
341
|
+
const getId = x => x?.id ? Number(x.id) : (x?.link ? parseInt(String(x.link).replace(/\D/g, '').slice(0,8)) : 0);
|
|
342
|
+
return getId(b) - getId(a);
|
|
343
|
+
});
|
|
344
|
+
},
|
|
345
|
+
|
|
346
|
+
applyInterestSort(tb){
|
|
347
|
+
if(['store','code','forum','gallery','products'].includes(tb)) {
|
|
348
|
+
this.items[tb] = [...this.sortItemsByInterest(tb, this.items[tb])];
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
|
|
352
|
+
trackItem(tb, i){
|
|
353
|
+
if(!i || (!i.id && !i.link)) return;
|
|
354
|
+
const uid = i.id || i.link;
|
|
355
|
+
const k = `${tb}_${uid}`;
|
|
356
|
+
this.itemProfile[k] = Math.min(1000, (this.itemProfile[k] || 0) + 1);
|
|
357
|
+
localStorage.setItem('isai_item_profile_v1', JSON.stringify(this.itemProfile));
|
|
358
|
+
setTimeout(() => { if (this.items[tb]?.length) { this.updateMacy(tb); } }, 300);
|
|
359
|
+
},
|
|
360
|
+
trackInterest(tb, i, w=1){
|
|
361
|
+
const text = String(`${i?.title||''} ${i?.cleanTitle||''} ${i?.content||''} ${i?.snippet||''} ${i?.listText||''} ${i?.prompt||''} ${i?.category||''} ${i?.keywords||''} ${i?.description||''} ${i?.source||''}`).toLowerCase();
|
|
362
|
+
const words = text.replace(/[^a-z0-9가-힣_]+/g, ' ').split(/\s+/).filter(x=>x.length>1).slice(0,10);
|
|
363
|
+
let u = false;
|
|
364
|
+
words.forEach(k => {
|
|
365
|
+
const pKey = `kw_${k}`;
|
|
366
|
+
this.interestProfile[pKey] = Math.min(500, Math.max(0, (this.interestProfile[pKey]||0) + w));
|
|
367
|
+
u = true;
|
|
368
|
+
});
|
|
369
|
+
if(u){
|
|
370
|
+
localStorage.setItem('isai_interest_profile_v1',JSON.stringify(this.interestProfile));
|
|
371
|
+
clearTimeout(this.sponsorSearchTimer);
|
|
372
|
+
this.sponsorSearchTimer = setTimeout(() => this.loadSearchProducts?.(), 250);
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
|
|
376
|
+
productPriceLabel(i){ const cur=String(i?.currency||'KRW').toUpperCase(); const n=Number(i?.price||0); return cur==='USD'?'$'+n.toLocaleString(undefined,{maximumFractionDigits:2}):Math.round(n).toLocaleString()+'원'; },
|
|
377
|
+
scoreProduct(i){ return this.scoreByInterestBase('products',i)+(Number(this.productProfile[i?.id]||0)*100)+(Number(i?.sort_order||0)); },
|
|
378
|
+
|
|
379
|
+
isProductSearchMode() {
|
|
380
|
+
// 카테고리 클릭이 아닌, 실제 검색어가 입력된 상태인지 탭별로 정확히 구분
|
|
381
|
+
if (this.curTab === 'news') return !!this.newsQuery && this.newsQuery !== this.selectedNewsCategoryQ;
|
|
382
|
+
if (this.curTab === 'store') return !!this.storeQuery;
|
|
383
|
+
if (this.curTab === 'forum') return !!this.forumQuery;
|
|
384
|
+
if (this.curTab === 'code') return !!this.codeQuery;
|
|
385
|
+
if (this.curTab === 'gallery') return !!this.galleryQuery;
|
|
386
|
+
return !!this.searchQuery;
|
|
387
|
+
},
|
|
388
|
+
getVisibleProducts(){
|
|
389
|
+
const limit = this.isProductSearchMode() ? 24 : 3;
|
|
390
|
+
return Array.isArray(this.items.products)
|
|
391
|
+
? [...this.items.products]
|
|
392
|
+
.sort((a, b) => {
|
|
393
|
+
const timeA = Date.parse(a?.created_at || a?.date || '') || 0;
|
|
394
|
+
const timeB = Date.parse(b?.created_at || b?.date || '') || 0;
|
|
395
|
+
if (timeA !== timeB) return timeB - timeA;
|
|
396
|
+
return Number(b?.id || 0) - Number(a?.id || 0);
|
|
397
|
+
})
|
|
398
|
+
.slice(0, limit)
|
|
399
|
+
: [];
|
|
400
|
+
},
|
|
401
|
+
getVisibleNewsItems(){ return Array.isArray(this.items.news) ? [...this.items.news].sort((a,b)=> (Date.parse(b?.date || b?.created_at || '') || 0) - (Date.parse(a?.date || a?.created_at || '') || 0) || ((Number(b?.id||0) || 0) - (Number(a?.id||0) || 0))) :[]; },
|
|
402
|
+
|
|
403
|
+
getMixedItems(tb) {
|
|
404
|
+
const visibleItems = tb === 'news' ? this.getVisibleNewsItems() : (this.items[tb] || []);
|
|
405
|
+
if (!visibleItems.length) return [];
|
|
406
|
+
|
|
407
|
+
const original = visibleItems.map((item, idx) => ({type: tb, item, key: `${tb}-${item.id || item.link || idx}`}));
|
|
408
|
+
const products = this.getVisibleProducts().map((item) => ({type: 'product', item, key: `product-${item.id}`}));
|
|
409
|
+
|
|
410
|
+
if (!products.length) return original;
|
|
411
|
+
|
|
412
|
+
const out = [];
|
|
413
|
+
let pQueue = [...products];
|
|
414
|
+
const isSearch = this.isProductSearchMode();
|
|
415
|
+
|
|
416
|
+
// 시드 기반 난수 생성 (화면 깜빡임 완벽 방지)
|
|
417
|
+
const getSeed = (str) => {
|
|
418
|
+
let h = 0; for(let i=0; i<str.length; i++) h = Math.imul(31, h) + str.charCodeAt(i) | 0; return Math.abs(h);
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
original.forEach((entry, idx) => {
|
|
422
|
+
out.push(entry);
|
|
423
|
+
let seed = getSeed(entry.key);
|
|
424
|
+
let insertCount = 0;
|
|
425
|
+
|
|
426
|
+
if (isSearch) {
|
|
427
|
+
// [검색 모드] 상품이 최대 33개이므로 일반 게시물 사이마다 1~2개씩 촘촘하게 섞음
|
|
428
|
+
insertCount = (seed % 10) < 4 ? 2 : 1; // 40% 확률로 2개, 60% 확률로 1개 삽입
|
|
429
|
+
} else {
|
|
430
|
+
// [일반 모드] 기본 광고 3개이므로 2, 6, 11번째 위치와 드물게(30%) 1개씩 삽입
|
|
431
|
+
let forceInsert = (idx === 2 || idx === 6 || idx === 11);
|
|
432
|
+
if (forceInsert || (idx >= 1 && (seed % 10) < 3)) {
|
|
433
|
+
insertCount = 1;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// 큐에 상품이 남아있고 넣어야 할 카운트가 있으면 빼서 삽입
|
|
438
|
+
while (pQueue.length > 0 && insertCount > 0) {
|
|
439
|
+
out.push(pQueue.shift());
|
|
440
|
+
insertCount--;
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
// 위에서 다 섞지 못하고 남은 상품/광고가 있다면 맨 뒤에 추가
|
|
445
|
+
while(pQueue.length > 0) {
|
|
446
|
+
out.push(pQueue.shift());
|
|
447
|
+
}
|
|
448
|
+
return out;
|
|
449
|
+
},
|
|
450
|
+
|
|
451
|
+
setGalleryCat(c){
|
|
452
|
+
this.selectedGalleryCategory = c.key;
|
|
453
|
+
const hadSearchQuery = !!String(this.galleryQuery || '').trim();
|
|
454
|
+
this.galleryQuery = '';
|
|
455
|
+
document.getElementById('gallery-search-input').value='';
|
|
456
|
+
toggleGallerySearchMode(false);
|
|
457
|
+
|
|
458
|
+
if (hadSearchQuery || !this.items.gallery.length) {
|
|
459
|
+
this.page.gallery=1;
|
|
460
|
+
this.end.gallery=false;
|
|
461
|
+
this.items.gallery=[];
|
|
462
|
+
this.loadData('gallery');
|
|
463
|
+
} else {
|
|
464
|
+
this.applyInterestSort('gallery');
|
|
465
|
+
this.updateMacy('gallery');
|
|
466
|
+
}
|
|
467
|
+
this.loadSearchProducts();
|
|
468
|
+
},
|
|
469
|
+
submitGallerySearch(k){ this.galleryQuery=String(k||'').trim(); this.selectedGalleryCategory=''; this.items.gallery=[]; this.page.gallery=1; this.end.gallery=false; this.loadData('gallery'); this.loadSearchProducts(); },
|
|
470
|
+
resetGalleryCategory(){ this.selectedGalleryCategory='all'; this.galleryQuery=''; document.getElementById('gallery-search-input').value=''; toggleGallerySearchMode(false); this.items.gallery=[]; this.page.gallery=1; this.end.gallery=false; this.loadData('gallery'); this.loadSearchProducts(); },
|
|
471
|
+
|
|
472
|
+
setForumCat(c){
|
|
473
|
+
this.selectedForumCategory = c.key; this.forumQuery = '';
|
|
474
|
+
document.getElementById('forum-search-input').value=''; toggleForumSearchMode(false);
|
|
475
|
+
this.page.forum=1; this.end.forum=false; this.items.forum=[];
|
|
476
|
+
this.applyInterestSort('forum');
|
|
477
|
+
this.updateMacy('forum');
|
|
478
|
+
this.loadData('forum'); this.loadSearchProducts();
|
|
479
|
+
},
|
|
480
|
+
submitForumSearch(k){ this.forumQuery=String(k||'').trim(); this.selectedForumCategory=''; this.items.forum=[]; this.page.forum=1; this.end.forum=false; this.loadData('forum'); this.loadSearchProducts(); },
|
|
481
|
+
resetForumCategory(){ this.selectedForumCategory='all'; this.forumQuery=''; document.getElementById('forum-search-input').value=''; toggleForumSearchMode(false); this.items.forum=[]; this.page.forum=1; this.end.forum=false; this.loadData('forum'); this.loadSearchProducts(); },
|
|
482
|
+
|
|
483
|
+
setStoreCat(c){
|
|
484
|
+
this.selectedStoreCategory = c.key; this.storeQuery = '';
|
|
485
|
+
document.getElementById('store-search-input').value=''; toggleStoreSearchMode(false);
|
|
486
|
+
this.page.store=1; this.end.store=false; this.items.store=[];
|
|
487
|
+
this.applyInterestSort('store');
|
|
488
|
+
this.updateMacy('store');
|
|
489
|
+
this.loadData('store'); this.loadSearchProducts();
|
|
490
|
+
},
|
|
491
|
+
submitStoreSearch(k){ this.storeQuery=String(k||'').trim(); this.selectedStoreCategory=''; this.items.store=[]; this.page.store=1; this.end.store=false; this.loadData('store'); this.loadSearchProducts(); },
|
|
492
|
+
resetStoreCategory(){ this.selectedStoreCategory='all'; this.storeQuery=''; document.getElementById('store-search-input').value=''; toggleStoreSearchMode(false); this.items.store=[]; this.page.store=1; this.end.store=false; this.loadData('store'); this.loadSearchProducts(); },
|
|
493
|
+
|
|
494
|
+
setNewsCat(c){
|
|
495
|
+
this.selectedNewsCategoryQ = this.newsQuery = c.q;
|
|
496
|
+
this.syncMarketWidgetMode(true); window.history.replaceState({},'','/');
|
|
497
|
+
this.items.news =[]; this.items.products =[];
|
|
498
|
+
this.updateMacy('news');
|
|
499
|
+
document.getElementById('news-search-input').value=''; toggleNewsSearchMode(false);
|
|
500
|
+
this.page.news=1; this.end.news=false; this.loadData('news'); this.loadSearchProducts();
|
|
501
|
+
},
|
|
502
|
+
submitNewsSearch(k){ k=String(k||'').trim(); if(!k)return this.resetNewsCategory(); this.selectedNewsCategoryQ=''; this.newsQuery=k; this.syncMarketWidgetMode(true); window.history.replaceState({},'',`/search/${encodeURIComponent(k)}`); this.items.news=[]; this.items.products=[]; this.page.news=1; this.end.news=false; toggleNewsSearchMode(false); this.loadData('news'); this.loadSearchProducts(); },
|
|
503
|
+
resetNewsCategory(){ if(this.newsCategories.length)this.setNewsCat(this.newsCategories[0]); },
|
|
504
|
+
|
|
505
|
+
setCodeCat(c){
|
|
506
|
+
this.selectedCodeCategory = c.key; this.codeQuery = '';
|
|
507
|
+
document.getElementById('code-search-input').value=''; toggleCodeSearchMode(false);
|
|
508
|
+
this.page.code=1; this.end.code=false; this.items.code=[];
|
|
509
|
+
this.applyInterestSort('code');
|
|
510
|
+
this.updateMacy('code');
|
|
511
|
+
this.loadData('code'); this.loadSearchProducts();
|
|
512
|
+
},
|
|
513
|
+
submitCodeSearch(k){ this.codeQuery=String(k||'').trim(); this.selectedCodeCategory=''; this.items.code=[]; this.page.code=1; this.end.code=false; this.loadData('code'); this.loadSearchProducts(); },
|
|
514
|
+
resetCodeCategory(){ this.selectedCodeCategory='all'; this.codeQuery=''; document.getElementById('code-search-input').value=''; toggleCodeSearchMode(false); this.items.code=[]; this.page.code=1; this.end.code=false; this.loadData('code'); this.loadSearchProducts(); },
|
|
515
|
+
|
|
516
|
+
openStoreItem(i){ if(i){ this.trackInterest('store', i); this.trackItem('store', i); const appId = i.id || i.app_id || i.appId; if(appId && typeof window.runApp==='function') window.runApp(appId); } },
|
|
517
|
+
openForumItem(i){ if(i){ this.trackInterest('forum', i); this.trackItem('forum', i); window.location.href='https://isai.kr/view/'+i.id; } },
|
|
518
|
+
openNewsGate(i){ if(i?.link){ this.trackInterest('news', i); this.trackItem('news', i); window.location.href=`/news_gate/?u=${encodeURIComponent(i.link)}&t=${encodeURIComponent(i.cleanTitle||i.title||'News')}&s=${encodeURIComponent(i.snippet||'')}`; } },
|
|
519
|
+
openProduct(i){ if(i){ this.trackInterest('products', i); this.trackItem('products', i); if(i.link_url) window.open(i.link_url,'_blank','noopener'); } },
|
|
520
|
+
async openCodeItem(i){ if(!i)return; this.trackInterest('code', i); this.trackItem('code', i); if(i.id){ window.location.href='/play_run/?id='+i.id; return; } if(i.run_url)window.open(i.run_url,'_blank','noopener'); else if(i.gist_url)window.open(i.gist_url,'_blank','noopener'); },
|
|
521
|
+
openCodeComposer(){ window.scrollTo({top:0,behavior:'smooth'}); if(typeof window.setMode==='function')window.setMode('code'); },
|
|
522
|
+
|
|
523
|
+
openGalleryItem(i){
|
|
524
|
+
if(i){
|
|
525
|
+
this.trackInterest('gallery', i);
|
|
526
|
+
this.trackItem('gallery', i);
|
|
527
|
+
fetch('re_store.php?action=increase_view&id='+i.id).catch(e=>{});
|
|
528
|
+
this.galleryModal.item = i; this.galleryModal.open = true;
|
|
529
|
+
window.__RE_BOARD3_SHARE_CONTEXT__ = buildGalleryShareContext(i);
|
|
530
|
+
const nextUrl = new URL(window.location.href); nextUrl.searchParams.set('v', i.share_id || i.id); window.history.pushState({}, '', nextUrl.toString());
|
|
531
|
+
}
|
|
532
|
+
},
|
|
533
|
+
closeGalleryModal() {
|
|
534
|
+
this.galleryModal.open = false; setTimeout(() => { this.galleryModal.item = null; }, 300);
|
|
535
|
+
window.__RE_BOARD3_SHARE_CONTEXT__ = null;
|
|
536
|
+
const nextUrl = new URL(window.location.href); nextUrl.searchParams.delete('v'); window.history.pushState({}, '', nextUrl.pathname + nextUrl.search);
|
|
537
|
+
},
|
|
538
|
+
|
|
539
|
+
async openCharacterChat(i) {
|
|
540
|
+
if(!i?.image_url) return;
|
|
541
|
+
this.trackInterest('gallery', i);
|
|
542
|
+
this.trackItem('gallery', i);
|
|
543
|
+
this.closeGalleryModal();
|
|
544
|
+
document.body.classList.remove('is-store-menu-open');
|
|
545
|
+
const p = String(i.content || i.prompt || i.title || '').replace(/2x2\s*grid/gi, '').replace(/\s+/g, ' ').trim();
|
|
546
|
+
if (typeof window.startCharacterImageChat === 'function') {
|
|
547
|
+
await window.startCharacterImageChat(i.id, i.image_url, p, (i.persona_name || i.nickname || i.safeIp || 'Character').slice(0,80), (i.persona_personality || '').slice(0,300));
|
|
548
|
+
}
|
|
549
|
+
},
|
|
550
|
+
|
|
551
|
+
getSortedNewsCategories(){ return this.newsCategories.slice(); },
|
|
552
|
+
getSortedGalleryCategories(){ return this.galleryCategories.slice(); },
|
|
553
|
+
getSortedForumCategories(){ return this.forumCategories.slice(); },
|
|
554
|
+
getSortedStoreCategories(){ return this.storeCategories.slice(); },
|
|
555
|
+
getSortedCodeCategories(){ return this.codeCategories.slice(); },
|
|
556
|
+
|
|
557
|
+
refreshInterestLayout(t){ this.$nextTick(() => { this.applyInterestSort(t); this.updateMacy(t); setTimeout(() => { this.applyInterestSort(t); this.updateMacy(t); }, 150); }); },
|
|
558
|
+
async warmupAiPool(t){ if(t!=='gallery'||this.warmupDone[t])return; this.warmupDone[t]=true; for(let i=0;i<2&&!this.end[t];i++)await this.loadData(t); this.refreshInterestLayout(t); this.initialInterestSortDone[t]=true; },
|
|
559
|
+
|
|
560
|
+
// =========================================================================
|
|
561
|
+
// [초기화 및 무한 스크롤 옵저버 셋업]
|
|
562
|
+
// =========================================================================
|
|
563
|
+
async init() {
|
|
564
|
+
window.$boardApp=this;
|
|
565
|
+
this.normalizeCategoryStrings();
|
|
566
|
+
|
|
567
|
+
this.interestProfile = JSON.parse(localStorage.getItem('isai_interest_profile_v1')||'{}');
|
|
568
|
+
this.productProfile = JSON.parse(localStorage.getItem('isai_product_profile_v1')||'{}');
|
|
569
|
+
this.marketProfile = JSON.parse(localStorage.getItem('isai_market_profile_v1')||'{}');
|
|
570
|
+
this.itemProfile = JSON.parse(localStorage.getItem('isai_item_profile_v1')||'{}');
|
|
571
|
+
|
|
572
|
+
if(!this.hasInitialNewsSearch) {
|
|
573
|
+
if (this.newsCategories && this.newsCategories.length > 0) {
|
|
574
|
+
this.selectedNewsCategoryQ = this.newsQuery = this.newsCategories[0].q;
|
|
575
|
+
}
|
|
576
|
+
} else {
|
|
577
|
+
this.selectedNewsCategoryQ = this.newsQuery = window.__RE_BOARD3_CONFIG__.initialNewsQuery || '';
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
this.syncMarketWidgetMode(true);
|
|
581
|
+
|
|
582
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
583
|
+
const tabParam = urlParams.get('tab');
|
|
584
|
+
if(['news','forum','store','gallery','code'].includes(tabParam)) this.curTab = tabParam;
|
|
585
|
+
const vParam = urlParams.get('v');
|
|
586
|
+
if (vParam) {
|
|
587
|
+
this.curTab = 'gallery';
|
|
588
|
+
fetch(`re_store.php?action=get_post&id=${vParam}`).then(r => r.json()).then(j => {
|
|
589
|
+
const item = j.data || j; if(item && (item.id || item.share_id)) this.openGalleryItem(item);
|
|
590
|
+
}).catch(e=>{});
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
await this.loadData(this.curTab);
|
|
594
|
+
await this.loadSearchProducts();
|
|
595
|
+
await this.warmupAiPool(this.curTab);
|
|
596
|
+
this.refreshInterestLayout(this.curTab);
|
|
597
|
+
|
|
598
|
+
// [무한 스크롤 완벽 지원] 감지 범위(rootMargin)를 600px로 대폭 늘려서 끊김없이 로드되도록 처리
|
|
599
|
+
const obs = new IntersectionObserver((entries) => {
|
|
600
|
+
entries.forEach(x => {
|
|
601
|
+
if (x.isIntersecting) {
|
|
602
|
+
const tab = x.target.dataset.tab;
|
|
603
|
+
if (tab === this.curTab && !this.loading[tab] && !this.end[tab]) {
|
|
604
|
+
this.loadData(tab);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
});
|
|
608
|
+
}, { rootMargin: '600px', threshold: 0.01 });
|
|
609
|
+
|
|
610
|
+
this.$nextTick(() => {
|
|
611
|
+
document.querySelectorAll('.loading-sentinel').forEach(el => {
|
|
612
|
+
el.style.display = 'block';
|
|
613
|
+
el.style.minHeight = '1px'; // 강제 영역 확보로 관찰 보장
|
|
614
|
+
obs.observe(el);
|
|
615
|
+
});
|
|
616
|
+
});
|
|
617
|
+
},
|
|
618
|
+
|
|
619
|
+
async switchTab(t){
|
|
620
|
+
this.curTab=t;
|
|
621
|
+
if(t==='news'){this.syncMarketWidgetMode(false);}
|
|
622
|
+
this.loadSearchProducts();
|
|
623
|
+
window.dispatchEvent(new CustomEvent('board-tab-change',{detail:{tab:t}}));
|
|
624
|
+
if(!this.items[t].length){ await this.loadData(t); await this.warmupAiPool(t); if(t!=='gallery')this.initialInterestSortDone[t]=true; }
|
|
625
|
+
this.refreshInterestLayout(t);
|
|
626
|
+
},
|
|
627
|
+
|
|
628
|
+
getTopInterestKeywords(limit=5){
|
|
629
|
+
return Object.entries(this.interestProfile || {})
|
|
630
|
+
.filter(([key, value]) => key.startsWith('kw_') && Number(value) > 0)
|
|
631
|
+
.sort((a, b) => Number(b[1]) - Number(a[1]))
|
|
632
|
+
.slice(0, limit)
|
|
633
|
+
.map(([key]) => key.replace(/^kw_/, ''))
|
|
634
|
+
.filter(Boolean);
|
|
635
|
+
},
|
|
636
|
+
|
|
637
|
+
getSponsorSearchQuery(base=''){
|
|
638
|
+
const terms = [];
|
|
639
|
+
String(base || '').split(/\s+/).forEach(v => {
|
|
640
|
+
const t = v.trim();
|
|
641
|
+
if (t && t.toLowerCase() !== 'all') terms.push(t);
|
|
642
|
+
});
|
|
643
|
+
this.getTopInterestKeywords(5).forEach(t => {
|
|
644
|
+
if (!terms.some(x => x.toLowerCase() === t.toLowerCase())) terms.push(t);
|
|
645
|
+
});
|
|
646
|
+
return terms.slice(0, 8).join(' ');
|
|
647
|
+
},
|
|
648
|
+
|
|
649
|
+
async loadSearchProducts(){
|
|
650
|
+
let q = '';
|
|
651
|
+
const isSearchMode = this.isProductSearchMode();
|
|
652
|
+
if (isSearchMode && this.curTab === 'news') q = this.newsQuery;
|
|
653
|
+
else if (isSearchMode && this.curTab === 'store') q = this.storeQuery;
|
|
654
|
+
else if (isSearchMode && this.curTab === 'forum') q = this.forumQuery;
|
|
655
|
+
else if (isSearchMode && this.curTab === 'code') q = this.codeQuery;
|
|
656
|
+
else if (isSearchMode && this.curTab === 'gallery') q = this.galleryQuery;
|
|
657
|
+
|
|
658
|
+
q = String(q || (isSearchMode ? this.searchQuery : '') || '').trim();
|
|
659
|
+
if(q === 'all') q = '';
|
|
660
|
+
|
|
661
|
+
try{
|
|
662
|
+
const productLimit = isSearchMode ? 24 : 3;
|
|
663
|
+
const lang = String(window.ISAI_SERVER_I18N?.locale || window.__RE_BOARD3_CONFIG__?.marketHeatmapLocale || 'all').trim();
|
|
664
|
+
const r = await fetch(`/re_products.php?action=list_products&q=${encodeURIComponent(q)}&limit=${productLimit}&lang=${encodeURIComponent(lang)}`);
|
|
665
|
+
const j = await r.json();
|
|
666
|
+
this.items.products = Array.isArray(j?.data) ? j.data :[];
|
|
667
|
+
this.$nextTick(()=>this.updateMacy(this.curTab));
|
|
668
|
+
}catch(e){ this.items.products=[]; }
|
|
669
|
+
},
|
|
670
|
+
|
|
671
|
+
async loadData(t) {
|
|
672
|
+
if (this.loading[t]||this.end[t]) return;
|
|
673
|
+
this.loading[t] = true;
|
|
674
|
+
try {
|
|
675
|
+
let u='';
|
|
676
|
+
if(t==='news') { const nl = window.__RE_BOARD3_CONFIG__?.newsLang || {}; u=`/re_board3.php?action=get_news&q=${encodeURIComponent(this.newsQuery)}&hl=${encodeURIComponent(nl.hl||'ko')}&gl=${encodeURIComponent(nl.gl||'KR')}&ceid=${encodeURIComponent(nl.ceid||'KR:ko')}`; }
|
|
677
|
+
else if(t==='gallery') u=`re_store.php?action=list_posts&type=gallery&page=${this.page[t]}&q=${encodeURIComponent(this.galleryQuery||'')}`;
|
|
678
|
+
else if(t==='store') u=`re_store.php?action=list_apps&page=${this.page[t]}&q=${encodeURIComponent(this.storeQuery||this.searchQuery||'')}`;
|
|
679
|
+
else if(t==='forum') u=`re_store.php?action=list_posts&type=forum&page=${this.page[t]}${this.currentTag?'&tag='+encodeURIComponent(this.currentTag):''}&q=${encodeURIComponent(this.forumQuery||'')}`;
|
|
680
|
+
else if(t==='code') u=`re_store.php?action=list_code_publish&page=${this.page[t]}&limit=24&q=${encodeURIComponent(this.codeQuery||'')}`;
|
|
681
|
+
|
|
682
|
+
const res = await fetch(u);
|
|
683
|
+
const d = (await res.json()).data||[];
|
|
684
|
+
if(!d.length) {
|
|
685
|
+
this.end[t]=true;
|
|
686
|
+
} else {
|
|
687
|
+
const pd = d.map(i=>{
|
|
688
|
+
i.title=this.decodeEscapedText(i.title); i.content=this.decodeEscapedText(i.content); i.snippet=this.decodeEscapedText(i.snippet);
|
|
689
|
+
if(i.image_url)i.image_url=i.image_url.replace(/(imgur\.com\/[a-zA-Z0-9]+)\.(jpg|png|webp)/i, (m,p1,p2)=>p1+'l.'+p2);
|
|
690
|
+
i.avatar=`https://api.dicebear.com/7.x/identicon/svg?seed=${encodeURIComponent(i.nickname||i.source||i.title||'U')}&backgroundColor=transparent`;
|
|
691
|
+
if(t==='forum'){ i.listText=String(i.content||i.title||'').replace(/<[^>]*>?/gm,' ').trim().slice(0, 100); }
|
|
692
|
+
else if(t==='news'){ i.cleanTitle=(i.title?.split(' - ')[0])||i.title; }
|
|
693
|
+
return i;
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
const existingKeys = new Set(this.items[t].map(x => x.id || x.link));
|
|
697
|
+
const newItems = pd.filter(x => !existingKeys.has(x.id || x.link));
|
|
698
|
+
|
|
699
|
+
// Alpine.js 배열 반응성을 100% 보장하기 위해 push 대신 재할당
|
|
700
|
+
if(newItems.length > 0) {
|
|
701
|
+
const wasEmpty = this.items[t].length === 0;
|
|
702
|
+
const currentPage = this.page[t];
|
|
703
|
+
this.items[t] = [...this.items[t], ...newItems];
|
|
704
|
+
this.applyInterestSort(t);
|
|
705
|
+
if(t==='news') this.end[t]=true; else this.page[t]++;
|
|
706
|
+
if (wasEmpty || currentPage <= 1) {
|
|
707
|
+
this.refreshInterestLayout(t);
|
|
708
|
+
} else {
|
|
709
|
+
this.$nextTick(() => { this.updateMacy(t); setTimeout(() => { this.updateMacy(t); }, 150); });
|
|
710
|
+
}
|
|
711
|
+
} else {
|
|
712
|
+
this.end[t] = true;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
} catch(e){} finally { this.loading[t]=false; }
|
|
716
|
+
},
|
|
717
|
+
|
|
718
|
+
updateMacy(t) {
|
|
719
|
+
this.$nextTick(() => {
|
|
720
|
+
if (!macyInstances[t]) {
|
|
721
|
+
macyInstances[t] = Macy({
|
|
722
|
+
container: `#${t}-feed`,
|
|
723
|
+
trueOrder: true, waitForImages: true, margin: 16, columns: 6,
|
|
724
|
+
breakAt: { 1600: 6, 1200: 4, 900: 3, 640: 2 }
|
|
725
|
+
});
|
|
726
|
+
macyInstances[t].runOnImageLoad(() => macyInstances[t].recalculate(true, true), true);
|
|
727
|
+
} else {
|
|
728
|
+
macyInstances[t].recalculate(true, true);
|
|
729
|
+
}
|
|
730
|
+
});
|
|
731
|
+
},
|
|
732
|
+
parseTags(t){ return String(t||'').replace(/<[^>]*>?/gm,'').replace(/(^|\s)#([a-zA-Z0-9가-힣_]+)/g,(m,p,tag)=>`${p}<span class="inline-block bg-gray-100 dark:bg-gray-800 text-[10px] px-2 py-0.5 rounded-full cursor-pointer font-bold" onclick="event.stopPropagation(); window.location.href='/?tag=${encodeURIComponent(tag)}'">#${tag}</span>`); }
|
|
733
|
+
}));
|
|
734
|
+
});
|