Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

prompt: restore uniticker (web curtain) with healthy mix of all 6 feeds

Restores the unified ticker with chat, laer-klokken chat, sigiled media
(kidlisp / painting / tape / sigil-clocks), recent commits, handle count
and moods — round-robin interleaved across all buckets (old version only
mixed chat + laer + moods, media/commits/handles were collected but
never displayed).

Also:
- Adds a 'clock' branch to fetchContentItems so sigiled kidlisp clocks
from /api/tv?filter=sprinkle show up (~19/60 items were being dropped).
- Swaps the moods fetch from /api/mood/moods-of-the-day?list=1 (only 2
moods today) to /api/mood/all?limit=20 for a thicker feed.

Skips the heavy media preview tooltip (kidlisp eval / painting image /
tape audio visualizer) for now — can restore that separately if desired.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+493 -1
+493 -1
system/public/aesthetic.computer/disks/prompt.mjs
··· 217 217 let contentTickerButton; // Button for content ticker hover interaction 218 218 let mediaPreviewBox; // Shared preview box renderer for all media types 219 219 220 + // 🎰 UNITICKER - Unified ticker combining chat, laer-klokken chat, sigiled media, commits, handles, moods 221 + let uniticker; // Single ticker for all combined content 222 + let unitickerButton; // Button for hover interaction 223 + let unitickerItems = []; // Combined items: {type, text, code, color, ...} 224 + let unitickerHoveredItem = null; // Currently hovered item for tooltip 225 + let unitickerIdleFrames = 0; 226 + let unitickerLastPenX = -1; 227 + let unitickerLastPenY = -1; 228 + let unitickerAutoSelectedItem = null; 229 + let unitickerAutoSelectedX = 0; 230 + let unitickerAutoSelectedWidth = 0; 231 + const UNITICKER_IDLE_THRESHOLD = 120; // ~2 seconds at 60fps before auto-selecting 232 + 220 233 221 234 const tinyTickers = true; // Use MatrixChunky8 font for tighter, smaller tickers 222 235 let contentItems = []; // Store fetched content: {type: 'kidlisp'|'painting'|'tape', code: string, source?: string} ··· 5865 5878 }); 5866 5879 } 5867 5880 5881 + // 🎰 UNITICKER - unified ticker combining chat, laer-klokken chat, sigiled media, commits, handles, moods 5882 + $.layer(2); // Render ticker on top of tooltips 5883 + 5884 + const loginY = screen.height / 2; 5885 + const tickerHeight = 8; // MatrixChunky8 glyph height 5886 + const tickerPadding = 5; 5887 + const tickerFont = "MatrixChunky8"; 5888 + const unitickerY = loginY + 44; // Position below login/signup buttons 5889 + 5890 + // Build item buckets from all sources 5891 + const hasChatMessages = $.chat?.messages && $.chat.messages.length > 0; 5892 + const hasClockMessages = clockChatMessages && clockChatMessages.length > 0; 5893 + const hasMediaItems = contentItems.length > 0; 5894 + 5895 + const chatItems = []; 5896 + const clockItems = []; 5897 + const mediaItems = []; 5898 + const commitItems = []; 5899 + const statsItems = []; 5900 + const moodItems = []; 5901 + 5902 + // Chat messages (blue) - jump to 'chat' 5903 + if (hasChatMessages) { 5904 + const numMessages = Math.min(6, $.chat.messages.length); 5905 + $.chat.messages.slice(-numMessages).forEach((msg) => { 5906 + const sanitizedText = msg.text.replace(/[\r\n]+/g, " "); 5907 + const displayText = msg.from + ": " + sanitizedText.slice(0, 50) + (sanitizedText.length > 50 ? "..." : ""); 5908 + chatItems.push({ type: "chat", text: displayText, code: "chat", color: [100, 200, 255] }); 5909 + }); 5910 + } 5911 + 5912 + // Laer-Klokken chat (orange) - jump to 'laer-klokken' 5913 + if (hasClockMessages) { 5914 + const numClockMessages = Math.min(4, clockChatMessages.length); 5915 + clockChatMessages.slice(-numClockMessages).forEach((msg) => { 5916 + const sanitizedText = (msg.text || msg.message || "").replace(/[\r\n]+/g, " "); 5917 + const from = msg.from || msg.handle || msg.author || "anon"; 5918 + const displayText = from + ": " + sanitizedText.slice(0, 50) + (sanitizedText.length > 50 ? "..." : ""); 5919 + clockItems.push({ type: "clock", text: displayText, code: "laer-klokken", color: [255, 180, 80] }); 5920 + }); 5921 + } 5922 + 5923 + // Sigiled media (kidlisp $, painting #, tape !, sigil-clock *) - jump to its own code 5924 + if (hasMediaItems) { 5925 + contentItems.forEach((item) => { 5926 + let prefix, color, code, itemType; 5927 + if (item.type === "kidlisp") { 5928 + itemType = "kidlisp"; 5929 + prefix = "$"; 5930 + color = [150, 255, 150]; 5931 + code = `$${item.code}`; 5932 + } else if (item.type === "painting") { 5933 + itemType = "painting"; 5934 + prefix = "#"; 5935 + color = [255, 150, 255]; 5936 + code = 5937 + !item.handle || item.handle === "undefined" || item.handle === "null" 5938 + ? `painting#${item.code}` 5939 + : `#${item.code}`; 5940 + } else if (item.type === "tape") { 5941 + itemType = "tape"; 5942 + prefix = "!"; 5943 + color = [255, 200, 100]; 5944 + code = `!${item.code}`; 5945 + } else if (item.type === "clock") { 5946 + // Sigiled kidlisp clock: served at /*code 5947 + itemType = "sigil-clock"; 5948 + prefix = "*"; 5949 + color = [200, 220, 255]; 5950 + code = `*${item.code}`; 5951 + } else { 5952 + return; // Unknown media type — skip 5953 + } 5954 + mediaItems.push({ type: itemType, text: `${prefix}${item.code}`, code, color, mediaItem: item }); 5955 + }); 5956 + } 5957 + 5958 + // Commits (lime) - jump to 'commits' 5959 + if (recentCommits.length > 0) { 5960 + const numCommits = Math.min(5, recentCommits.length); 5961 + recentCommits.slice(0, numCommits).forEach((commit) => { 5962 + let author = commit.author; 5963 + if (author === "Jeffrey Alan Scudder") author = "@jeffrey"; 5964 + commitItems.push({ 5965 + type: "commit", 5966 + text: `[${commit.hash}] ${commit.message} ~${author}`, 5967 + code: "commits", 5968 + color: [100, 255, 100], 5969 + }); 5970 + }); 5971 + } 5972 + 5973 + // Handles stat (pink) - jump to 'handles' 5974 + if (handles) { 5975 + statsItems.push({ 5976 + type: "stats", 5977 + text: `${handles.toLocaleString()} handles`, 5978 + code: "handles", 5979 + color: [255, 100, 255], 5980 + }); 5981 + } 5982 + 5983 + // Moods (cyan) - jump to 'mood' 5984 + if (motdCandidates.length > 0) { 5985 + motdCandidates.forEach((candidate) => { 5986 + if (!candidate?.mood) return; 5987 + const moodText = candidate.mood.replace(/[\r\n]+/g, " ").slice(0, 60); 5988 + const handle = candidate.handle || candidate.handleInfo?.handle || candidate.owner?.handle || candidate.user?.handle || null; 5989 + const from = handle ? (handle.startsWith("@") ? handle : `@${handle}`) : "anon"; 5990 + moodItems.push({ type: "mood", text: `${from}: ${moodText}`, code: "mood", color: [100, 255, 220] }); 5991 + }); 5992 + } 5993 + 5994 + // 🎲 Round-robin interleave across all 6 buckets for a healthy mix 5995 + unitickerItems = []; 5996 + const allBuckets = [chatItems, clockItems, mediaItems, commitItems, statsItems, moodItems].filter( 5997 + (b) => b.length > 0, 5998 + ); 5999 + const bucketIndices = allBuckets.map(() => 0); 6000 + const totalItems = allBuckets.reduce((sum, b) => sum + b.length, 0); 6001 + 6002 + let placed = 0; 6003 + let bucketCursor = 0; 6004 + while (placed < totalItems) { 6005 + let attempts = 0; 6006 + while ( 6007 + bucketIndices[bucketCursor] >= allBuckets[bucketCursor].length && 6008 + attempts < allBuckets.length 6009 + ) { 6010 + bucketCursor = (bucketCursor + 1) % allBuckets.length; 6011 + attempts++; 6012 + } 6013 + if (attempts >= allBuckets.length) break; 6014 + const bucket = allBuckets[bucketCursor]; 6015 + const idx = bucketIndices[bucketCursor]; 6016 + if (idx < bucket.length) { 6017 + unitickerItems.push(bucket[idx]); 6018 + bucketIndices[bucketCursor]++; 6019 + placed++; 6020 + } 6021 + bucketCursor = (bucketCursor + 1) % allBuckets.length; 6022 + } 6023 + 6024 + const showUniticker = screen.height >= 180 && unitickerItems.length > 0; 6025 + 6026 + if (showUniticker) { 6027 + const fullText = unitickerItems.map((item) => item.text).join(" · "); 6028 + 6029 + if (!uniticker) { 6030 + uniticker = new $.gizmo.Ticker(fullText, { speed: 1, separator: " · " }); 6031 + uniticker.paused = false; 6032 + uniticker.offset = 0; 6033 + } else { 6034 + uniticker.setText(fullText); 6035 + } 6036 + 6037 + if (uniticker && !uniticker.paused) uniticker.update($); 6038 + 6039 + const boxHeight = tickerHeight + tickerPadding * 2; 6040 + const boxY = unitickerY - tickerPadding; 6041 + 6042 + if (!unitickerButton) { 6043 + unitickerButton = new $.ui.Button({ x: 0, y: boxY, w: screen.width, h: boxHeight }); 6044 + unitickerButton.noEdgeDetection = true; 6045 + unitickerButton.noRolloverActivation = true; 6046 + unitickerButton.stickyScrubbing = true; 6047 + } else { 6048 + unitickerButton.box.x = 0; 6049 + unitickerButton.box.y = boxY; 6050 + unitickerButton.box.w = screen.width; 6051 + unitickerButton.box.h = boxHeight; 6052 + } 6053 + 6054 + if (!unitickerButton.disabled) { 6055 + const isTickerHover = unitickerButton.over && !unitickerButton.down; 6056 + const bgAlpha = unitickerButton.down ? 100 : isTickerHover ? 80 : 60; 6057 + ink([20, 20, 30, bgAlpha]).box(0, boxY, screen.width, boxHeight - 1); 6058 + 6059 + const borderAlpha = isTickerHover ? 230 : 180; 6060 + const borderColor = isTickerHover ? [180, 120, 230, borderAlpha] : [150, 100, 200, borderAlpha]; 6061 + ink(borderColor).line(0, boxY, screen.width, boxY); 6062 + ink(borderColor).line(0, boxY + boxHeight - 1, screen.width, boxY + boxHeight - 1); 6063 + 6064 + const textY = unitickerY; 6065 + const tickerAlpha = unitickerButton.down ? 255 : 220; 6066 + 6067 + const separator = " · "; 6068 + const separatorWidth = $.text.box(separator, undefined, undefined, undefined, undefined, tickerFont).box.width; 6069 + const itemWidths = []; 6070 + let totalCycleWidth = 0; 6071 + unitickerItems.forEach((item, idx) => { 6072 + const w = $.text.box(item.text, undefined, undefined, undefined, undefined, tickerFont).box.width; 6073 + itemWidths.push(w); 6074 + totalCycleWidth += w; 6075 + if (idx < unitickerItems.length - 1) totalCycleWidth += separatorWidth; 6076 + }); 6077 + totalCycleWidth += separatorWidth; // trailing separator for seamless loop 6078 + 6079 + const rawOffset = uniticker.getOffset(); 6080 + const loopedOffset = totalCycleWidth > 0 ? rawOffset % totalCycleWidth : 0; 6081 + 6082 + let hoveredItem = null; 6083 + let hoveredItemX = 0; 6084 + let hoveredItemWidth = 0; 6085 + const visibleItemsForAutoSelect = []; 6086 + 6087 + // Track pen idle state 6088 + const penX = $.pen?.x ?? -1; 6089 + const penY = $.pen?.y ?? -1; 6090 + if (penX !== unitickerLastPenX || penY !== unitickerLastPenY) { 6091 + unitickerIdleFrames = 0; 6092 + unitickerLastPenX = penX; 6093 + unitickerLastPenY = penY; 6094 + if (hoveredItem) unitickerAutoSelectedItem = null; 6095 + } else { 6096 + unitickerIdleFrames++; 6097 + } 6098 + 6099 + const startMargin = 6; 6100 + const baseX = startMargin - loopedOffset; 6101 + const numCycles = Math.ceil((screen.width + totalCycleWidth) / totalCycleWidth) + 1; 6102 + 6103 + for (let cycle = 0; cycle < numCycles; cycle++) { 6104 + let currentX = baseX + cycle * totalCycleWidth; 6105 + unitickerItems.forEach((item, idx) => { 6106 + const text = item.text; 6107 + const color = item.color; 6108 + const textWidth = itemWidths[idx]; 6109 + 6110 + const mouseX = $.pen?.x ?? -1; 6111 + const mouseY = $.pen?.y ?? -1; 6112 + const isHovered = 6113 + mouseX >= currentX && 6114 + mouseX < currentX + textWidth && 6115 + mouseY >= boxY && 6116 + mouseY < boxY + boxHeight; 6117 + 6118 + if (isHovered && currentX >= 0 && currentX < screen.width) { 6119 + hoveredItem = item; 6120 + hoveredItemX = currentX; 6121 + hoveredItemWidth = textWidth; 6122 + } 6123 + 6124 + if (currentX >= 0 && currentX < screen.width) { 6125 + visibleItemsForAutoSelect.push({ item, x: currentX, width: textWidth }); 6126 + } 6127 + 6128 + const isVisible = currentX + textWidth > -10 && currentX < screen.width + 10; 6129 + if (isVisible) { 6130 + if (isHovered && !unitickerButton.down) { 6131 + $.ink([...color, 50]).box(currentX - 1, boxY + 1, textWidth + 2, boxHeight - 3); 6132 + $.ink(color, 255).write(text, { x: currentX, y: textY }, undefined, undefined, false, tickerFont); 6133 + } else { 6134 + $.ink(color, tickerAlpha).write(text, { x: currentX, y: textY }, undefined, undefined, false, tickerFont); 6135 + } 6136 + } 6137 + 6138 + currentX += textWidth; 6139 + 6140 + const sepVisible = currentX + separatorWidth > -10 && currentX < screen.width + 10; 6141 + if (sepVisible) { 6142 + const blinkAlpha = 120 + Math.sin(motdFrame * 0.2) * 60; 6143 + ink([180, 180, 200], blinkAlpha).write(separator, { x: currentX, y: textY }, undefined, undefined, false, tickerFont); 6144 + } 6145 + currentX += separatorWidth; 6146 + }); 6147 + } 6148 + 6149 + unitickerHoveredItem = hoveredItem; 6150 + unitickerButton.hoveredItem = hoveredItem; 6151 + 6152 + // 🎯 Auto-select an item when idle (picks from center-right, scrolls with ticker) 6153 + let displayItem = hoveredItem; 6154 + let displayItemX = hoveredItemX; 6155 + let displayItemWidth = hoveredItemWidth; 6156 + 6157 + if (!hoveredItem && unitickerIdleFrames >= UNITICKER_IDLE_THRESHOLD && visibleItemsForAutoSelect.length > 0) { 6158 + visibleItemsForAutoSelect.sort((a, b) => a.x - b.x); 6159 + 6160 + if (unitickerAutoSelectedItem) { 6161 + const stillVisible = visibleItemsForAutoSelect.find( 6162 + (v) => v.item === unitickerAutoSelectedItem && v.x >= -v.width * 0.3, 6163 + ); 6164 + if (stillVisible) { 6165 + displayItem = stillVisible.item; 6166 + displayItemX = stillVisible.x; 6167 + displayItemWidth = stillVisible.width; 6168 + unitickerAutoSelectedX = stillVisible.x; 6169 + unitickerAutoSelectedWidth = stillVisible.width; 6170 + } else { 6171 + unitickerAutoSelectedItem = null; 6172 + } 6173 + } 6174 + 6175 + if (!unitickerAutoSelectedItem && visibleItemsForAutoSelect.length > 0) { 6176 + const minThreshold = screen.width * 0.4; 6177 + const maxThreshold = screen.width * 0.7; 6178 + const centered = visibleItemsForAutoSelect.filter( 6179 + (v) => v.x >= minThreshold && v.x <= maxThreshold, 6180 + ); 6181 + let target; 6182 + if (centered.length > 0) { 6183 + target = centered[centered.length - 1]; 6184 + } else { 6185 + const idealX = screen.width * 0.55; 6186 + target = visibleItemsForAutoSelect.reduce((best, v) => 6187 + Math.abs(v.x - idealX) < Math.abs(best.x - idealX) ? v : best, 6188 + ); 6189 + } 6190 + unitickerAutoSelectedItem = target.item; 6191 + unitickerAutoSelectedX = target.x; 6192 + unitickerAutoSelectedWidth = target.width; 6193 + displayItem = target.item; 6194 + displayItemX = target.x; 6195 + displayItemWidth = target.width; 6196 + } 6197 + } else if (hoveredItem) { 6198 + unitickerAutoSelectedItem = null; 6199 + } 6200 + 6201 + // 🎯 Contextual "Enter 'code' to ..." tooltip below hovered/auto-selected item 6202 + if (displayItem && !unitickerButton.down) { 6203 + let tooltipPrefix, tooltipCode, tooltipSuffix; 6204 + const doc = tooltipDocs?.[displayItem.code]; 6205 + if (doc?.desc) { 6206 + tooltipPrefix = "Enter '"; 6207 + tooltipCode = displayItem.code; 6208 + tooltipSuffix = `' to ${doc.desc.toLowerCase().replace(/\.$/, "")}`; 6209 + } else if (displayItem.type === "kidlisp") { 6210 + tooltipPrefix = "Enter '"; 6211 + tooltipCode = displayItem.code; 6212 + tooltipSuffix = "' to run"; 6213 + } else if (displayItem.type === "painting") { 6214 + tooltipPrefix = "Enter '"; 6215 + tooltipCode = displayItem.code; 6216 + tooltipSuffix = "' to view"; 6217 + } else if (displayItem.type === "tape") { 6218 + tooltipPrefix = "Enter '"; 6219 + tooltipCode = displayItem.code; 6220 + tooltipSuffix = "' to listen"; 6221 + } else if (displayItem.type === "sigil-clock") { 6222 + tooltipPrefix = "Enter '"; 6223 + tooltipCode = displayItem.code; 6224 + tooltipSuffix = "' to run"; 6225 + } else if (displayItem.type === "commit") { 6226 + tooltipPrefix = "Enter '"; 6227 + tooltipCode = displayItem.code; 6228 + tooltipSuffix = "' to browse"; 6229 + } else if (displayItem.type === "stats") { 6230 + tooltipPrefix = "Enter '"; 6231 + tooltipCode = displayItem.code; 6232 + tooltipSuffix = "' to browse"; 6233 + } else if (displayItem.type === "mood") { 6234 + tooltipPrefix = "Enter '"; 6235 + tooltipCode = displayItem.code; 6236 + tooltipSuffix = "' to set yours"; 6237 + } else { 6238 + tooltipPrefix = "Enter '"; 6239 + tooltipCode = displayItem.code; 6240 + tooltipSuffix = "'"; 6241 + } 6242 + const tooltipText = tooltipPrefix + tooltipCode + tooltipSuffix; 6243 + const tooltipWidth = $.text.box(tooltipText, undefined, undefined, undefined, undefined, tickerFont).box.width; 6244 + const tooltipHeight = 10; 6245 + const tooltipPadding = 3; 6246 + 6247 + let tooltipX = displayItemX + displayItemWidth / 2 - tooltipWidth / 2 - tooltipPadding; 6248 + const tooltipY = boxY + boxHeight + 10; 6249 + tooltipX = Math.max(4, Math.min(tooltipX, screen.width - tooltipWidth - tooltipPadding * 2 - 4)); 6250 + 6251 + const alphaMultiplier = hoveredItem ? 1 : 0.7; 6252 + const tooltipBgColor = [...displayItem.color, Math.round(180 * alphaMultiplier)]; 6253 + ink([10, 10, 20, Math.round(220 * alphaMultiplier)]).box( 6254 + tooltipX, 6255 + tooltipY, 6256 + tooltipWidth + tooltipPadding * 2, 6257 + tooltipHeight + tooltipPadding * 2, 6258 + ); 6259 + ink(tooltipBgColor).box( 6260 + tooltipX, 6261 + tooltipY, 6262 + tooltipWidth + tooltipPadding * 2, 6263 + tooltipHeight + tooltipPadding * 2, 6264 + "inline", 6265 + ); 6266 + 6267 + const ttTextY = tooltipY + tooltipPadding + 1; 6268 + let textX = tooltipX + tooltipPadding; 6269 + const baseAlpha = Math.round(255 * alphaMultiplier); 6270 + const dimAlpha = Math.round(180 * alphaMultiplier); 6271 + 6272 + ink([255, 255, 255, dimAlpha]).write(tooltipPrefix, { x: textX, y: ttTextY }, undefined, undefined, false, tickerFont); 6273 + textX += $.text.box(tooltipPrefix, undefined, undefined, undefined, undefined, tickerFont).box.width; 6274 + 6275 + ink([...displayItem.color, baseAlpha]).write(tooltipCode, { x: textX, y: ttTextY }, undefined, undefined, false, tickerFont); 6276 + textX += $.text.box(tooltipCode, undefined, undefined, undefined, undefined, tickerFont).box.width; 6277 + 6278 + ink([255, 255, 255, dimAlpha]).write(tooltipSuffix, { x: textX, y: ttTextY }, undefined, undefined, false, tickerFont); 6279 + 6280 + const arrowX = displayItemX + displayItemWidth / 2; 6281 + const arrowY = tooltipY; 6282 + const arrowAlpha = Math.round(200 * alphaMultiplier); 6283 + ink([...displayItem.color, arrowAlpha]).line(arrowX, arrowY, arrowX - 3, arrowY - 3); 6284 + ink([...displayItem.color, arrowAlpha]).line(arrowX, arrowY, arrowX + 3, arrowY - 3); 6285 + 6286 + if (!hoveredItem && unitickerAutoSelectedItem) { 6287 + $.ink([...displayItem.color, 30]).box(displayItemX - 1, boxY + 1, displayItemWidth + 2, boxHeight - 3); 6288 + } 6289 + } 6290 + } 6291 + } else { 6292 + uniticker = null; 6293 + unitickerButton = null; 6294 + unitickerHoveredItem = null; 6295 + unitickerAutoSelectedItem = null; 6296 + unitickerIdleFrames = 0; 6297 + } 6298 + 6299 + $.layer(1); 6300 + 5868 6301 // 📦 Commit hash button - shows version status / update availability 5869 6302 // Hide commits button when KidLisp button is active (they share the same screen area) 5870 6303 if (versionInfo && versionInfo.deployed && !(kidlispBtn && !kidlispBtn.btn.disabled)) { ··· 7316 7749 } 7317 7750 } 7318 7751 7752 + // 🎰 UNITICKER button handler (unified ticker) 7753 + if (unitickerButton && !unitickerButton.disabled) { 7754 + unitickerButton.act(e, { 7755 + down: () => { 7756 + downSound(); 7757 + if (uniticker) { 7758 + uniticker.paused = true; 7759 + unitickerButton.scrubStartX = e.x; 7760 + unitickerButton.scrubInitialOffset = uniticker.getOffset(); 7761 + unitickerButton.hasScrubbed = false; 7762 + } 7763 + needsPaint(); 7764 + }, 7765 + scrub: () => { 7766 + if (uniticker && e.x !== undefined && e.y !== undefined) { 7767 + const scrubDelta = e.x - unitickerButton.scrubStartX; 7768 + let newOffset = unitickerButton.scrubInitialOffset - scrubDelta; 7769 + if (newOffset < 0) newOffset = newOffset * 0.3; // Elastic bounds 7770 + 7771 + uniticker.setOffset(newOffset); 7772 + unitickerButton.hasScrubbed = Math.abs(scrubDelta) > 5; 7773 + 7774 + synth({ 7775 + type: "sine", 7776 + tone: 1200 + Math.abs(scrubDelta) * 2, 7777 + attack: 0.005, 7778 + decay: 0.9, 7779 + volume: 0.08, 7780 + duration: 0.01, 7781 + }); 7782 + 7783 + needsPaint(); 7784 + } 7785 + }, 7786 + push: () => { 7787 + if (!unitickerButton.hasScrubbed) pushSound(); 7788 + 7789 + if (!unitickerButton.hasScrubbed && unitickerButton.hoveredItem) { 7790 + const destination = unitickerButton.hoveredItem.code; 7791 + system.prompt.input.text = destination; 7792 + system.prompt.input.snap(); 7793 + jump(destination); 7794 + } else if (uniticker) { 7795 + uniticker.paused = false; 7796 + } 7797 + unitickerButton.hasScrubbed = false; 7798 + }, 7799 + cancel: () => { 7800 + cancelSound(); 7801 + if (uniticker) uniticker.paused = false; 7802 + unitickerButton.hasScrubbed = false; 7803 + }, 7804 + }); 7805 + } 7806 + 7319 7807 // (DEPRECATED) Chat ticker button 7320 7808 if (chatTickerButton && !chatTickerButton.disabled) { 7321 7809 chatTickerButton.act(e, { ··· 7550 8038 (osBtn?.btn?.disabled === false && osBtn?.btn?.box.contains(e)) || 7551 8039 (blankAdBtn?.btn?.disabled === false && blankAdBtn?.btn?.box.contains(e)) || 7552 8040 (products.getShopBoxButton()?.disabled === false && products.getShopBoxButton()?.box.contains(e)) || 8041 + (unitickerButton?.disabled === false && unitickerButton?.box.contains(e)) || 7553 8042 (chatTickerButton?.disabled === false && chatTickerButton?.box.contains(e)) || 7554 8043 (contentTickerButton?.disabled === false && contentTickerButton?.box.contains(e)) || 7555 8044 isOverMotdHandle || ··· 8199 8688 motdController = new AbortController(); 8200 8689 8201 8690 try { 8202 - const res = await fetch("/api/mood/moods-of-the-day?list=1", { 8691 + const res = await fetch("/api/mood/all?limit=20", { 8203 8692 signal: motdController.signal, 8204 8693 }); 8205 8694 if (res.status === 200) { ··· 8278 8767 mediaUrl: item.media?.url || null, 8279 8768 title: item.title || null 8280 8769 }); 8770 + } else if (item.type === 'clock') { 8771 + // Sigiled kidlisp clock pieces served at /*code 8772 + items.push({ ...baseItem, source: item.source, isSigilClock: true }); 8281 8773 } 8282 8774 }); 8283 8775 } else {