mirror of
https://github.com/DustinBrett/daedalOS.git
synced 2025-12-06 00:20:05 +01:00
Compare commits
4 Commits
66b7a509e8
...
492b34e92e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
492b34e92e | ||
|
|
839c9a9061 | ||
|
|
8f9eae5027 | ||
|
|
dcea214934 |
2
IDEAS.md
2
IDEAS.md
|
|
@ -319,7 +319,7 @@
|
|||
- [Xash3D-Emscripten](https://github.com/btarg/Xash3D-Emscripten)
|
||||
- Minesweeper ([1](https://github.com/ziebelje/minesweeper), [2](https://github.com/ShizukuIchi/minesweeper))
|
||||
- [S.U.R.F.](https://github.com/jackbuehner/MicrosoftEdge-S.U.R.F.)
|
||||
- Game of Life ([1](https://github.com/rustwasm/wasm_game_of_life), [2](https://github.com/skeeto/webgl-game-of-life/))
|
||||
- Game of Life ([1](https://github.com/copy/life), [2](https://github.com/rustwasm/wasm_game_of_life), [3](https://github.com/skeeto/webgl-game-of-life/))
|
||||
- Solitaire ([1](https://github.com/rjanjic/js-solitaire), [2](https://github.com/1j01/98/tree/master/programs/js-solitaire))
|
||||
- [Heroes of Might and Magic II](https://github.com/ihhub/fheroes2)
|
||||
- [Doom 3](https://wasm.continuation-labs.com/d3demo/)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import {
|
|||
PACKAGE_DATA,
|
||||
} from "utils/constants";
|
||||
import {
|
||||
bufferToUrl,
|
||||
getDpi,
|
||||
getExtension,
|
||||
getMimeType,
|
||||
|
|
@ -53,22 +52,13 @@ const Metadata: FC = () => {
|
|||
);
|
||||
const getCursor = useCallback(
|
||||
async (path: string) => {
|
||||
const { getFirstAniImage, getLargestIcon } = await import(
|
||||
"utils/imageDecoder"
|
||||
);
|
||||
const imageBuffer = await readFile(path);
|
||||
const extension = getExtension(path);
|
||||
let image: Buffer | undefined = imageBuffer;
|
||||
|
||||
if (extension === ".ani") {
|
||||
image = await getFirstAniImage(imageBuffer);
|
||||
} else {
|
||||
const largestIcon = await getLargestIcon(imageBuffer, 128);
|
||||
if (!imageBuffer || imageBuffer.length === 0) return "";
|
||||
|
||||
if (largestIcon) return largestIcon;
|
||||
}
|
||||
const { cursorToCss } = await import("utils/imageDecoder");
|
||||
|
||||
return image ? bufferToUrl(image, getMimeType(path)) : "";
|
||||
return cursorToCss(imageBuffer, path);
|
||||
},
|
||||
[readFile]
|
||||
);
|
||||
|
|
@ -173,9 +163,7 @@ const Metadata: FC = () => {
|
|||
/>
|
||||
);
|
||||
})}
|
||||
{customCursor && (
|
||||
<style>{`*, *::before, *::after { cursor: url(${customCursor}), default !important; }`}</style>
|
||||
)}
|
||||
{customCursor && <style>{customCursor}</style>}
|
||||
</Head>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
24
package.json
24
package.json
|
|
@ -39,7 +39,7 @@
|
|||
"*.{js,ts,tsx}": "eslint --fix"
|
||||
},
|
||||
"resolutions": {
|
||||
"@emotion/is-prop-valid": "^1.3.1"
|
||||
"@emotion/is-prop-valid": "^1.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ffmpeg/ffmpeg": "^0.12.15",
|
||||
|
|
@ -63,7 +63,7 @@
|
|||
"ini": "^5.0.0",
|
||||
"isomorphic-git": "^1.33.1",
|
||||
"libheif-js": "^1.19.8",
|
||||
"mediainfo.js": "0.3.5",
|
||||
"mediainfo.js": "0.3.6",
|
||||
"minimist": "^1.2.8",
|
||||
"motion": "^12.23.12",
|
||||
"multiformats": "^13.4.0",
|
||||
|
|
@ -98,7 +98,7 @@
|
|||
"@types/jest": "^30.0.0",
|
||||
"@types/lunr": "^2.3.7",
|
||||
"@types/minimist": "^1.2.5",
|
||||
"@types/node": "^24.3.0",
|
||||
"@types/node": "^24.3.1",
|
||||
"@types/offscreencanvas": "^2019.7.3",
|
||||
"@types/opentype.js": "^1.3.8",
|
||||
"@types/react": "^19.1.12",
|
||||
|
|
@ -106,8 +106,8 @@
|
|||
"@types/ua-parser-js": "^0.7.39",
|
||||
"@types/video.js": "^7.3.58",
|
||||
"@types/wicg-file-system-access": "^2023.10.6",
|
||||
"@typescript-eslint/eslint-plugin": "^8.41.0",
|
||||
"@typescript-eslint/parser": "^8.41.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.42.0",
|
||||
"@typescript-eslint/parser": "^8.42.0",
|
||||
"emulators": "^8.3.9",
|
||||
"emulators-ui": "^0.73.9",
|
||||
"eruda": "^3.4.3",
|
||||
|
|
@ -134,21 +134,21 @@
|
|||
"html-minifier-terser": "^7.2.0",
|
||||
"html-to-image": "^1.11.13",
|
||||
"husky": "^9.1.7",
|
||||
"jest": "^30.1.1",
|
||||
"jest-environment-jsdom": "^30.1.1",
|
||||
"lint-staged": "^16.1.5",
|
||||
"jest": "^30.1.3",
|
||||
"jest-environment-jsdom": "^30.1.2",
|
||||
"lint-staged": "^16.1.6",
|
||||
"lunr": "^2.3.9",
|
||||
"monaco-editor": "^0.52.2",
|
||||
"pdfjs-dist": "^5.4.54",
|
||||
"pdfjs-dist": "^5.4.149",
|
||||
"playwright-core": "^1.55.0",
|
||||
"postcss": "^8.5.6",
|
||||
"postcss-styled-syntax": "^0.7.1",
|
||||
"postcss-syntax": "^0.36.2",
|
||||
"serve": "^14.2.4",
|
||||
"stylelint": "^16.23.1",
|
||||
"serve": "^14.2.5",
|
||||
"stylelint": "^16.24.0",
|
||||
"stylelint-config-standard": "^39.0.0",
|
||||
"stylelint-order": "^7.0.0",
|
||||
"terser": "^5.43.1",
|
||||
"terser": "^5.44.0",
|
||||
"tinymce": "^7.9.1",
|
||||
"ts-prune": "^0.10.3",
|
||||
"typescript": "^5.9.2",
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
blobToBuffer,
|
||||
bufferToUrl,
|
||||
cleanUpBufferUrl,
|
||||
getExtension,
|
||||
getGifJs,
|
||||
getMimeType,
|
||||
imgDataToBuffer,
|
||||
|
|
@ -15,6 +16,9 @@ import {
|
|||
|
||||
type JxlDecodeResponse = { data: { imgData: ImageData } };
|
||||
|
||||
const JIFFIES_IN_SECOND = 60;
|
||||
const DEFAULT_JIFFY_RATE = 10;
|
||||
|
||||
const supportsImageType = async (type: string): Promise<boolean> => {
|
||||
const img = document.createElement("img");
|
||||
|
||||
|
|
@ -71,9 +75,10 @@ const aniToGif = async (aniBuffer: Buffer): Promise<Buffer> => {
|
|||
const gif = await getGifJs();
|
||||
const { parseAni } = await import("ani-cursor/dist/parser");
|
||||
let images: Uint8Array[] = [];
|
||||
let metadata: { iDispRate?: number } = {};
|
||||
|
||||
try {
|
||||
({ images } = parseAni(aniBuffer));
|
||||
({ images, metadata } = parseAni(aniBuffer));
|
||||
} catch {
|
||||
return aniBuffer;
|
||||
}
|
||||
|
|
@ -88,7 +93,12 @@ const aniToGif = async (aniBuffer: Buffer): Promise<Buffer> => {
|
|||
imageIcon.addEventListener(
|
||||
"load",
|
||||
() => {
|
||||
gif.addFrame(imageIcon);
|
||||
gif.addFrame(imageIcon, {
|
||||
delay:
|
||||
((metadata.iDispRate || DEFAULT_JIFFY_RATE) /
|
||||
JIFFIES_IN_SECOND) *
|
||||
1000,
|
||||
});
|
||||
cleanUpBufferUrl(bufferUrl);
|
||||
resolve();
|
||||
},
|
||||
|
|
@ -128,7 +138,46 @@ export const getFirstAniImage = async (
|
|||
return undefined;
|
||||
};
|
||||
|
||||
export const getLargestIcon = async (
|
||||
const getGlobalCursorCSS = (cursorUrl: string): string =>
|
||||
`*, *::before, *::after { cursor: url(${cursorUrl}), default !important; }`;
|
||||
|
||||
const aniToCss = async (
|
||||
imageBuffer: Buffer,
|
||||
mimeType: string
|
||||
): Promise<string> => {
|
||||
const { parseAni } = await import("ani-cursor/dist/parser");
|
||||
const { metadata, images } = parseAni(imageBuffer);
|
||||
const toUrl = (image: Uint8Array): string =>
|
||||
bufferToUrl(Buffer.from(image), mimeType);
|
||||
|
||||
if (images.length === 1) return getGlobalCursorCSS(toUrl(images[0]));
|
||||
|
||||
if (images.length > 1) {
|
||||
const animationName = `cursor-ani-${Date.now()}`;
|
||||
const keyframes = `
|
||||
@keyframes ${animationName} {
|
||||
${images
|
||||
.map(
|
||||
(image, i) =>
|
||||
`${((i / images.length) * 100).toFixed(1)}% { cursor: url(${toUrl(image)}), default; }`
|
||||
)
|
||||
.join("")}
|
||||
100% { cursor: url(${toUrl(images[0])}), default; }
|
||||
}
|
||||
`;
|
||||
const duration = Math.ceil(
|
||||
((metadata.iDispRate || DEFAULT_JIFFY_RATE) / JIFFIES_IN_SECOND) *
|
||||
images.length *
|
||||
1000
|
||||
);
|
||||
|
||||
return `${keyframes}* { animation: ${animationName} ${duration}ms infinite steps(1) !important; }`;
|
||||
}
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
const getLargestIcon = async (
|
||||
imageBuffer: Buffer,
|
||||
maxSize: number
|
||||
): Promise<string> => {
|
||||
|
|
@ -152,6 +201,23 @@ export const getLargestIcon = async (
|
|||
}
|
||||
};
|
||||
|
||||
export const cursorToCss = async (
|
||||
buffer: Buffer,
|
||||
path: string
|
||||
): Promise<string> => {
|
||||
if (getExtension(path) === ".ani") {
|
||||
const animatedCursorCss = await aniToCss(buffer, getMimeType(path));
|
||||
|
||||
if (animatedCursorCss) return animatedCursorCss;
|
||||
}
|
||||
|
||||
const largestIcon = await getLargestIcon(buffer, 128);
|
||||
|
||||
return getGlobalCursorCSS(
|
||||
largestIcon || bufferToUrl(buffer, getMimeType(path))
|
||||
);
|
||||
};
|
||||
|
||||
const canLoadNative = async (
|
||||
extension: string,
|
||||
file: Buffer
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user