feat(ui): use LCE textinput

This commit is contained in:
neoapps-dev
2026-09-18 14:27:00 +03:00
parent 31639366d4
commit a243b8362c
21 changed files with 278 additions and 175 deletions
+19 -15
View File
@@ -433,7 +433,9 @@ const SkinViewer = memo(function SkinViewer({
renderer.domElement.addEventListener("mousedown", onMouseDown); renderer.domElement.addEventListener("mousedown", onMouseDown);
window.addEventListener("mousemove", onMouseMove); window.addEventListener("mousemove", onMouseMove);
window.addEventListener("mouseup", onMouseUp); window.addEventListener("mouseup", onMouseUp);
renderer.domElement.addEventListener("touchstart", onTouchStart, { passive: true }); renderer.domElement.addEventListener("touchstart", onTouchStart, {
passive: true,
});
window.addEventListener("touchmove", onTouchMove, { passive: false }); window.addEventListener("touchmove", onTouchMove, { passive: false });
window.addEventListener("touchend", onTouchEnd); window.addEventListener("touchend", onTouchEnd);
return () => { return () => {
@@ -692,20 +694,22 @@ const SkinViewer = memo(function SkinViewer({
data-focus="0" data-focus="0"
tabIndex={0} tabIndex={0}
> >
<input <div>
type="text" <input
value={username} type="text"
maxLength={16} value={username}
style={{ width: `${Math.max(username.length, 3) + 2}ch` }} maxLength={16}
onChange={(e) => setUsername(e.target.value)} style={{ width: `${Math.max(username.length, 3) + 2}ch` }}
onKeyDown={(e) => { onChange={(e) => setUsername(e.target.value)}
if (e.key === "Enter") { onKeyDown={(e) => {
e.currentTarget.blur(); if (e.key === "Enter") {
e.stopPropagation(); e.currentTarget.blur();
} e.stopPropagation();
}} }
className="bg-transparent text-white focus:text-[#FFFF55] outline-none border-none text-center font-[var(--font-base)] mc-text-shadow tracking-widest text-xl cursor-text" }}
/> className="text-white focus:text-[#FFFF55] outline-none text-center font-[var(--font-base)] tracking-widest text-xl cursor-text"
/>
</div>
</div> </div>
)} )}
{!legacyMode && ( {!legacyMode && (
+12 -4
View File
@@ -144,6 +144,7 @@ export default function CustomTUModal({
<label className="text-gray text-sm mc-text-shadow uppercase tracking-widest"> <label className="text-gray text-sm mc-text-shadow uppercase tracking-widest">
{t("modals.customTu.tuName")} {t("modals.customTu.tuName")}
</label> </label>
<div className="mc-textinput-outer">
<input <input
type="text" type="text"
autoFocus autoFocus
@@ -151,39 +152,44 @@ export default function CustomTUModal({
onChange={(e) => setName(e.target.value)} onChange={(e) => setName(e.target.value)}
onFocus={() => setFocusIndex(0)} onFocus={() => setFocusIndex(0)}
placeholder={t("modals.customTu.namePlaceholder")} placeholder={t("modals.customTu.namePlaceholder")}
className="w-full h-10 px-3 bg-black/40 border-2 border-[#373737] text-white text-base outline-none font-[var(--font-base)]" className="mc-textinput w-full h-10 px-3 text-white text-base outline-none font-[var(--font-base)]"
style={{ imageRendering: "pixelated" }} style={{ imageRendering: "pixelated" }}
/> />
</div>
</div> </div>
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<label className="text-gray text-sm mc-text-shadow uppercase tracking-widest"> <label className="text-gray text-sm mc-text-shadow uppercase tracking-widest">
{t("modals.customTu.description")} {t("modals.customTu.description")}
</label> </label>
<div className="mc-textinput-outer">
<input <input
type="text" type="text"
value={desc} value={desc}
onChange={(e) => setDesc(e.target.value)} onChange={(e) => setDesc(e.target.value)}
onFocus={() => setFocusIndex(1)} onFocus={() => setFocusIndex(1)}
placeholder={t("modals.customTu.descPlaceholder")} placeholder={t("modals.customTu.descPlaceholder")}
className="w-full h-10 px-3 bg-black/40 border-2 border-[#373737] text-white text-base outline-none font-[var(--font-base)]" className="mc-textinput w-full h-10 px-3 text-white text-base outline-none font-[var(--font-base)]"
style={{ imageRendering: "pixelated" }} style={{ imageRendering: "pixelated" }}
/> />
</div>
</div> </div>
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<label className="text-gray text-sm mc-text-shadow uppercase tracking-widest"> <label className="text-gray text-sm mc-text-shadow uppercase tracking-widest">
{t("modals.customTu.downloadUrl")} {t("modals.customTu.downloadUrl")}
</label> </label>
<div className="mc-textinput-outer">
<input <input
type="text" type="text"
value={url} value={url}
onChange={(e) => setUrl(e.target.value)} onChange={(e) => setUrl(e.target.value)}
onFocus={() => setFocusIndex(2)} onFocus={() => setFocusIndex(2)}
placeholder={t("modals.customTu.urlPlaceholder")} placeholder={t("modals.customTu.urlPlaceholder")}
className="w-full h-10 px-3 bg-black/40 border-2 border-[#373737] text-white text-base outline-none font-[var(--font-base)]" className="mc-textinput w-full h-10 px-3 text-white text-base outline-none font-[var(--font-base)]"
style={{ imageRendering: "pixelated" }} style={{ imageRendering: "pixelated" }}
/> />
</div>
</div> </div>
{path && ( {path && (
@@ -191,13 +197,15 @@ export default function CustomTUModal({
<label className="text-gray text-sm mc-text-shadow uppercase tracking-widest"> <label className="text-gray text-sm mc-text-shadow uppercase tracking-widest">
{t("modals.customTu.localPath")} {t("modals.customTu.localPath")}
</label> </label>
<div className="mc-textinput-outer">
<input <input
type="text" type="text"
readOnly readOnly
value={path} value={path}
className="w-full h-10 px-3 bg-black/20 border-2 border-[#222] text-black text-xs outline-none font-[var(--font-base)] cursor-not-allowed" className="mc-textinput w-full h-10 px-3 text-black text-xs outline-none font-[var(--font-base)] cursor-not-allowed"
style={{ imageRendering: "pixelated" }} style={{ imageRendering: "pixelated" }}
/> />
</div>
</div> </div>
)} )}
+6 -2
View File
@@ -255,6 +255,7 @@ export default function OptionsModal({
case "int": case "int":
case "number": case "number":
return ( return (
<div className="mc-textinput-outer w-24">
<input <input
ref={(el) => { ref={(el) => {
inputRefs.current[index] = el; inputRefs.current[index] = el;
@@ -273,14 +274,16 @@ export default function OptionsModal({
})); }));
}} }}
onFocus={() => setFocusIndex(index)} onFocus={() => setFocusIndex(index)}
className={`w-24 h-8 bg-black/40 border-2 border-[#373737] text-white text-sm px-2 outline-none text-center font-[var(--font-base)] focus:border-[#FFFF55] [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${ className={`mc-textinput w-full h-10 px-3 text-white text-sm outline-none text-center font-[var(--font-base)] [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${
disabled ? "opacity-40 cursor-not-allowed" : "" disabled ? "opacity-40 cursor-not-allowed" : ""
}`} }`}
style={{ imageRendering: "pixelated" }} style={{ imageRendering: "pixelated" }}
/> />
</div>
); );
case "string": case "string":
return ( return (
<div className="mc-textinput-outer w-44">
<input <input
ref={(el) => { ref={(el) => {
inputRefs.current[index] = el; inputRefs.current[index] = el;
@@ -293,11 +296,12 @@ export default function OptionsModal({
setValues((prev) => ({ ...prev, [option.id]: e.target.value })); setValues((prev) => ({ ...prev, [option.id]: e.target.value }));
}} }}
onFocus={() => setFocusIndex(index)} onFocus={() => setFocusIndex(index)}
className={`w-44 h-8 bg-black/40 border-2 border-[#373737] text-white text-sm px-2 outline-none font-[var(--font-base)] focus:border-[#FFFF55] ${ className={`mc-textinput w-full h-10 px-3 text-white text-sm outline-none font-[var(--font-base)] ${
disabled ? "opacity-40 cursor-not-allowed" : "" disabled ? "opacity-40 cursor-not-allowed" : ""
}`} }`}
style={{ imageRendering: "pixelated" }} style={{ imageRendering: "pixelated" }}
/> />
</div>
); );
case "choice": case "choice":
return ( return (
+3 -1
View File
@@ -183,6 +183,7 @@ export default function SetUidModal({
<label className="text-gray text-xs mc-text-shadow uppercase tracking-widest text-[#AAAAAA]"> <label className="text-gray text-xs mc-text-shadow uppercase tracking-widest text-[#AAAAAA]">
{t("modals.setUid.enterUid")} {t("modals.setUid.enterUid")}
</label> </label>
<div className="mc-textinput-outer w-full">
<input <input
type="text" type="text"
autoFocus autoFocus
@@ -190,9 +191,10 @@ export default function SetUidModal({
onChange={(e) => setUid(e.target.value)} onChange={(e) => setUid(e.target.value)}
onFocus={() => setFocusIndex(2)} onFocus={() => setFocusIndex(2)}
placeholder="0xFF02F0C87E8AC1F2" placeholder="0xFF02F0C87E8AC1F2"
className={`w-full h-10 px-3 bg-black/40 border-2 ${focusIndex === 2 ? 'border-white' : 'border-[#373737]'} text-white text-base outline-none font-[var(--font-base)] text-center`} className="mc-textinput w-full h-10 px-3 text-white text-base outline-none font-[var(--font-base)] text-center"
style={{ imageRendering: "pixelated", filter: focusIndex === 2 ? 'brightness(1.2)' : 'none' }} style={{ imageRendering: "pixelated", filter: focusIndex === 2 ? 'brightness(1.2)' : 'none' }}
/> />
</div>
</div> </div>
) : ( ) : (
<div className="flex flex-col gap-1 items-center w-full relative"> <div className="flex flex-col gap-1 items-center w-full relative">
+12 -4
View File
@@ -387,13 +387,15 @@ export const ArcEditorView: React.FC = () => {
<div className="flex h-full overflow-hidden"> <div className="flex h-full overflow-hidden">
<div className="w-2/3 flex flex-col p-4 border-r-2 border-black/20"> <div className="w-2/3 flex flex-col p-4 border-r-2 border-black/20">
<div className="mb-4 flex gap-4"> <div className="mb-4 flex gap-4">
<div className="mc-textinput-outer flex-1">
<input <input
type="text" type="text"
placeholder={t("arcEditor.searchEntries")} placeholder={t("arcEditor.searchEntries")}
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
className="flex-1 bg-black/40 border-2 border-[#373737] text-white px-4 py-2 outline-none focus:border-[#FFFF55] transition-colors" className="mc-textinput w-full h-10 px-3 text-white text-base outline-none font-[var(--font-base)]"
/> />
</div>
<button <button
onClick={() => setIsAddModalOpen(true)} onClick={() => setIsAddModalOpen(true)}
className="px-6 py-2 text-white mc-text-shadow text-sm" className="px-6 py-2 text-white mc-text-shadow text-sm"
@@ -506,13 +508,15 @@ export const ArcEditorView: React.FC = () => {
<option key={idx} value={idx}>{lang.id} {lang.isStatic ? `[${t("arcEditor.static")}]` : `[${t("arcEditor.keyed")}]`}</option> <option key={idx} value={idx}>{lang.id} {lang.isStatic ? `[${t("arcEditor.static")}]` : `[${t("arcEditor.keyed")}]`}</option>
))} ))}
</select> </select>
<div className="mc-textinput-outer flex-1">
<input <input
type="text" type="text"
placeholder={t("arcEditor.searchStrings")} placeholder={t("arcEditor.searchStrings")}
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
className="flex-1 bg-black/40 border-2 border-[#373737] text-white px-4 py-2 outline-none focus:border-[#FFFF55] transition-colors" className="mc-textinput w-full h-10 px-3 text-white text-base outline-none font-[var(--font-base)]"
/> />
</div>
<button <button
onClick={() => setIsLocEditModalOpen({ langIdx: selectedLocLangIdx, strIdx: -1, isNew: true })} onClick={() => setIsLocEditModalOpen({ langIdx: selectedLocLangIdx, strIdx: -1, isNew: true })}
className="px-6 py-2 text-white mc-text-shadow text-sm" className="px-6 py-2 text-white mc-text-shadow text-sm"
@@ -665,12 +669,14 @@ function RenameModal({ initialName, initialCompressed, onClose, onConfirm }: { i
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
<div> <div>
<label className="text-white/40 text-xs uppercase mb-2 block">{t("arcEditor.newArchivePath")}</label> <label className="text-white/40 text-xs uppercase mb-2 block">{t("arcEditor.newArchivePath")}</label>
<div className="mc-textinput-outer">
<input <input
type="text" type="text"
value={name} value={name}
onChange={(e) => setName(e.target.value)} onChange={(e) => setName(e.target.value)}
className="w-full bg-black/40 border-2 border-[#373737] text-white px-4 py-3 outline-none focus:border-[#FFFF55] transition-colors" className="mc-textinput w-full h-10 px-3 text-white text-base outline-none font-[var(--font-base)]"
/> />
</div>
</div> </div>
<label className="flex items-center gap-3 cursor-pointer group"> <label className="flex items-center gap-3 cursor-pointer group">
<input type="checkbox" checked={comp} onChange={(e) => setComp(e.target.checked)} className="w-5 h-5 accent-[#FFFF55]" /> <input type="checkbox" checked={comp} onChange={(e) => setComp(e.target.checked)} className="w-5 h-5 accent-[#FFFF55]" />
@@ -704,13 +710,15 @@ function LocEditModal({ data, lang, onClose, onConfirm }: { data: { langIdx: num
{!lang.isStatic ? ( {!lang.isStatic ? (
<div> <div>
<label className="text-white/40 text-xs uppercase mb-2 block">{t("arcEditor.stringKey")}</label> <label className="text-white/40 text-xs uppercase mb-2 block">{t("arcEditor.stringKey")}</label>
<div className="mc-textinput-outer">
<input <input
type="text" type="text"
value={key} value={key}
onChange={(e) => setKey(e.target.value)} onChange={(e) => setKey(e.target.value)}
className="w-full bg-black/40 border-2 border-[#373737] text-white px-4 py-3 outline-none focus:border-[#FFFF55] transition-colors font-mono" className="mc-textinput w-full h-10 px-3 text-white text-base outline-none font-[var(--font-base)] font-mono"
/> />
</div> </div>
</div>
) : ( ) : (
<div className="text-white/40 italic mb-2">{t("arcEditor.staticEntry", { index: data.isNew ? lang.strings.length : data.strIdx })}</div> <div className="text-white/40 italic mb-2">{t("arcEditor.staticEntry", { index: data.isNew ? lang.strings.length : data.strIdx })}</div>
)} )}
+33 -19
View File
@@ -183,13 +183,15 @@ export default function ColEditorView() {
<div className="flex-1 flex flex-col p-4 overflow-hidden"> <div className="flex-1 flex flex-col p-4 overflow-hidden">
<div className="mb-4 flex gap-4"> <div className="mb-4 flex gap-4">
<div className="mc-textinput-outer flex-1">
<input <input
type="text" type="text"
placeholder={t("colEditor.searchColors")} placeholder={t("colEditor.searchColors")}
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
className="flex-1 bg-black/40 border-2 border-[#373737] text-white px-4 py-2 outline-none focus:border-[#FFFF55] transition-colors" className="mc-textinput w-full h-10 px-3 text-white text-base outline-none font-[var(--font-base)]"
/> />
</div>
<button <button
onClick={activeTab === "colors" ? handleAddColor : handleAddWorldColor} onClick={activeTab === "colors" ? handleAddColor : handleAddWorldColor}
className="px-6 py-2 text-white mc-text-shadow text-sm" className="px-6 py-2 text-white mc-text-shadow text-sm"
@@ -219,12 +221,14 @@ export default function ColEditorView() {
{activeTab === "colors" && currentColors.map((c) => ( {activeTab === "colors" && currentColors.map((c) => (
<tr key={c.originalIdx} className="border-b border-[#373737]/30 group"> <tr key={c.originalIdx} className="border-b border-[#373737]/30 group">
<td className="p-2"> <td className="p-2">
<div className="mc-textinput-outer">
<input <input
type="text" type="text"
value={c.name} value={c.name}
onChange={(e) => handleUpdateColor(c.originalIdx, "name", e.target.value)} onChange={(e) => handleUpdateColor(c.originalIdx, "name", e.target.value)}
className="w-full bg-black/40 border border-[#373737] px-2 py-1 outline-none focus:border-[#FFFF55] text-white text-sm" className="mc-textinput w-full h-10 px-3 text-white text-sm outline-none font-[var(--font-base)]"
/> />
</div>
</td> </td>
<td className="p-2"> <td className="p-2">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
@@ -239,19 +243,21 @@ export default function ColEditorView() {
}} }}
className="w-8 h-8 p-0 cursor-pointer shrink-0 border border-[#373737] rounded-sm bg-transparent" className="w-8 h-8 p-0 cursor-pointer shrink-0 border border-[#373737] rounded-sm bg-transparent"
/> />
<input <div className="mc-textinput-outer w-24">
type="text" <input
value={argbToHex(c.color)} type="text"
onChange={(e) => { value={argbToHex(c.color)}
const s = e.target.value.replace(/[^0-9A-Fa-f]/g, ''); onChange={(e) => {
if (s.length <= 8) { const s = e.target.value.replace(/[^0-9A-Fa-f]/g, '');
const parsed = parseInt(s, 16); if (s.length <= 8) {
if (!isNaN(parsed)) handleUpdateColor(c.originalIdx, "color", parsed >>> 0); const parsed = parseInt(s, 16);
} if (!isNaN(parsed)) handleUpdateColor(c.originalIdx, "color", parsed >>> 0);
}} }
className="bg-black/40 border border-[#373737] w-24 px-2 py-1 outline-none focus:border-[#FFFF55] text-white font-mono text-sm uppercase" }}
maxLength={8} className="mc-textinput w-full h-10 px-3 text-white font-mono text-sm uppercase outline-none font-[var(--font-base)]"
/> maxLength={8}
/>
</div>
</div> </div>
</td> </td>
<td className="p-2 text-right"> <td className="p-2 text-right">
@@ -265,12 +271,14 @@ export default function ColEditorView() {
{activeTab === "worldColors" && currentWorldColors.map((w) => ( {activeTab === "worldColors" && currentWorldColors.map((w) => (
<tr key={w.originalIdx} className="border-b border-[#373737]/30 group"> <tr key={w.originalIdx} className="border-b border-[#373737]/30 group">
<td className="p-2"> <td className="p-2">
<div className="mc-textinput-outer">
<input <input
type="text" type="text"
value={w.name} value={w.name}
onChange={(e) => handleUpdateWorldColor(w.originalIdx, "name", e.target.value)} onChange={(e) => handleUpdateWorldColor(w.originalIdx, "name", e.target.value)}
className="w-full bg-black/40 border border-[#373737] px-2 py-1 outline-none focus:border-[#FFFF55] text-white text-sm" className="mc-textinput w-full h-10 px-3 text-white text-sm outline-none font-[var(--font-base)]"
/> />
</div>
</td> </td>
<td className="p-2"> <td className="p-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@@ -284,6 +292,7 @@ export default function ColEditorView() {
}} }}
className="w-6 h-6 p-0 cursor-pointer shrink-0 border border-[#373737] rounded-sm bg-transparent" className="w-6 h-6 p-0 cursor-pointer shrink-0 border border-[#373737] rounded-sm bg-transparent"
/> />
<div className="mc-textinput-outer w-20">
<input <input
type="text" type="text"
value={argbToHex(w.waterColor)} value={argbToHex(w.waterColor)}
@@ -294,9 +303,10 @@ export default function ColEditorView() {
if (!isNaN(parsed)) handleUpdateWorldColor(w.originalIdx, "waterColor", parsed >>> 0); if (!isNaN(parsed)) handleUpdateWorldColor(w.originalIdx, "waterColor", parsed >>> 0);
} }
}} }}
className="bg-black/40 border border-[#373737] w-20 px-2 py-1 outline-none focus:border-[#FFFF55] text-white font-mono text-xs uppercase" className="mc-textinput w-full h-10 px-3 text-white font-mono text-xs uppercase outline-none font-[var(--font-base)]"
maxLength={8} maxLength={8}
/> />
</div>
</div> </div>
</td> </td>
<td className="p-2"> <td className="p-2">
@@ -311,6 +321,7 @@ export default function ColEditorView() {
}} }}
className="w-6 h-6 p-0 cursor-pointer shrink-0 border border-[#373737] rounded-sm bg-transparent" className="w-6 h-6 p-0 cursor-pointer shrink-0 border border-[#373737] rounded-sm bg-transparent"
/> />
<div className="mc-textinput-outer w-20">
<input <input
type="text" type="text"
value={argbToHex(w.underwaterColor)} value={argbToHex(w.underwaterColor)}
@@ -321,9 +332,10 @@ export default function ColEditorView() {
if (!isNaN(parsed)) handleUpdateWorldColor(w.originalIdx, "underwaterColor", parsed >>> 0); if (!isNaN(parsed)) handleUpdateWorldColor(w.originalIdx, "underwaterColor", parsed >>> 0);
} }
}} }}
className="bg-black/40 border border-[#373737] w-20 px-2 py-1 outline-none focus:border-[#FFFF55] text-white font-mono text-xs uppercase" className="mc-textinput w-full h-10 px-3 text-white font-mono text-xs uppercase outline-none font-[var(--font-base)]"
maxLength={8} maxLength={8}
/> />
</div>
</div> </div>
</td> </td>
<td className="p-2"> <td className="p-2">
@@ -338,6 +350,7 @@ export default function ColEditorView() {
}} }}
className="w-6 h-6 p-0 cursor-pointer shrink-0 border border-[#373737] rounded-sm bg-transparent" className="w-6 h-6 p-0 cursor-pointer shrink-0 border border-[#373737] rounded-sm bg-transparent"
/> />
<div className="mc-textinput-outer w-20">
<input <input
type="text" type="text"
value={argbToHex(w.fogColor)} value={argbToHex(w.fogColor)}
@@ -348,9 +361,10 @@ export default function ColEditorView() {
if (!isNaN(parsed)) handleUpdateWorldColor(w.originalIdx, "fogColor", parsed >>> 0); if (!isNaN(parsed)) handleUpdateWorldColor(w.originalIdx, "fogColor", parsed >>> 0);
} }
}} }}
className="bg-black/40 border border-[#373737] w-20 px-2 py-1 outline-none focus:border-[#FFFF55] text-white font-mono text-xs uppercase" className="mc-textinput w-full h-10 px-3 text-white font-mono text-xs uppercase outline-none font-[var(--font-base)]"
maxLength={8} maxLength={8}
/> />
</div>
</div> </div>
</td> </td>
<td className="p-2 text-right"> <td className="p-2 text-right">
+13 -13
View File
@@ -354,7 +354,7 @@ const GoldMapperView = memo(function GoldMapperView() {
? handleToggleEnabled ? handleToggleEnabled
: () => openBind(row.id) : () => openBind(row.id)
} }
className={`w-full h-10 flex items-center pl-6 pr-4 outline-none border-none shrink-0 transition-colors ${ className={`w-full h-10 flex items-center ${!isEnable ? "pl-6" : "pl-2"} pr-4 outline-none border-none shrink-0 transition-colors ${
focused focused
? "text-[#ffff00]" ? "text-[#ffff00]"
: isEnable : isEnable
@@ -386,7 +386,7 @@ const GoldMapperView = memo(function GoldMapperView() {
</div> </div>
)} )}
<span <span
className={`tracking-widest text-lg mc-text-shadow truncate ${ className={`tracking-widest text-lg truncate ${
isReset ? "" : "flex-1 text-left" isReset ? "" : "flex-1 text-left"
}`} }`}
> >
@@ -425,9 +425,9 @@ const GoldMapperView = memo(function GoldMapperView() {
<button <button
onMouseEnter={() => setFocusIndex(null)} onMouseEnter={() => setFocusIndex(null)}
onClick={handleBack} onClick={handleBack}
className="w-40 h-10 flex items-center justify-center transition-colors text-xl mc-text-shadow outline-none border-none hover:text-[#ffff00] mt-4 text-white" className="w-40 h-10 flex items-center justify-center transition-colors text-xl outline-none border-none hover:text-[#ffff00] mt-4 text-white"
style={{ style={{
backgroundImage: "url('/images/Layout_Button_Bmp.png')", backgroundImage: "url('/images/Button_Background.png')",
backgroundSize: "100% 100%", backgroundSize: "100% 100%",
imageRendering: "pixelated", imageRendering: "pixelated",
}} }}
@@ -443,13 +443,13 @@ const GoldMapperView = memo(function GoldMapperView() {
}} }}
> >
<div className="relative w-[620px] max-w-[95vw] h-[580px] max-h-[88vh] p-5 flex flex-col font-[var(--font-base)] mc-options-bg"> <div className="relative w-[620px] max-w-[95vw] h-[580px] max-h-[88vh] p-5 flex flex-col font-[var(--font-base)] mc-options-bg">
<h2 className="text-xl text-black mc-text-shadow mb-4 text-center"> <h2 className="text-xl text-black mb-4 text-center">
{t("goldMapper.assign", { name: displayName(editing) })} {t("goldMapper.assign", { name: displayName(editing) })}
</h2> </h2>
<div className="w-full flex-1 min-h-0 overflow-y-auto custom-scrollbar mb-4"> <div className="w-full flex-1 min-h-0 overflow-y-auto custom-scrollbar mb-4">
<div className="mb-3"> <div className="mb-3">
<h3 className="text-[#333333] mc-text-shadow uppercase tracking-widest text-sm px-3 pt-2 pb-1"> <h3 className="text-[#333333] tracking-widest text-sm px-3 pt-2 pb-1">
{t("goldMapper.mouse")} {t("goldMapper.mouse")}
</h3> </h3>
<div className="grid grid-cols-4 gap-2 p-1 content-start"> <div className="grid grid-cols-4 gap-2 p-1 content-start">
@@ -472,7 +472,7 @@ const GoldMapperView = memo(function GoldMapperView() {
</div> </div>
<div className="mb-3"> <div className="mb-3">
<h3 className="text-[#333333] mc-text-shadow uppercase tracking-widest text-sm px-3 pt-2 pb-1"> <h3 className="text-[#333333] tracking-widest text-sm px-3 pt-2 pb-1">
{t("goldMapper.controller")} {t("goldMapper.controller")}
</h3> </h3>
<div className="grid grid-cols-4 gap-2 p-1 content-start"> <div className="grid grid-cols-4 gap-2 p-1 content-start">
@@ -500,9 +500,10 @@ const GoldMapperView = memo(function GoldMapperView() {
</div> </div>
<div className="mb-3"> <div className="mb-3">
<h3 className="text-[#333333] mc-text-shadow uppercase tracking-widest text-sm px-3 pt-2 pb-1"> <h3 className="text-[#333333] tracking-widest text-sm px-3 pt-2 pb-1">
{t("goldMapper.keyboard")} {t("goldMapper.keyboard")}
</h3> </h3>
<div className="mc-textinput-outer">
<input <input
data-modal-index={MOUSE_IDS.length + controllerIds.length} data-modal-index={MOUSE_IDS.length + controllerIds.length}
value={keyInput} value={keyInput}
@@ -520,13 +521,12 @@ const GoldMapperView = memo(function GoldMapperView() {
if (e.key === "Enter") submitKeyInput(); if (e.key === "Enter") submitKeyInput();
}} }}
placeholder={t("goldMapper.typeKeyName")} placeholder={t("goldMapper.typeKeyName")}
className={`w-full h-10 px-3 bg-black/40 border-2 text-white text-base outline-none text-center ${ className={`mc-textinput w-full h-10 px-3 text-white text-base outline-none text-center font-[var(--font-base)] ${
keyInputError keyInputError ? "" : ""
? "border-red-600"
: "border-[#373737] focus:border-[#FFFF55]"
}`} }`}
style={{ imageRendering: "pixelated" }} style={{ imageRendering: "pixelated" }}
/> />
</div>
{keyInputError && ( {keyInputError && (
<p className="text-red-600 text-xs mt-1 px-3"> <p className="text-red-600 text-xs mt-1 px-3">
{keyInputError} {keyInputError}
@@ -544,7 +544,7 @@ const GoldMapperView = memo(function GoldMapperView() {
setModalFocusIndex(MOUSE_IDS.length + controllerIds.length + 1) setModalFocusIndex(MOUSE_IDS.length + controllerIds.length + 1)
} }
onClick={closeModal} onClick={closeModal}
className={`w-full h-12 flex items-center justify-center text-xl mc-text-shadow transition-colors outline-none border-none cursor-pointer ${ className={`w-full h-12 flex items-center justify-center text-xl transition-colors outline-none border-none cursor-pointer ${
modalFocusIndex === MOUSE_IDS.length + controllerIds.length + 1 modalFocusIndex === MOUSE_IDS.length + controllerIds.length + 1
? "text-[#ffff00]" ? "text-[#ffff00]"
: "text-white" : "text-white"
+3 -1
View File
@@ -291,12 +291,14 @@ function GrfNodeView({ node, level, path, onUpdate }: { node: GrfNode, level: nu
{node.parameters.map((p, i) => ( {node.parameters.map((p, i) => (
<div key={i} className="flex gap-4 border-b border-[#373737]/30 py-2 text-sm items-center"> <div key={i} className="flex gap-4 border-b border-[#373737]/30 py-2 text-sm items-center">
<span className="text-[#AAAAAA] w-1/3 truncate">{p.name}</span> <span className="text-[#AAAAAA] w-1/3 truncate">{p.name}</span>
<div className="mc-textinput-outer flex-1">
<input <input
type="text" type="text"
value={p.value} value={p.value}
onChange={(e) => onUpdate(currentPath, i, e.target.value)} onChange={(e) => onUpdate(currentPath, i, e.target.value)}
className="flex-1 bg-white/5 border border-white/10 px-2 py-1 text-white outline-none focus:border-[#FFFF55]/50 font-mono transition-colors" className="mc-textinput w-full h-10 px-3 text-white text-base outline-none font-[var(--font-base)] font-mono"
/> />
</div>
</div> </div>
))} ))}
</div> </div>
+3 -1
View File
@@ -821,14 +821,16 @@ const LceOnlineView = memo(function LceOnlineView({
<h2 className="text-[#FFFF55] text-3xl mc-text-shadow mb-6 border-b-2 border-[#373737] pb-2 w-full text-center uppercase tracking-widest"> <h2 className="text-[#FFFF55] text-3xl mc-text-shadow mb-6 border-b-2 border-[#373737] pb-2 w-full text-center uppercase tracking-widest">
{t("lceOnline.addFriend")} {t("lceOnline.addFriend")}
</h2> </h2>
<div className="mc-textinput-outer w-full mb-6">
<input <input
ref={addFriendInputRef} ref={addFriendInputRef}
type="text" type="text"
className="bg-black/20 border-4 border-[#555] text-white p-4 w-full text-2xl font-bold outline-none focus:border-[#FFFF55] transition-colors placeholder:text-[#888] mb-6 mc-text-shadow" className="mc-textinput w-full h-10 px-3 text-white text-base outline-none font-[var(--font-base)] text-2xl font-bold placeholder:text-[#888] mc-text-shadow"
placeholder={t("lceOnline.username")} placeholder={t("lceOnline.username")}
value={addFriendUsername} value={addFriendUsername}
onChange={(e) => setAddFriendUsername(e.target.value)} onChange={(e) => setAddFriendUsername(e.target.value)}
/> />
</div>
<div className="flex gap-4 w-full"> <div className="flex gap-4 w-full">
<button <button
className="h-12 flex-1 flex items-center justify-center text-white mc-text-shadow text-xl font-bold uppercase tracking-widest hover:text-[#FFFF55] outline-none border-none" className="h-12 flex-1 flex items-center justify-center text-white mc-text-shadow text-xl font-bold uppercase tracking-widest hover:text-[#FFFF55] outline-none border-none"
+6 -2
View File
@@ -150,13 +150,15 @@ export default function LocEditorView() {
<option key={idx} value={idx}>{lang.id} {lang.isStatic ? `[${t("locEditor.static")}]` : `[${t("locEditor.keyed")}]`}</option> <option key={idx} value={idx}>{lang.id} {lang.isStatic ? `[${t("locEditor.static")}]` : `[${t("locEditor.keyed")}]`}</option>
))} ))}
</select> </select>
<div className="mc-textinput-outer flex-1">
<input <input
type="text" type="text"
placeholder={t("locEditor.searchStrings")} placeholder={t("locEditor.searchStrings")}
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
className="flex-1 bg-black/40 border-2 border-[#373737] text-white px-4 py-2 outline-none focus:border-[#FFFF55] transition-colors" className="mc-textinput w-full h-10 px-3 text-white text-base outline-none font-[var(--font-base)]"
/> />
</div>
<button <button
onClick={() => setIsLocEditModalOpen({ langIdx: selectedLocLangIdx, strIdx: -1, isNew: true })} onClick={() => setIsLocEditModalOpen({ langIdx: selectedLocLangIdx, strIdx: -1, isNew: true })}
className="px-6 py-2 text-white mc-text-shadow text-sm" className="px-6 py-2 text-white mc-text-shadow text-sm"
@@ -250,12 +252,14 @@ function LocEditModal({ data, lang, onClose, onConfirm }: { data: { langIdx: num
{!lang.isStatic ? ( {!lang.isStatic ? (
<div> <div>
<label className="text-white/40 text-xs uppercase mb-2 block">{t("locEditor.stringKey")}</label> <label className="text-white/40 text-xs uppercase mb-2 block">{t("locEditor.stringKey")}</label>
<div className="mc-textinput-outer">
<input <input
type="text" type="text"
value={key} value={key}
onChange={(e) => setKey(e.target.value)} onChange={(e) => setKey(e.target.value)}
className="w-full bg-black/40 border-2 border-[#373737] text-white px-4 py-3 outline-none focus:border-[#FFFF55] transition-colors font-mono" className="mc-textinput w-full h-10 px-3 text-white text-base outline-none font-[var(--font-base)] font-mono"
/> />
</div>
</div> </div>
) : ( ) : (
<div className="text-white/40 italic mb-2">{t("locEditor.staticEntry", { index: data.isNew ? lang.strings.length : data.strIdx })}</div> <div className="text-white/40 italic mb-2">{t("locEditor.staticEntry", { index: data.isNew ? lang.strings.length : data.strIdx })}</div>
+18 -6
View File
@@ -865,13 +865,15 @@ function PartEditModal({
<label className="text-white/40 text-[10px] uppercase tracking-widest mb-1 block"> <label className="text-white/40 text-[10px] uppercase tracking-widest mb-1 block">
{t("modelEditor.name")} {t("modelEditor.name")}
</label> </label>
<div className="mc-textinput-outer">
<input <input
type="text" type="text"
value={name} value={name}
onChange={(e) => setName(e.target.value)} onChange={(e) => setName(e.target.value)}
className="w-full bg-black/40 border-2 border-[#373737] text-white px-4 py-2 outline-none focus:border-[#FFFF55] transition-colors" className="mc-textinput w-full h-10 px-3 text-white text-base outline-none font-[var(--font-base)]"
autoFocus autoFocus
/> />
</div>
</div> </div>
<div> <div>
<label className="text-white/40 text-[10px] uppercase tracking-widest mb-1 block"> <label className="text-white/40 text-[10px] uppercase tracking-widest mb-1 block">
@@ -883,6 +885,7 @@ function PartEditModal({
<span className="text-white/40 text-xs font-mono"> <span className="text-white/40 text-xs font-mono">
{axis} {axis}
</span> </span>
<div className="mc-textinput-outer flex-1">
<input <input
type="number" type="number"
step={0.5} step={0.5}
@@ -893,8 +896,9 @@ function PartEditModal({
else if (axis === "Y") setTy(v); else if (axis === "Y") setTy(v);
else setTz(v); else setTz(v);
}} }}
className="w-full bg-black/40 border border-[#373737] text-white px-2 py-1 outline-none focus:border-[#FFFF55] text-sm" className="mc-textinput w-full h-10 px-3 text-white text-sm outline-none font-[var(--font-base)]"
/> />
</div>
</div> </div>
))} ))}
</div> </div>
@@ -985,6 +989,7 @@ function BoxEditModal({
<span className="text-white/40 text-xs font-mono"> <span className="text-white/40 text-xs font-mono">
{axis} {axis}
</span> </span>
<div className="mc-textinput-outer flex-1">
<input <input
type="number" type="number"
step={0.5} step={0.5}
@@ -995,8 +1000,9 @@ function BoxEditModal({
else if (axis === "Y") setPosY(v); else if (axis === "Y") setPosY(v);
else setPosZ(v); else setPosZ(v);
}} }}
className="w-full bg-black/40 border border-[#373737] text-white px-2 py-1 outline-none focus:border-[#FFFF55] text-sm" className="mc-textinput w-full h-10 px-3 text-white text-sm outline-none font-[var(--font-base)]"
/> />
</div>
</div> </div>
))} ))}
</div> </div>
@@ -1011,6 +1017,7 @@ function BoxEditModal({
<span className="text-white/40 text-xs font-mono"> <span className="text-white/40 text-xs font-mono">
{axis} {axis}
</span> </span>
<div className="mc-textinput-outer flex-1">
<input <input
type="number" type="number"
step={0.5} step={0.5}
@@ -1022,8 +1029,9 @@ function BoxEditModal({
else if (axis === "Y") setSizeY(v); else if (axis === "Y") setSizeY(v);
else setSizeZ(v); else setSizeZ(v);
}} }}
className="w-full bg-black/40 border border-[#373737] text-white px-2 py-1 outline-none focus:border-[#FFFF55] text-sm" className="mc-textinput w-full h-10 px-3 text-white text-sm outline-none font-[var(--font-base)]"
/> />
</div>
</div> </div>
))} ))}
</div> </div>
@@ -1038,6 +1046,7 @@ function BoxEditModal({
<span className="text-white/40 text-xs font-mono"> <span className="text-white/40 text-xs font-mono">
{axis} {axis}
</span> </span>
<div className="mc-textinput-outer flex-1">
<input <input
type="number" type="number"
min={0} min={0}
@@ -1047,8 +1056,9 @@ function BoxEditModal({
if (axis === "X") setUvX(v); if (axis === "X") setUvX(v);
else setUvY(v); else setUvY(v);
}} }}
className="w-full bg-black/40 border border-[#373737] text-white px-2 py-1 outline-none focus:border-[#FFFF55] text-sm" className="mc-textinput w-full h-10 px-3 text-white text-sm outline-none font-[var(--font-base)]"
/> />
</div>
</div> </div>
))} ))}
</div> </div>
@@ -1057,13 +1067,15 @@ function BoxEditModal({
<label className="text-white/40 text-[10px] uppercase tracking-widest"> <label className="text-white/40 text-[10px] uppercase tracking-widest">
{t("modelEditor.inflate")} {t("modelEditor.inflate")}
</label> </label>
<div className="mc-textinput-outer w-20">
<input <input
type="number" type="number"
step={0.1} step={0.1}
value={inflate} value={inflate}
onChange={(e) => setInflate(parseFloat(e.target.value) || 0)} onChange={(e) => setInflate(parseFloat(e.target.value) || 0)}
className="w-20 bg-black/40 border border-[#373737] text-white px-2 py-1 outline-none focus:border-[#FFFF55] text-sm" className="mc-textinput w-full h-10 px-3 text-white text-sm outline-none font-[var(--font-base)]"
/> />
</div>
</div> </div>
<div className="flex items-center gap-3 justify-end"> <div className="flex items-center gap-3 justify-end">
<label className="text-white/40 text-[10px] uppercase tracking-widest"> <label className="text-white/40 text-[10px] uppercase tracking-widest">
+9 -3
View File
@@ -161,27 +161,33 @@ export default function OptionsEditorView() {
<div className="flex flex-col gap-8 max-w-xl mx-auto"> <div className="flex flex-col gap-8 max-w-xl mx-auto">
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<span className="text-white/80 uppercase tracking-widest">{t("optionsEditor.chosenSkinId")}</span> <span className="text-white/80 uppercase tracking-widest">{t("optionsEditor.chosenSkinId")}</span>
<div className="mc-textinput-outer">
<input <input
type="number" value={opt.chosenSkin} onChange={(e) => updateSetting("chosenSkin", parseInt(e.target.value) || 0)} type="number" value={opt.chosenSkin} onChange={(e) => updateSetting("chosenSkin", parseInt(e.target.value) || 0)}
className="bg-black/40 border border-[#373737] p-2 text-white outline-none focus:border-[#FFFF55] font-mono text-lg" className="mc-textinput w-full h-10 px-3 text-white text-lg outline-none font-[var(--font-base)] font-mono"
/> />
</div>
</div> </div>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<span className="text-white/80 uppercase tracking-widest">{t("optionsEditor.playerCapeId")}</span> <span className="text-white/80 uppercase tracking-widest">{t("optionsEditor.playerCapeId")}</span>
<div className="mc-textinput-outer">
<input <input
type="number" value={opt.playerCape} onChange={(e) => updateSetting("playerCape", parseInt(e.target.value) || 0)} type="number" value={opt.playerCape} onChange={(e) => updateSetting("playerCape", parseInt(e.target.value) || 0)}
className="bg-black/40 border border-[#373737] p-2 text-white outline-none focus:border-[#FFFF55] font-mono text-lg" className="mc-textinput w-full h-10 px-3 text-white text-lg outline-none font-[var(--font-base)] font-mono"
/> />
</div>
</div> </div>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<span className="text-white/80 uppercase tracking-widest">{t("optionsEditor.favoriteSkins")}</span> <span className="text-white/80 uppercase tracking-widest">{t("optionsEditor.favoriteSkins")}</span>
{opt.favoriteSkins.map((s, i) => ( {opt.favoriteSkins.map((s, i) => (
<div key={i} className="flex items-center gap-4"> <div key={i} className="flex items-center gap-4">
<span className="text-white/40 w-8">{i + 1}.</span> <span className="text-white/40 w-8">{i + 1}.</span>
<div className="mc-textinput-outer flex-1">
<input <input
type="number" value={s} onChange={(e) => updateFavSkin(i, parseInt(e.target.value) || 0)} type="number" value={s} onChange={(e) => updateFavSkin(i, parseInt(e.target.value) || 0)}
className="bg-black/40 flex-1 border border-[#373737] p-2 text-white outline-none focus:border-[#FFFF55] font-mono" className="mc-textinput w-full h-10 px-3 text-white outline-none font-[var(--font-base)] font-mono"
/> />
</div>
</div> </div>
))} ))}
</div> </div>
+12 -4
View File
@@ -646,13 +646,15 @@ export default function PckEditorView() {
}} }}
> >
<div className="mb-4 flex gap-4"> <div className="mb-4 flex gap-4">
<div className="mc-textinput-outer flex-1">
<input <input
type="text" type="text"
placeholder={t("pckEditor.searchAssets")} placeholder={t("pckEditor.searchAssets")}
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
className="flex-1 bg-black/40 border-2 border-[#373737] text-white px-4 py-2 outline-none focus:border-[#FFFF55] transition-colors" className="mc-textinput w-full h-10 px-3 text-white text-base outline-none font-[var(--font-base)]"
/> />
</div>
<button <button
onClick={() => addAssetInputRef.current?.click()} onClick={() => addAssetInputRef.current?.click()}
className="px-4 py-2 text-white mc-text-shadow text-sm shrink-0" className="px-4 py-2 text-white mc-text-shadow text-sm shrink-0"
@@ -848,14 +850,16 @@ export default function PckEditorView() {
className="flex flex-col gap-1 group/prop" className="flex flex-col gap-1 group/prop"
> >
<div className="flex justify-between items-center px-1"> <div className="flex justify-between items-center px-1">
<div className="mc-textinput-outer w-2/3">
<input <input
type="text" type="text"
value={prop.key} value={prop.key}
onChange={(e) => onChange={(e) =>
handlePropertyEdit(idx, e.target.value, true) handlePropertyEdit(idx, e.target.value, true)
} }
className="bg-transparent text-white/40 text-[10px] outline-none hover:text-white/60 focus:text-[#FFFF55] w-2/3" className="mc-textinput w-full h-8 px-2 text-white/60 text-[10px] outline-none font-[var(--font-base)]"
/> />
</div>
<button <button
onClick={() => handleRemoveProperty(idx)} onClick={() => handleRemoveProperty(idx)}
className="text-red-500/0 group-hover/prop:text-red-500/40 hover:text-red-500 transition-colors text-[10px] uppercase" className="text-red-500/0 group-hover/prop:text-red-500/40 hover:text-red-500 transition-colors text-[10px] uppercase"
@@ -864,14 +868,16 @@ export default function PckEditorView() {
</button> </button>
</div> </div>
<div className="relative"> <div className="relative">
<div className="mc-textinput-outer">
<input <input
type="text" type="text"
value={prop.value} value={prop.value}
onChange={(e) => onChange={(e) =>
handlePropertyEdit(idx, e.target.value) handlePropertyEdit(idx, e.target.value)
} }
className="w-full bg-black/40 p-2 text-white border border-[#373737] text-sm focus:border-[#FFFF55] outline-none transition-colors" className="mc-textinput w-full h-10 px-3 text-white text-base outline-none font-[var(--font-base)] text-sm"
/> />
</div>
</div> </div>
{prop.key === "ANIM" && ( {prop.key === "ANIM" && (
@@ -1175,13 +1181,15 @@ function RenameAssetModal({
<label className="text-white/40 text-[10px] uppercase tracking-widest mb-1 block"> <label className="text-white/40 text-[10px] uppercase tracking-widest mb-1 block">
{t("pckEditor.newAssetPath")} {t("pckEditor.newAssetPath")}
</label> </label>
<div className="mc-textinput-outer">
<input <input
type="text" type="text"
value={path} value={path}
onChange={(e) => setPath(e.target.value)} onChange={(e) => setPath(e.target.value)}
className="w-full bg-black/40 border-2 border-[#373737] text-white px-4 py-3 outline-none focus:border-[#FFFF55] transition-colors" className="mc-textinput w-full h-10 px-3 text-white text-base outline-none font-[var(--font-base)]"
autoFocus autoFocus
/> />
</div>
</div> </div>
<div className="flex justify-end gap-4 mt-4"> <div className="flex justify-end gap-4 mt-4">
<button <button
+34 -16
View File
@@ -798,6 +798,13 @@ const SettingsView = memo(function SettingsView() {
useEffect(() => { useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => { const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "Escape") { if (e.key === "Escape") {
if (
document.activeElement instanceof HTMLInputElement &&
document.activeElement.type === "text"
) {
document.activeElement.blur();
return;
}
if (showModal) { if (showModal) {
playBackSound(); playBackSound();
setShowModal(null); setShowModal(null);
@@ -848,6 +855,11 @@ const SettingsView = memo(function SettingsView() {
const item = settingsItems[focusIndex]; const item = settingsItems[focusIndex];
if (item.type === "button") { if (item.type === "button") {
item.onClick(); item.onClick();
} else if (item.type === "textinput") {
const el = containerRef.current?.querySelector(
`[data-index="${focusIndex}"] input`,
) as HTMLInputElement | null;
el?.focus();
} }
} }
}; };
@@ -955,6 +967,7 @@ const SettingsView = memo(function SettingsView() {
<div <div
key={item.id} key={item.id}
data-index={index} data-index={index}
tabIndex={0}
onMouseEnter={() => setFocusIndex(index)} onMouseEnter={() => setFocusIndex(index)}
className="relative w-[480px] flex flex-col cursor-pointer transition-all outline-none shrink-0" className="relative w-[480px] flex flex-col cursor-pointer transition-all outline-none shrink-0"
> >
@@ -1116,6 +1129,7 @@ const SettingsView = memo(function SettingsView() {
<div <div
key={item.id} key={item.id}
data-index={index} data-index={index}
tabIndex={0}
onMouseEnter={() => setFocusIndex(index)} onMouseEnter={() => setFocusIndex(index)}
className="relative w-[600px] flex flex-col cursor-pointer transition-all outline-none shrink-0" className="relative w-[600px] flex flex-col cursor-pointer transition-all outline-none shrink-0"
> >
@@ -1238,14 +1252,16 @@ const SettingsView = memo(function SettingsView() {
<p className="text-[#AAAAAA] text-xs mb-4 text-center mc-text-shadow"> <p className="text-[#AAAAAA] text-xs mb-4 text-center mc-text-shadow">
{t("settings.extraLaunchArgsDesc")} {t("settings.extraLaunchArgsDesc")}
</p> </p>
<input <div className="mc-textinput-outer">
autoFocus <input
value={argsInput} autoFocus
onChange={(e) => setArgsInput(e.target.value)} value={argsInput}
placeholder="e.g. -quitondisconnect -ip 127.0.0.1" onChange={(e) => setArgsInput(e.target.value)}
className="w-full h-10 px-3 bg-black/40 border-2 border-[#373737] text-white text-base outline-none font-[var(--font-base)] text-center" placeholder="e.g. -quitondisconnect -ip 127.0.0.1"
style={{ imageRendering: "pixelated" }} className="mc-textinput w-full h-10 px-3 text-white text-base outline-none font-[var(--font-base)] text-center"
/> style={{ imageRendering: "pixelated" }}
/>
</div>
<div className="flex gap-4 mt-6 w-full justify-center"> <div className="flex gap-4 mt-6 w-full justify-center">
<button <button
onClick={() => { onClick={() => {
@@ -1318,14 +1334,16 @@ const SettingsView = memo(function SettingsView() {
<p className="text-[#AAAAAA] text-xs mb-4 text-center mc-text-shadow"> <p className="text-[#AAAAAA] text-xs mb-4 text-center mc-text-shadow">
{t("settings.launchPrefixDesc")} {t("settings.launchPrefixDesc")}
</p> </p>
<input <div className="mc-textinput-outer">
autoFocus <input
value={prefixInput} autoFocus
onChange={(e) => setPrefixInput(e.target.value)} value={prefixInput}
placeholder="e.g. gamemoderun" onChange={(e) => setPrefixInput(e.target.value)}
className="w-full h-10 px-3 bg-black/40 border-2 border-[#373737] text-white text-base outline-none font-[var(--font-base)] text-center" placeholder="e.g. gamemoderun"
style={{ imageRendering: "pixelated" }} className="mc-textinput w-full h-10 px-3 text-white text-base outline-none font-[var(--font-base)] text-center"
/> style={{ imageRendering: "pixelated" }}
/>
</div>
<div className="flex gap-4 mt-6 w-full justify-center"> <div className="flex gap-4 mt-6 w-full justify-center">
<button <button
onClick={() => { onClick={() => {
+6 -6
View File
@@ -273,6 +273,7 @@ const SetupView: React.FC<SetupViewProps> = ({ onComplete }) => {
{t("setup.username")} {t("setup.username")}
</span> </span>
<div className="relative"> <div className="relative">
<div className="mc-textinput-outer">
<input <input
type="text" type="text"
ref={(el) => { ref={(el) => {
@@ -293,7 +294,7 @@ const SetupView: React.FC<SetupViewProps> = ({ onComplete }) => {
const caret = const caret =
document.getElementById("custom-caret"); document.getElementById("custom-caret");
if (caret) { if (caret) {
caret.style.left = `${cursorPosition + 16}px`; caret.style.left = `${cursorPosition + 14}px`;
} }
} }
}} }}
@@ -321,29 +322,28 @@ const SetupView: React.FC<SetupViewProps> = ({ onComplete }) => {
const caret = const caret =
document.getElementById("custom-caret"); document.getElementById("custom-caret");
if (caret) { if (caret) {
caret.style.left = `${cursorPosition + 16}px`; caret.style.left = `${cursorPosition + 14}px`;
} }
} }
}} }}
className={`w-full px-4 py-2 focus:outline-none transition-colors text-white tracking-widest className="mc-textinput w-full h-10 px-3 text-white text-base tracking-widest outline-none font-[var(--font-base)]"
${focusIndex === 0 ? "border-4 border-[#FFFF55] text-[#FFFF55]" : "border-4 border-[#323232]"}`}
style={{ style={{
imageRendering: "pixelated", imageRendering: "pixelated",
fontFamily: "'Mojangles', monospace", fontFamily: "'Mojangles', monospace",
backgroundColor: "#646464",
caretColor: "transparent", caretColor: "transparent",
}} }}
placeholder={t("setup.enterUsername")} placeholder={t("setup.enterUsername")}
maxLength={16} maxLength={16}
autoFocus autoFocus
/> />
</div>
{focusIndex === 0 && ( {focusIndex === 0 && (
<span <span
id="custom-caret" id="custom-caret"
className="absolute top-1/2 -translate-y-1/2 text-white blink-caret" className="absolute top-1/2 -translate-y-1/2 text-white blink-caret"
style={{ style={{
fontFamily: "'Mojangles', monospace", fontFamily: "'Mojangles', monospace",
left: "16px", left: "14px",
}} }}
> >
_ _
+3 -1
View File
@@ -641,6 +641,7 @@ const SkinEditorView = memo(function SkinEditorView() {
style={{ boxShadow: "inset 2px 2px 0 #000, inset -1px -1px 0 #555" }} style={{ boxShadow: "inset 2px 2px 0 #000, inset -1px -1px 0 #555" }}
> >
<span className="pl-2 text-white/50 text-sm mc-text-shadow">#</span> <span className="pl-2 text-white/50 text-sm mc-text-shadow">#</span>
<div className="mc-textinput-outer w-full h-8">
<input <input
type="text" type="text"
value={hexValue} value={hexValue}
@@ -651,8 +652,9 @@ const SkinEditorView = memo(function SkinEditorView() {
setHexValue(hex.toUpperCase()); setHexValue(hex.toUpperCase());
if (hex.length === 6) setColorFromHex(`#${hex}`); if (hex.length === 6) setColorFromHex(`#${hex}`);
}} }}
className="w-full h-full px-1 text-sm text-white tracking-widest uppercase mc-text-shadow bg-transparent" className="mc-textinput w-full h-full px-1 text-sm text-white tracking-widest uppercase outline-none"
/> />
</div>
</div> </div>
<div className="relative w-full h-10 flex items-center justify-center" style={sliderBg}> <div className="relative w-full h-10 flex items-center justify-center" style={sliderBg}>
+46 -34
View File
@@ -561,7 +561,9 @@ const SkinsView = memo(function SkinsView() {
imageRendering: "pixelated", imageRendering: "pixelated",
}} }}
> >
{viewMode === "skin" ? t("skins.importSkin") : t("skins.importCape")} {viewMode === "skin"
? t("skins.importSkin")
: t("skins.importCape")}
</button> </button>
<button <button
@@ -596,7 +598,9 @@ const SkinsView = memo(function SkinsView() {
imageRendering: "pixelated", imageRendering: "pixelated",
}} }}
> >
{viewMode === "skin" ? t("skins.deleteSkin") : t("skins.deleteCape")} {viewMode === "skin"
? t("skins.deleteSkin")
: t("skins.deleteCape")}
</button> </button>
{viewMode === "skin" && ( {viewMode === "skin" && (
@@ -708,16 +712,20 @@ const SkinsView = memo(function SkinsView() {
> >
<HeadPreview src={skin.url} /> <HeadPreview src={skin.url} />
</div> </div>
<input <div className="w-full">
type="text" <input
value={skin.name} type="text"
maxLength={16} value={skin.name}
onChange={(e) => handleNameChange(skin.id, e.target.value)} maxLength={16}
className={`bg-transparent text-center outline-none border-none text-base mc-text-shadow w-full truncate transition-colors relative z-10 ${isActive || isFocused ? "text-[#FFFF55]" : "text-white"} ${isDefaultSkin(skin.id) ? "pointer-events-none" : ""}`} onChange={(e) =>
onClick={(e) => e.stopPropagation()} handleNameChange(skin.id, e.target.value)
spellCheck={false} }
readOnly={isDefaultSkin(skin.id)} className={`w-full h-10 px-3 text-base text-center truncate outline-none relative z-10 font-[var(--font-base)] ${isActive || isFocused ? "text-[#FFFF55]" : "text-white"} ${isDefaultSkin(skin.id) ? "pointer-events-none" : ""}`}
/> onClick={(e) => e.stopPropagation()}
spellCheck={false}
readOnly={isDefaultSkin(skin.id)}
/>
</div>
</div> </div>
); );
}) })
@@ -779,18 +787,20 @@ const SkinsView = memo(function SkinsView() {
> >
<CapePreview src={cape.url} /> <CapePreview src={cape.url} />
</div> </div>
<input <div className="mc-textinput-outer w-full">
type="text" <input
value={cape.name} type="text"
maxLength={16} value={cape.name}
onChange={(e) => maxLength={16}
handleCapeNameChange(cape.id, e.target.value) onChange={(e) =>
} handleCapeNameChange(cape.id, e.target.value)
className={`bg-transparent text-center outline-none border-none text-base mc-text-shadow w-full truncate transition-colors relative z-10 ${isActive || isFocused ? "text-[#FFFF55]" : "text-white"} ${isDefaultCape(cape.id) ? "pointer-events-none" : ""}`} }
onClick={(e) => e.stopPropagation()} className={`mc-textinput w-full h-10 px-3 text-base text-center truncate outline-none relative z-10 font-[var(--font-base)] ${isActive || isFocused ? "text-[#FFFF55]" : "text-white"} ${isDefaultCape(cape.id) ? "pointer-events-none" : ""}`}
spellCheck={false} onClick={(e) => e.stopPropagation()}
readOnly={isDefaultCape(cape.id)} spellCheck={false}
/> readOnly={isDefaultCape(cape.id)}
/>
</div>
</div> </div>
); );
})} })}
@@ -896,16 +906,18 @@ const SkinsView = memo(function SkinsView() {
</div> </div>
) : importMode === "username" ? ( ) : importMode === "username" ? (
<div className="flex flex-col gap-4 w-full px-4 mb-2"> <div className="flex flex-col gap-4 w-full px-4 mb-2">
<input <div className="mc-textinput-outer w-full">
type="text" <input
placeholder={t("skins.minecraftUsername")} type="text"
value={importUsername} placeholder={t("skins.minecraftUsername")}
onChange={(e) => setImportUsername(e.target.value)} value={importUsername}
onFocus={() => setModalFocusIndex(0)} onChange={(e) => setImportUsername(e.target.value)}
autoFocus onFocus={() => setModalFocusIndex(0)}
spellCheck={false} autoFocus
className={`w-full h-12 bg-black/50 border-2 text-white px-4 text-xl outline-none transition-colors relative z-10 ${modalFocusIndex === 0 ? "border-[#FFFF55]" : "border-[#373737]"}`} spellCheck={false}
/> className="mc-textinput w-full h-10 px-3 text-white text-xl outline-none font-[var(--font-base)]"
/>
</div>
{importError && ( {importError && (
<span className="text-red-400 text-sm text-center mc-text-shadow"> <span className="text-red-400 text-sm text-center mc-text-shadow">
+3 -1
View File
@@ -218,13 +218,15 @@ export default function SwfView() {
<div className="flex-1 w-full flex gap-4 overflow-hidden"> <div className="flex-1 w-full flex gap-4 overflow-hidden">
<div className="w-1/3 flex flex-col p-4" style={{ backgroundImage: "url('/images/frame_background.png')", backgroundSize: "100% 100%", imageRendering: "pixelated" }}> <div className="w-1/3 flex flex-col p-4" style={{ backgroundImage: "url('/images/frame_background.png')", backgroundSize: "100% 100%", imageRendering: "pixelated" }}>
<div className="mb-4"> <div className="mb-4">
<div className="mc-textinput-outer">
<input <input
type="text" type="text"
placeholder={t("swfEditor.searchAssets")} placeholder={t("swfEditor.searchAssets")}
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
className="w-full bg-black/40 border-2 border-[#373737] text-white px-4 py-2 outline-none focus:border-[#FFFF55] transition-colors" className="mc-textinput w-full h-10 px-3 text-white text-base outline-none font-[var(--font-base)]"
/> />
</div>
</div> </div>
<div className="flex-1 overflow-y-auto pr-2 custom-scrollbar space-y-1"> <div className="flex-1 overflow-y-auto pr-2 custom-scrollbar space-y-1">
{filteredImages.map(img => ( {filteredImages.map(img => (
+34 -35
View File
@@ -843,46 +843,41 @@ const WorkshopView = memo(function WorkshopView({
{showSearch ? ( {showSearch ? (
<div className="absolute inset-0 flex flex-col pt-2"> <div className="absolute inset-0 flex flex-col pt-2">
<div className="flex items-center gap-3 px-6 pb-4"> <div className="flex items-center gap-3 px-6 pb-4">
<div <div className="flex items-center flex-1 h-12 px-4 rounded">
className="flex items-center flex-1 h-12 px-4 border-2 border-[#444] bg-black/40 rounded shadow-inner" <div className="mc-textinput-outer flex-1">
style={{ <input
backgroundImage: "url('/images/Button_Background2.png')", ref={searchRef}
backgroundSize: "100% 100%", type="text"
imageRendering: "pixelated", value={search}
}} onChange={(e) => {
> setSearch(e.target.value);
<input setFocusedIdx(null);
ref={searchRef} }}
type="text" placeholder={
value={search} isInstalledTab
onChange={(e) => { ? t("workshop.filterInstalled")
setSearch(e.target.value); : isVersionTab
setFocusedIdx(null); ? t("workshop.filterVersions")
}} : isPluginTab
placeholder={
isInstalledTab
? t("workshop.filterInstalled")
: isVersionTab
? t("workshop.filterVersions")
: isPluginTab
? t("workshop.filterPlugins")
: activeTab === "Server Plugins"
? t("workshop.filterPlugins") ? t("workshop.filterPlugins")
: activeTab === "Server" : activeTab === "Server Plugins"
? t("workshop.filterServers") ? t("workshop.filterPlugins")
: t("workshop.enterKeywords") : activeTab === "Server"
} ? t("workshop.filterServers")
spellCheck={false} : t("workshop.enterKeywords")
autoFocus={isSearchTab} }
className="bg-transparent border-none outline-none text-white text-lg mc-text-shadow w-full placeholder-white/40 font-[var(--font-base)] tracking-widest" spellCheck={false}
/> autoFocus={isSearchTab}
className="mc-textinput w-full h-10 px-3 text-white text-base outline-none font-[var(--font-base)] placeholder-white/40 tracking-widest"
/>
</div>
{search && ( {search && (
<button <button
onClick={() => { onClick={() => {
setSearch(""); setSearch("");
searchRef.current?.focus(); searchRef.current?.focus();
}} }}
className="text-white/60 hover:text-white text-lg ml-2 bg-transparent border-none outline-none cursor-pointer mc-text-shadow" className="text-white/60 hover:text-white text-lg mx-2 border-none outline-none cursor-pointer"
> >
</button> </button>
@@ -901,7 +896,9 @@ const WorkshopView = memo(function WorkshopView({
: "text-[#A0A0A0] bg-black/30 border-[#444] hover:text-white hover:border-[#888]" : "text-[#A0A0A0] bg-black/30 border-[#444] hover:text-white hover:border-[#888]"
}`} }`}
> >
{cat === "all" ? t("common.all").toUpperCase() : cat.toUpperCase()} {cat === "all"
? t("common.all").toUpperCase()
: cat.toUpperCase()}
</button> </button>
))} ))}
</div> </div>
@@ -918,7 +915,9 @@ const WorkshopView = memo(function WorkshopView({
: "text-[#A0A0A0] bg-black/30 border-[#444] hover:text-white hover:border-[#888]" : "text-[#A0A0A0] bg-black/30 border-[#444] hover:text-white hover:border-[#888]"
}`} }`}
> >
{cat === "all" ? t("common.all").toUpperCase() : cat.toUpperCase()} {cat === "all"
? t("common.all").toUpperCase()
: cat.toUpperCase()}
</button> </button>
))} ))}
</div> </div>
+2 -1
View File
@@ -453,7 +453,8 @@ body {
image-rendering: pixelated; image-rendering: pixelated;
} }
.mc-textinput-outer:focus-within { .mc-textinput-outer:focus-within,
:focus > .mc-textinput-outer {
border: 2px solid #ffff00; border: 2px solid #ffff00;
} }
+1 -6
View File
@@ -1,11 +1,6 @@
import { useEffect, useState, useMemo, useCallback, useRef } from "react"; import { useEffect, useState, useMemo, useCallback, useRef } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { import { motion, AnimatePresence, MotionConfig } from "framer-motion";
motion,
AnimatePresence,
MotionConfig,
getDefaultTransition,
} from "framer-motion";
import "../css/App.css"; import "../css/App.css";
import HomeView from "../components/views/HomeView"; import HomeView from "../components/views/HomeView";
import SettingsView from "../components/views/SettingsView"; import SettingsView from "../components/views/SettingsView";