ddd
This commit is contained in:
parent
0a343ecbc5
commit
c3a6f5ff37
80 changed files with 2512 additions and 1625 deletions
|
|
@ -1,21 +1,23 @@
|
|||
'use strict';
|
||||
const cp=require('child_process');
|
||||
const fs=require('fs');
|
||||
const {root,path,vm,app,html,css,worker,appLogicSource,assert,functionSource,loadBendPuzzle,loadAppLogic,starterPuzzle,read}=require('./helpers/app-source');
|
||||
const {root,path,vm,app,html,css,worker,appLogicSource,buildMeta,assert,functionSource,loadBendPuzzle,loadAppLogic,starterPuzzle,read}=require('./helpers/app-source');
|
||||
const serverSource=read('server.js');
|
||||
const packageVersion=JSON.parse(read('package.json')).version,appVersion=packageVersion.split('.').slice(0,2).join('.');
|
||||
const storeCatalog=JSON.parse(read('store-catalog.json'));
|
||||
for(const file of ['app.js','app-logic.js','puzzle-core.js','puzzle-worker.js','field-persistence.js','field-persistence-worker.js','server.js'])cp.execFileSync(process.execPath,['--check',path.join(root,file)],{stdio:'inherit'});
|
||||
assert(buildMeta.APP_VERSION===appVersion&&buildMeta.SAVE_SCHEMA===31&&buildMeta.STORAGE_SCHEMA===30&&buildMeta.IDB_LAYOUT_VERSION===8&&buildMeta.FIELD_STORAGE_FORMAT===2&&buildMeta.GAMEPLAY_DATA_VERSION===3&&buildMeta.WORLD_GENERATION==='v47-field-reset-20260728-interaction-fix','Canonical build metadata does not match the package or persistence contracts');
|
||||
for(const marker of [
|
||||
`APP_VERSION='${appVersion}',SAVE_SCHEMA=31,STORAGE_SCHEMA=30,IDB_LAYOUT_VERSION=8,FIELD_STORAGE_FORMAT=2,GAMEPLAY_DATA_VERSION=3,WORLD_GENERATION='v47-field-reset-20260728-interaction-fix'`,
|
||||
'SPECIAL_CELL_MIN_LEVEL=5,SPECIAL_CELL_DEBUG_ALL_LEVELS=false',
|
||||
'SPECIAL_CELL_MIN_LEVEL=5',
|
||||
'shapeCandidatesForLevel','addSpecialCellPattern','addWarpSpecial','addLockSpecial','addCrossingSpecial',
|
||||
'specialPathValid','crossingsSatisfied','activateCrossing','pathRenderSegments','pathStrokePieces','pathProgressAtCell','specialCellInfoMap','normalizeSpecialCells'
|
||||
])assert(app.includes(marker),`Missing v47 marker: ${marker}`);
|
||||
assert(!app.includes('SPECIAL_CELL_DEBUG_ALL_LEVELS'),'Special-cell generation still contains a debug-all-levels switch');
|
||||
assert(app.includes('const AppLogic=globalThis.BendAppLogic')&&app.includes('AppLogic.shapeCandidatesForLevel')&&app.includes('AppLogic.stateForStorage')&&app.includes('AppLogic.collectConnectedLineComponent'),'Application does not consume the shared pure-logic module');
|
||||
assert(html.includes(`style.css?v=${appVersion}`)&&html.includes(`puzzle-core.js?v=${appVersion}-5`)&&html.includes(`app-logic.js?v=${appVersion}`)&&html.includes(`field-persistence.js?v=${appVersion}`)&&html.includes(`app.js?v=${appVersion}`),'Web assets do not match package/app/generator version');
|
||||
assert(worker.includes(`puzzle-core.js?v=${appVersion}-5`),'Worker imports an old puzzle-core asset');
|
||||
assert(html.includes('build-meta.js')&&html.indexOf('build-meta.js')<html.indexOf('puzzle-core.js')&&html.indexOf('shared-contracts.js')<html.indexOf('app.js'),'Canonical metadata/contracts are not loaded before application assets');
|
||||
assert(worker.includes('build-meta.js')&&worker.includes('BendBuildMeta.APP_VERSION')&&worker.includes('BendBuildMeta.GENERATOR_VERSION'),'Worker does not derive its puzzle-core asset version from canonical metadata');
|
||||
assert(functionSource('createPuzzleWorker').includes('message.error?job.reject')&&!functionSource('createPuzzleWorker').includes('message.error?generatePuzzleOnMainThread'),'Algorithmic worker failures are retried redundantly on the main thread');
|
||||
assert(html.includes(`<small>v${appVersion}</small>`)&&html.includes(`v${appVersion}</title>`),'Visible version does not match package/app version');
|
||||
assert(app.includes('const appVersionLabel=`v${APP_VERSION}`')&&app.includes('brandVersion.textContent=appVersionLabel')&&app.includes('document.title=`${document.title.replace('),'Visible version is not derived from canonical build metadata');
|
||||
assert(html.includes('id="viewport" tabindex="-1"')&&['modal','storeModal','inventoryModal','timeAttackModal'].every(id=>html.includes(`id="${id}" aria-hidden="true" inert`)),'Hidden dialogs are not inert or the viewport is not programmatically focusable');
|
||||
assert(functionSource('closeDialogRoot').includes('focusOutsideDialog(root,preferredFocus)')&&functionSource('closeDialogRoot').indexOf('focusOutsideDialog')<functionSource('closeDialogRoot').indexOf("setAttribute('aria-hidden','true')"),'Dialog hiding occurs before focus leaves the dialog');
|
||||
assert(functionSource('openDialogRoot').includes('setDialogInert(root,false)')&&functionSource('closeDialogRoot').includes('setDialogInert(root,true)'),'Dialog inert state is not synchronized with visibility');
|
||||
|
|
@ -23,7 +25,7 @@ assert(!app.includes('labyrinth-seed')&&!app.includes('giantCompactShapes')&&!ap
|
|||
assert(!app.includes('anomalyAt')&&!app.includes('anomalyScoreMultiplier')&&!app.includes('renderAnomalyOverlays')&&!css.includes('.anomaly'),'Retired anomaly code remains');
|
||||
assert(!serverSource.includes('migrateLegacyPlayer')&&!serverSource.includes('value.metas')&&!serverSource.includes('value.states'),'Cloud server still reads or migrates the retired monolithic player format');
|
||||
assert(html.includes('id="clearFeed"')&&css.includes('.clear-feed-item'),'Shared clear feed is missing above the minimap');
|
||||
assert(serverSource.includes("const WORLD_FILE = path.join(DATA_DIR, 'shared-world.json')")&&serverSource.includes("url.pathname==='/api/cloud/profile'")&&serverSource.includes('withWorldQueue')&&serverSource.includes('clearEvents'),'Shared-world storage, profile naming, serialization, or clear feed API is missing');
|
||||
assert(serverSource.includes("const WORLD_FILE = path.join(DATA_DIR, 'shared-world.json')")&&serverSource.includes(".add('POST','/api/cloud/profile',handleCloudProfile)")&&serverSource.includes('withWorldQueue')&&serverSource.includes('clearEvents'),'Shared-world storage, profile naming, serialization, or clear feed API is missing');
|
||||
assert(functionSource('checkSolvedAndExpand').indexOf('pullCloudWorld(true)')<functionSource('checkSolvedAndExpand').indexOf('st.solved=true')&&functionSource('checkSolvedAndExpand').indexOf('pushCloudPending()')<functionSource('checkSolvedAndExpand').indexOf('expandMeta('),'Clear publication is not server-validated before shared expansion');
|
||||
assert(functionSource('mergeSnapshotIntoData').includes('authoritativeWorld')&&functionSource('pullCloudWorld').includes('initial&&previousRevision===0&&targetRevision>0')&&functionSource('clearSharedWorldJournalRow').includes('cloudOutboxDeleteKeys'),'Initial shared-world adoption does not replace stale local world rows or clean their outbox entries');
|
||||
assert(functionSource('noteCloudRow').includes("kind==='state'&&data?.states?.[id]?.solved!==true")&&functionSource('currentCloudPending').includes("solved===true"),'Unfinished personal paths can still enter the shared durable outbox');
|
||||
|
|
@ -42,7 +44,7 @@ assert(functionSource('makeSpecialMarker').includes("class:'key-ring'")&&!functi
|
|||
assert(css.includes('.board-card.solved .special-cell-layer'),'Solved boards do not hide special cells');
|
||||
assert(functionSource('selectBoard').includes('setActiveBoard(b.id)')&&functionSource('setActiveBoard').includes('previous.drawing?.pointerId==null'),'Board selection does not preserve an active pointer draw while reconciling inactive keyboard state');
|
||||
assert(!css.includes('.board-card:not(.active)'),'Unselected-board styling remains active');
|
||||
assert(css.includes('.board-card.hud-current:not(.solved) .board-label')&&functionSource('boardPlayHudVisible').includes('hudBoardId===b?.id')&&functionSource('renderBoardNow').includes("card.classList.toggle('hud-current',hudVisible)"),'Board HUD is not limited to an actively played board');
|
||||
assert(html.includes('id="boardHudLayer"')&&css.includes('.board-label.hud-visible:not([hidden])')&&css.includes('.board-label[hidden]{display:none!important}')&&functionSource('boardPlayHudVisible').includes('hudBoardId===b?.id')&&functionSource('activateBoardPointerDrag').includes('activateBoardHud(b)')&&functionSource('refreshInteractionState').includes('setBoardHudVisibility(board,boardPlayHudVisible(board))')&&functionSource('renderBoardNow').includes('setBoardHudVisibility(b,hudVisible)'),'Board HUD is not retained for the last manipulated board or is still clipped inside the board');
|
||||
assert(functionSource('gateFromCell').includes('maxPixels')&&functionSource('extendPointerTo').includes('active.startGate,20'),'Opposite gate selection is not distance-limited');
|
||||
assert(functionSource('renderBoardNow').includes('pathStrokePieces(segments,startColor,endColor)')&&functionSource('pathColorAtCell').includes('pathProgressAtCell'),'Line colors are not blended along cumulative route length');
|
||||
assert(functionSource('updateSelectedProgress').includes('b.meta.level')&&!functionSource('updateSelectedProgress').includes('filled'),'Top HUD includes information other than level');
|
||||
|
|
@ -53,22 +55,22 @@ assert(functionSource('syncBoundaryConnections').includes('boundaryColorSource(m
|
|||
assert(!app.includes('level-min-10')&&!app.includes('level-max-10')&&!app.includes('fieldEffects')&&!app.includes('fieldOverlayCanvas')&&!serverSource.includes('/api/player/place-field'),'Difficulty adjustment items or their field implementation remain active');
|
||||
assert(functionSource('makeBoard').includes('cellShape')&&functionSource('makeBoard').includes('boardCellsPath')&&!functionSource('makeBoard').includes('cellHits'),'Detailed boards do not use compound SVG paths or still allocate per-cell hit nodes');
|
||||
assert(functionSource('desiredInteractiveBoardIds').includes('for(const id of ids)')&&!app.includes('INTERACTIVE_DETAIL_CELL_BUDGET'),'Visible puzzles are not all selected for detailed rendering');
|
||||
assert(functionSource('shouldTeleportToUnsolvedBoard').includes('return false')&&!app.includes('function promoteStaticBoard(')&&!functionSource('bindBoard').includes('centerMeta('),'Board input can still promote a summary or teleport the camera');
|
||||
assert(!app.includes('shouldTeleportToUnsolvedBoard')&&!app.includes('makeStaticBoard')&&!app.includes('promoteStaticBoard')&&!functionSource('bindBoard').includes('centerMeta('),'Board input can still promote a summary or teleport the camera');
|
||||
assert(html.includes('id="fpsCounter"')&&functionSource('refreshFpsCounter').includes('FPS ${fpsLastValue}')&&app.includes('AUXILIARY_FPS=30')&&app.includes('DRAG_TARGET_FPS=60'),'Lightweight FPS display or split interaction budgets are missing');
|
||||
assert(css.includes('.gem-particle{position:fixed')&&css.includes('width:28px;height:28px')&&functionSource('completionEffect').includes('1800'),'Completion gems are not enlarged or retained long enough');
|
||||
assert(css.includes('#customEmojiCursor{')&&css.includes('overflow:visible')&&css.includes('#customEmojiCursor.flag-cursor{width:32px;height:32px;overflow:hidden'),'Non-flag custom cursors are still clipped or flag clipping is no longer isolated');
|
||||
assert(css.includes('#customEmojiCursor{')&&css.includes('overflow:visible')&&css.includes('#customEmojiCursor.flag-cursor{width:32px;height:32px;overflow:hidden')&&css.includes('.board-input-surface')&&css.includes('body.is-drawing #viewport')&&functionSource('updateCustomCursorFromPointer').includes("classList.contains('is-drawing')"),'Custom cursor coverage or drag-time cursor hiding is incomplete');
|
||||
|
||||
assert(functionSource('extendOne').includes('warpedDuringExtend=true')&&!functionSource('extendOne').includes('safeRelease(b.svg,pointerId)'),'Warp traversal still releases pointer capture');
|
||||
assert(functionSource('extendPointerTo').includes('pointerOffset'),'Warp continuation does not remap the pointer to the exit');
|
||||
assert(!app.includes('hysteresisDragPoint')&&!app.includes('DRAG_AXIS_LOCK_DISTANCE')&&!app.includes('POINTER_SNAP_RELEASE'),'Retired drawing hysteresis remains active');
|
||||
assert(functionSource('scheduleBoardDragFrame').includes('requestAnimationFrame')&&functionSource('queueBoardPointerMove').includes('scheduleBoardDragFrame')&&functionSource('bindBoard').includes('queueBoardPointerMove')&&functionSource('processBoardDragFrame').includes('const move=b.pendingPointerMove'),'Pointer movement is not frame-batched with ordered samples');
|
||||
assert(functionSource('queueCameraInteraction').includes('requestAnimationFrame')&&functionSource('movePan').includes('queueCameraInteraction'),'Camera panning is not frame-coalesced');
|
||||
assert(functionSource('scheduleBoardDragFrame').includes('requestFrame')&&functionSource('queueBoardPointerMove').includes('scheduleBoardDragFrame')&&!functionSource('queueBoardPointerMove').includes('updatePickupHandleOverlay')&&functionSource('bindBoard').includes('queueBoardPointerMove')&&functionSource('processBoardDragFrame').includes('scheduler.latest()')&&functionSource('processBoardDragFrame').includes('updatePickupHandleOverlay')&&!functionSource('processBoardDragFrame').includes('visualBlend')&&!css.includes('transition:transform 16.67ms linear'),'Pointer logic or pickup presentation is not confined to the capped frame lane');
|
||||
assert(functionSource('queueCameraInteraction').includes('cameraInteractionScheduler.push')&&functionSource('movePan').includes('queueCameraInteraction'),'Camera panning is not frame-coalesced');
|
||||
assert(functionSource('leftFieldPanAllowed').includes("classList?.contains('solved')")&&functionSource('beginPan').includes('e.button!==2&&!leftFieldPanAllowed(e)'),'Left-drag field panning is not isolated to solved or undiscovered space');
|
||||
assert(functionSource('processBoardDragFrame').includes('edgePanVelocity')&&functionSource('processBoardDragFrame').includes('applyCamera(true)'),'Drag edge auto-pan is missing');
|
||||
assert(!functionSource('makeBoard').includes('darkness')&&!css.includes('.darkness'),'Retired darkness rendering remains');
|
||||
assert(functionSource('positionBoardLabel').includes('hudPlacementCandidates')&&functionSource('positionBoardLabel').includes("placement.side==='S'"),'HUD does not move to a free edge');
|
||||
assert(functionSource('positionBoardLabel').includes("b.label.style.width=innerWidth+'px'")&&functionSource('positionBoardLabel').includes("b.label.style.top=(PAD-22)+'px'"),'Top/bottom HUD does not span the board edge or clear upper gates');
|
||||
assert(functionSource('gateHitBox').includes("side==='N'")&&functionSource('makeBoard').includes('...gateHitBox(gp,g.side)'),'Gate hit areas are not constrained to the owning board');
|
||||
assert(functionSource('positionBoardLabel').includes('hudPlacementCandidates')&&functionSource('positionBoardLabel').includes("side==='S'"),'HUD does not move to a free edge');
|
||||
assert(functionSource('positionBoardLabel').includes('viewportRect.width-margin-labelWidth')&&functionSource('positionBoardLabel').includes('viewportRect.height-margin-labelHeight')&&functionSource('makeBoard').includes('boardHudLayer?.append(label)'),'Board HUD is not viewport-clamped in a detached overlay layer');
|
||||
assert(functionSource('gateHitBox').includes("side==='N'")&&functionSource('makeBoard').includes('...gateHitBox(gp,g.side,g.internal)'),'Gate hit areas are not constrained to the owning board');
|
||||
assert(!app.includes('sharedGateVisible'),'No-op shared-gate visibility wrapper remains');
|
||||
assert(functionSource('isSolved').includes('crossingsSatisfied(st,p)'),'Crossing is not a prerequisite for normal board completion');
|
||||
assert(functionSource('crossingsSatisfied').includes('crossingStateAtCell')&&functionSource('activateCrossing').includes('path.cells.push(cell)'),'Crossing is not derived from the live overlapping line state');
|
||||
|
|
@ -78,9 +80,9 @@ assert(functionSource('resetSelectedBoard').includes('specialProgress={crossings
|
|||
assert(functionSource('resetSelectedBoard').includes('renderBoardNow(b)')&&!functionSource('resetSelectedBoard').includes('syncBoundaryConnections'),'Reset is not immediate or still recreates inherited routes');
|
||||
assert(!app.toLowerCase().includes('undolastreset')&&!html.toLowerCase().includes('undo'),'Visible reset undo remains active');
|
||||
|
||||
assert(app.includes('STORE_CHANCE=1/30'),'Store appearance rate is not 1/30');
|
||||
assert(app.includes('STORE_CHANCE=1/10'),'Store appearance rate is not 1/10');
|
||||
assert(app.includes('MINIMAP_VIEW_CHUNKS_X=42'),'Minimap does not use the wider scale');
|
||||
assert(app.includes('SOUND_GAIN_MULTIPLIER=3.6')&&functionSource('soundTone').includes('Math.min(.28'),'Sound effects were not amplified');
|
||||
assert(app.includes('SOUND_GAIN_MULTIPLIER=5.2')&&functionSource('soundTone').includes('Math.min(.42'),'Sound effects were not amplified');
|
||||
assert(app.includes('UNIQUE_SOLUTION_MIN_LEVEL=6')&&functionSource('placeChildAtFrontierAttempt').includes('level>=UNIQUE_SOLUTION_MIN_LEVEL'),'Unique-solution selection does not begin at level 6');
|
||||
assert(functionSource('shapeCandidatesForArea').includes('nearbyShapeFamilyCounts')&&appLogicSource.includes('generatedShapeFamilyKey')&&appLogicSource.includes('balancedShapeCandidates'),'Area-local board shape balancing is missing');
|
||||
assert(!html.includes('矢印キー:線を伸ばす')&&css.includes('.control-chips span{padding:9px 12px')&&css.includes('font-size:13px'),'Help controls are too small or still list arrow keys');
|
||||
|
|
@ -89,31 +91,32 @@ assert(functionSource('addCrossingSpecial').includes('neighbors.some(candidate=>
|
|||
assert(functionSource('updateZoomPresentation').includes('world-overview')&&functionSource('drawWorldOverview').includes('overviewCanvas')&&html.includes('id="overviewCanvas"')&&css.includes('#viewport.canvas-overview #world'),'Canvas overview mode is missing');
|
||||
assert(!functionSource('drawWorldOverview').includes('drawWorldOverviewPaths')&&functionSource('rebuildWorldOverviewCache').includes('drawMapLongLines'),'Far zoom does not use the minimap long-line renderer');
|
||||
assert(functionSource('rebuildWorldOverviewCache').includes('drawMapBoardCells')&&functionSource('rebuildWorldOverviewCache').includes('drawMapLongLines')&&!app.includes('function drawWorldOverviewShops('),'Zoomed-out rendering does not share the minimap renderer');
|
||||
assert(functionSource('makeStaticBoard').includes('renderedConnectedLineWidth(meta,index)')&&!functionSource('makeStaticBoard').includes('state.solved')&&css.includes('.static-summary-path{'),'Unsolved nearby boards do not preserve route thickness');
|
||||
assert(!app.includes('makeStaticBoard')&&!css.includes('.static-summary'),'Inactive static-board LOD code remains');
|
||||
assert(functionSource('updateTimeAttackUi').includes("classList.toggle('starting'")&&css.includes('@keyframes timeAttackStartEmphasis'),'Time-attack start clock emphasis is missing');
|
||||
const yellowFaces=app.match(/const YELLOW_FACE_CURSOR_SOURCE=`([\s\S]*?)`;/)?.[1]?.split('\n')||[];
|
||||
assert(yellowFaces.length===101&&yellowFaces.some(row=>row.startsWith('1FAE9|'))&&yellowFaces.some(row=>row.startsWith('1FAEA|')),'Complete Unicode Emoji 17.0 yellow-face cursor catalog is missing');
|
||||
assert(app.includes('MAX_FACE_CURSOR_PRICE=50000')&&app.includes('cost=MIN_CURSOR_PRICE+((index*61+37)%100)*MIN_CURSOR_PRICE')&&functionSource('storeItemPrice').includes('Math.min(MAX_FACE_CURSOR_PRICE,adjusted)'),'Yellow-face cursor prices are not deterministic random values spanning 500-50000 gems');
|
||||
const faceContracts=storeCatalog.filter(item=>item.id.startsWith('cursor-face-'));
|
||||
assert(faceContracts.length===101&&Math.min(...faceContracts.map(item=>item.cost))===500&&Math.max(...faceContracts.map(item=>item.cost))===50000&&app.includes('MAX_FACE_CURSOR_PRICE=50000')&&functionSource('storeItemPrice').includes('Math.min(MAX_FACE_CURSOR_PRICE,adjusted)'),'Canonical yellow-face cursor prices do not span 500-50000 gems');
|
||||
const flagCodes=app.match(/const FLAG_REGION_CODES=`([^`]+)`\.split\(' '\)/)?.[1]?.split(' ')||[];
|
||||
assert(flagCodes.length===259&&new Set(flagCodes).size===259&&app.includes("['gbeng','England'],['gbsct','Scotland'],['gbwls','Wales']"),'Complete Unicode Emoji 17.0 flag cursor catalog is missing');
|
||||
const oecdCodes=app.match(/const OECD_FLAG_CODES=new Set\('([^']+)'\.split\(' '\)\)/)?.[1]?.split(' ')||[];
|
||||
assert(oecdCodes.length===38&&new Set(oecdCodes).size===38&&app.includes('OECD_FLAG_CURSOR_BASE_PRICE=20000')&&app.includes('FLAG_CURSOR_BASE_PRICE=10000'),'Flag cursor base prices or the 38-country OECD tier are missing');
|
||||
const oecdCodes='AU AT BE CA CL CO CR CZ DK EE FI FR DE GR HU IS IE IL IT JP KR LV LT LU MX NL NZ NO PL PT SK SI ES SE CH TR GB US'.split(' ');
|
||||
assert(oecdCodes.length===38&&oecdCodes.every(code=>storeCatalog.find(item=>item.id===`cursor-flag-${code.toLowerCase()}`)?.cost===20000)&&storeCatalog.filter(item=>item.id.startsWith('cursor-flag-')).every(item=>item.cost===10000||item.cost===20000),'Canonical flag prices or the 38-country OECD tier are missing');
|
||||
const flagAssetDir=path.join(root,'assets','flags'),flagAssets=fs.readdirSync(flagAssetDir).filter(name=>name.endsWith('.svg'));
|
||||
assert(flagAssets.length===262&&fs.existsSync(path.join(flagAssetDir,'LICENSE-TWEMOJI.txt')),'Bundled cross-platform flag SVG catalog or attribution is incomplete');
|
||||
assert(functionSource('syncCursorAppearance').includes("image.src=selected.flagAsset")&&functionSource('setItemIcon').includes("image.src=item.flagAsset")&&functionSource('syncCursorAppearance').includes('nativeSupported')&&css.includes('cursor:none!important')&&css.includes('#customEmojiCursor.flag-cursor{width:32px;height:32px;overflow:hidden')&&css.includes('border-radius:50%'),'Native/DOM SVG-backed circular flag cursor rendering is missing');
|
||||
assert(functionSource('buildDragCache').includes('endpoint-cursor-image')&&functionSource('buildDragCache').includes('DRAG_FLAG_CLIP_RADIUS')&&functionSource('buildDragCache').includes('x:-DRAG_FLAG_CURSOR_SIZE/2')&&functionSource('buildDragCache').includes("class:'drag-tip-group'")&&functionSource('updateDragCursorDesign').includes('activeCustomCursorItem')&&functionSource('renderDragFrame').includes('cache.tipGroup.style.transform')&&functionSource('bindBoard').includes('queueMicrotask(()=>updateCustomCursorFromPointer(e))')&&!css.includes('body.is-drawing #customEmojiCursor{display:none!important}'),'Grabbed cursor sizing, centering, clipping, or smooth cursor continuity is missing');
|
||||
assert(functionSource('syncCursorAppearance').includes("image.src=selected.flagAsset")&&functionSource('setItemIcon').includes("image.src=item.flagAsset")&&functionSource('syncCursorAppearance').includes('dataset.cursorMode=presentation.mode')&&css.includes('cursor:none!important')&&css.includes('#customEmojiCursor.flag-cursor{width:32px;height:32px;overflow:hidden')&&css.includes('border-radius:50%'),'DOM SVG-backed circular flag cursor rendering is missing');
|
||||
assert(functionSource('buildDragCache').includes('endpoint-cursor-image')&&functionSource('buildDragCache').includes('DRAG_FLAG_CLIP_RADIUS')&&functionSource('buildDragCache').includes('x:-DRAG_FLAG_CURSOR_SIZE/2')&&functionSource('buildDragCache').includes("class:'drag-tip-group'")&&functionSource('updateDragCursorDesign').includes('activeCustomCursorItem')&&functionSource('renderDragFrame').includes('cache.tipGroup.style.transform')&&functionSource('bindBoard').includes('queueMicrotask(()=>updateCustomCursorFromPointer(e))')&&css.includes('body.is-drawing #customEmojiCursor{display:none!important}'),'Grabbed cursor sizing, centering, clipping, or drag-time cursor hiding is missing');
|
||||
assert(css.includes('@font-face{font-family:"DotGothic16Local"')&&css.includes('--emoji-font:')&&css.includes('body,button,input,select,textarea{font-family:var(--dot-font)}')&&!css.includes(':root{--dot-font:"DotGothic16"')&&html.includes('id="customEmojiCursor"'),'Bundled Japanese dot font is overridden or emoji-specific isolation is missing');
|
||||
assert(!html.includes('所持ジェム')&&!functionSource('completionEffect').includes('ジェム')&&!functionSource('updateScoreLensBadge').includes('予想ジェム'),'Standalone gem terminology remains in the reward UI');
|
||||
assert(functionSource('renderStorePanel').includes('storeInventoryItems(meta,store)')&&functionSource('purchaseStoreItem').includes('storeInventoryItems(meta,store).some'),'Store UI or purchase validation bypasses its seeded thirteen-item inventory');
|
||||
assert(functionSource('seededStoreItemIds').includes('.slice(0,12)')&&functionSource('seededStoreItemIds').includes('.slice(0,1)')&&functionSource('maybeOpenStore').includes('itemIds:seededStoreItemIds(meta.seed)'),'Stores do not persist twelve seeded cursors and one seeded non-cursor item');
|
||||
assert(functionSource('renderStorePanel').includes("{title:'アイテム'")&&functionSource('renderStorePanel').includes("{title:'カーソル'")&&functionSource('renderStorePanel').includes('if(category.cursor)card.append(icon,buy)')&&css.includes('.store-cursor-list{grid-template-columns:repeat(6'),'Shop is not split into item and horizontal twelve-cursor sections');
|
||||
assert(functionSource('renderInventoryPanel').includes("'inventory-cursor-grid'")&&functionSource('renderInventoryPanel').includes("option.classList.toggle('selected'")&&functionSource('useInventoryItemLoaded').includes("previousCursor===item.cursorStyle?'default':item.cursorStyle"),'Persistent click-to-toggle cursor inventory is missing');
|
||||
assert(functionSource('makeStaticBoard').includes("openStoreMeta(meta)")&&!functionSource('beginPan').includes('overviewShopAtClient'),'Distant overview still exposes a shop-only hit target instead of matching the minimap');
|
||||
assert(!app.includes('static-shop-icon')&&functionSource('beginPan').includes('nearestStoreMetaAtWorldPoint')&&!functionSource('beginPan').includes('overviewShopAtClient'),'Distant overview still exposes a shop-only hit target instead of matching the minimap');
|
||||
assert(functionSource('discardUnmovedCreatedPath').includes('path.cells.length!==1')&&functionSource('bindBoard').includes('discardUnmovedCreatedPath(b)'),'Cancelled pickup creation can leave an orphan handle');
|
||||
assert(functionSource('renderDragFrame').includes('refreshDragNumberColors(b)'),'Number colors do not update in the live pickup renderer');
|
||||
assert(functionSource('detachPathFromStartGate').includes('path.detachedStart=true')&&functionSource('renderBoardNow').includes("'data-endpoint-side':'start'")&&!app.includes('whitePickupEnd')&&!functionSource('finalizeAtGate').includes("'#fff'"),'Two-ended colored pickup support is incomplete');
|
||||
assert(!functionSource('openTipMergePlan').includes('a.detachedStart||o.detachedStart')&&functionSource('openTipMergePlan').includes('tipDistance!==0')&&!app.includes('function gateConnectionSteps'),'Same-cell pickup joining or exact-cell gate snapping is incomplete');
|
||||
assert(functionSource('updateScoreLensBadge').includes('projectedScoreForBoard')&&functionSource('scoreLensVisibleForMeta').includes('SCORE_LENS_ZOOM_THRESHOLD')&&functionSource('useInventoryItemLoaded').includes('data.scoreLensEnabled=!previous')&&/id:'score-lens'[^\n]+scoreLens:true[^\n]+icon:/.test(app)&&!app.includes('SCORE_LENS_RADIUS')&&css.includes('.score-lens-badge'),'Persistent ON/OFF score lens display is missing or still asks for a position');
|
||||
assert(functionSource('updateScoreLensBadge').includes('projectedScoreForBoard')&&functionSource('scoreLensVisibleForMeta').includes('SCORE_LENS_ZOOM_THRESHOLD')&&functionSource('useInventoryItemLoaded').includes('data.scoreLensEnabled=!previous')&&storeCatalog.find(item=>item.id==='score-lens')?.scoreLens===true&&/id:'score-lens'[^\n]+icon:/.test(app)&&!app.includes('SCORE_LENS_RADIUS')&&css.includes('.score-lens-badge'),'Persistent ON/OFF score lens display is missing or still asks for a position');
|
||||
assert(functionSource('renderStorePanel').includes('formatScore(price)')&&!functionSource('renderStorePanel').includes('price-data.score')&&!functionSource('purchaseStoreItem').includes('price-data.score'),'Store buttons do not always show the actual item price');
|
||||
assert(functionSource('zoomAt').includes('MIN_CAMERA_SCALE'),'Camera cannot zoom out to overview scale');
|
||||
assert(functionSource('addObstaclePattern').includes('puzzle.difficulty=sourcePuzzle.difficulty'),'Obstacle generation changes the displayed level and section constraint');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue