This commit is contained in:
33333-33333 2026-07-31 12:54:46 +09:00
commit c3a6f5ff37
80 changed files with 2512 additions and 1625 deletions

17
client/ui/cursor.css Normal file
View file

@ -0,0 +1,17 @@
body[data-cursor-mode="dom"],
body[data-cursor-mode="dom"] *{cursor:none!important}
.emoji-glyph,#customEmojiCursor{font-family:var(--emoji-font)!important;font-variant-emoji:emoji}
.item-flag-image{display:block;width:34px;height:26px;object-fit:contain;pointer-events:none}
#customEmojiCursor{position:fixed;z-index:300;left:0;top:0;display:none;width:32px;height:32px;place-items:center;overflow:visible;border:0;border-radius:0;background:transparent;font-size:26px;line-height:1;opacity:1;pointer-events:none;will-change:transform}
#customEmojiCursor.visible{display:grid}
#customEmojiCursor.flag-cursor{width:32px;height:32px;overflow:hidden;border:2px solid #101214;border-radius:50%;background:#f4d45f;font-size:26px;opacity:1;box-shadow:0 2px 7px rgba(0,0,0,.5)}
#customEmojiCursor.flag-cursor img{position:absolute;left:50%;top:50%;display:block;width:140%;height:110%;margin:0;transform:translate(-50%,-50%);border-radius:0;object-fit:cover;object-position:center;clip-path:none}
body.is-drawing #viewport,
body.is-drawing #viewport *{cursor:none!important}
body.is-drawing #customEmojiCursor{display:none!important}
#pickupHandleOverlay{position:fixed;z-index:299;left:0;top:0;display:none;width:15px;height:15px;border:2px solid #111820;border-radius:50%;background:var(--pickup-color,#f4d45f);box-shadow:0 0 0 2px rgba(255,255,255,.82),0 2px 7px rgba(0,0,0,.48);pointer-events:none;will-change:transform;contain:strict}
#pickupHandleOverlay.visible{display:block}
#pickupHandleOverlay.custom-cursor{width:32px;height:32px;place-items:center;border:0;border-radius:0;background:transparent;box-shadow:none;font:26px/1 var(--emoji-font);overflow:visible}
#pickupHandleOverlay.custom-cursor.visible{display:grid}
#pickupHandleOverlay.custom-cursor.flag-cursor{overflow:hidden;border:2px solid #101214;border-radius:50%;background:var(--pickup-color,#f4d45f);box-shadow:0 2px 7px rgba(0,0,0,.5)}
#pickupHandleOverlay.custom-cursor.flag-cursor img{position:absolute;left:50%;top:50%;display:block;width:140%;height:110%;transform:translate(-50%,-50%);object-fit:cover;object-position:center;pointer-events:none}

30
client/ui/cursor.js Normal file
View file

@ -0,0 +1,30 @@
(function(root,factory){
const api=factory();
if(typeof module==='object'&&module.exports)module.exports=api;
root.BendCursorModel=api;
})(typeof globalThis!=='undefined'?globalThis:this,function(){
'use strict';
function createCursorModel(items=[]){
const byStyle=new Map();
for(const item of items)if(item?.cursorStyle)byStyle.set(item.cursorStyle,Object.freeze({...item}));
const item=style=>byStyle.get(String(style||''))||null;
const presentation=style=>{
const selected=item(style);
if(!selected)return Object.freeze({style:'default',mode:'default',asset:null,glyph:'',flag:false,hotspot:Object.freeze([0,0]),pickup:Object.freeze({kind:'default'})});
const flag=Boolean(selected.flagAsset),asset=selected.flagAsset||null,glyph=flag?'':selected.cursorEmoji||'';
return Object.freeze({
style:selected.cursorStyle,
mode:'dom',
asset,
glyph,
flag,
hotspot:Object.freeze([.5,.5]),
pickup:Object.freeze({kind:flag?'flag':'glyph',asset,glyph})
});
};
return Object.freeze({item,presentation,styles:()=>Object.freeze([...byStyle.keys()])});
}
return Object.freeze({createCursorModel});
});