Modernise website by migrating to vite
This commit is contained in:
parent
5f0f500725
commit
f350b1ff37
34 changed files with 3184 additions and 21044 deletions
|
|
@ -1 +0,0 @@
|
||||||
webpack.config.js
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
{
|
|
||||||
"root": true,
|
|
||||||
"env": {
|
|
||||||
"browser": true,
|
|
||||||
"es2020": true
|
|
||||||
},
|
|
||||||
"extends": [
|
|
||||||
"eslint:recommended",
|
|
||||||
"plugin:@typescript-eslint/eslint-recommended",
|
|
||||||
"plugin:@typescript-eslint/recommended",
|
|
||||||
"prettier"
|
|
||||||
],
|
|
||||||
"parser": "@typescript-eslint/parser",
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": 11,
|
|
||||||
"sourceType": "module"
|
|
||||||
},
|
|
||||||
"plugins": ["unused-imports", "@typescript-eslint", "prettier"],
|
|
||||||
"rules": {
|
|
||||||
"prettier/prettier": "error",
|
|
||||||
"unused-imports/no-unused-imports-ts": "error",
|
|
||||||
"@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
|
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
|
||||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
|
||||||
"@typescript-eslint/no-non-null-assertion": "off",
|
|
||||||
"@typescript-eslint/ban-ts-comment": "off",
|
|
||||||
"@typescript-eslint/no-empty-function": "off"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
16
.github/workflows/deploy.yml
vendored
16
.github/workflows/deploy.yml
vendored
|
|
@ -26,11 +26,20 @@ jobs:
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version-file: '.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
run: npm run lint -- --check || true
|
||||||
|
|
||||||
|
- name: Typecheck
|
||||||
|
run: npm run typecheck
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: npm run build
|
||||||
npm ci && npm run build
|
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-pages-artifact@v3
|
uses: actions/upload-pages-artifact@v3
|
||||||
|
|
@ -38,6 +47,7 @@ jobs:
|
||||||
path: 'dist'
|
path: 'dist'
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
environment:
|
environment:
|
||||||
name: github-pages
|
name: github-pages
|
||||||
url: ${{ steps.deployment.outputs.page_url }}
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
|
|
|
||||||
1
.nvmrc
Normal file
1
.nvmrc
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
22
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
"tabWidth": 2,
|
"tabWidth": 2,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"endOfLine": "lf",
|
"endOfLine": "lf",
|
||||||
"importOrder": ["^[./]", ".*", ".scss$"],
|
"plugins": ["@ianvs/prettier-plugin-sort-imports"],
|
||||||
"importOrderSeparation": true,
|
"importOrder": ["<BUILTIN_MODULES>", "<THIRD_PARTY_MODULES>", "", "^[./]"],
|
||||||
"importOrderSortSpecifiers": true
|
"importOrderTypeScriptVersion": "5.0.0"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
definitions.d.ts
vendored
12
definitions.d.ts
vendored
|
|
@ -1,14 +1,6 @@
|
||||||
declare module '*.wgsl' {
|
declare module '*.wgsl?raw' {
|
||||||
const content: string;
|
const content: string;
|
||||||
export default content;
|
export default content;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '*.svg' {
|
declare const __BUILD_DATE__: number;
|
||||||
const content: string;
|
|
||||||
export default content;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module '*.html' {
|
|
||||||
const content: string;
|
|
||||||
export default content;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,33 @@
|
||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
|
|
||||||
<meta property="og:title" content="Just a bunch of blobs" />
|
|
||||||
<meta property="og:description" content="Discover my projects." />
|
|
||||||
<meta property="og:url" content="https://schmelczer.dev" />
|
|
||||||
|
|
||||||
<meta property="og:image:width" content="1920" />
|
|
||||||
<meta property="og:image:height" content="1920" />
|
|
||||||
<meta property="og:image" content="https://schmelczer.dev/og-image.jpg" />
|
|
||||||
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
|
||||||
<link rel="icon" href="favicon.ico" type="image/x-icon" />
|
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
|
||||||
<meta
|
|
||||||
name="description"
|
|
||||||
content="I'm Andras Schmelczer, and this is my portfolio. Discover some of my projects. I'm passionate about solving challenging problems and designing large-scale systems, especially in the context of machine learning."
|
|
||||||
/>
|
|
||||||
<meta
|
<meta
|
||||||
name="viewport"
|
name="viewport"
|
||||||
content="width=device-width,initial-scale=1,viewport-fit=cover"
|
content="width=device-width,initial-scale=1,viewport-fit=cover"
|
||||||
/>
|
/>
|
||||||
<meta name="theme-color" content="#b7455e" />
|
<meta name="theme-color" content="#b7455e" />
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="A WebGPU agent simulation: a million blobs leave trails, infect each other across generations, and react to your brush."
|
||||||
|
/>
|
||||||
|
|
||||||
|
<meta property="og:title" content="Just a bunch of blobs" />
|
||||||
|
<meta
|
||||||
|
property="og:description"
|
||||||
|
content="A WebGPU agent simulation: a million blobs leave trails, infect each other across generations, and react to your brush."
|
||||||
|
/>
|
||||||
|
<meta property="og:url" content="https://schmelczer.dev" />
|
||||||
|
<meta property="og:image" content="https://schmelczer.dev/og-image.jpg" />
|
||||||
|
<meta property="og:image:width" content="1920" />
|
||||||
|
<meta property="og:image:height" content="1920" />
|
||||||
|
|
||||||
|
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||||
|
|
||||||
<title>Just a bunch of blobs</title>
|
<title>Just a bunch of blobs</title>
|
||||||
|
|
||||||
<link inline inline-asset="index.css" inline-asset-delete />
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main class="canvas-container">
|
<main class="canvas-container">
|
||||||
|
|
@ -36,17 +36,15 @@
|
||||||
<section class="errors-container">
|
<section class="errors-container">
|
||||||
<noscript>JavaScript is required for this website.</noscript>
|
<noscript>JavaScript is required for this website.</noscript>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- <div class="counters"><pre></pre></div> -->
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<aside>
|
<aside>
|
||||||
<nav class="buttons">
|
<nav class="buttons">
|
||||||
<button class="info"></button>
|
<button class="info" aria-label="About"></button>
|
||||||
<button class="maximize-full-screen"></button>
|
<button class="maximize-full-screen" aria-label="Enter fullscreen"></button>
|
||||||
<button class="minimize-full-screen"></button>
|
<button class="minimize-full-screen" aria-label="Exit fullscreen"></button>
|
||||||
<button class="settings"></button>
|
<button class="settings" aria-label="Settings"></button>
|
||||||
<button class="restart"></button>
|
<button class="restart" aria-label="Restart simulation"></button>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<main class="pages hidden info-page">
|
<main class="pages hidden info-page">
|
||||||
|
|
@ -67,6 +65,6 @@
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
</aside>
|
</aside>
|
||||||
<script inline inline-asset="index.js" inline-asset-delete></script>
|
<script type="module" src="/src/index.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
21622
package-lock.json
generated
21622
package-lock.json
generated
File diff suppressed because it is too large
Load diff
82
package.json
82
package.json
|
|
@ -1,61 +1,51 @@
|
||||||
{
|
{
|
||||||
"name": "webgpu-seed",
|
"name": "webgpu-seed",
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"description": "🔺 A simple hello triangle example introducing WebGPU.",
|
"private": true,
|
||||||
"main": "dist/main.js",
|
"type": "module",
|
||||||
|
"description": "A WebGPU-powered slime-mold-meets-territory-control simulation.",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "webpack serve --open --mode development",
|
"dev": "vite --host 0.0.0.0",
|
||||||
"lint": "eslint --fix \"src/**/*.ts\" && prettier --write \"src/**/*.(ts|scss|json|html)\"",
|
"build": "vite build",
|
||||||
"build": "webpack --mode production",
|
"preview": "vite preview",
|
||||||
|
"lint": "eslint --fix \"src/**/*.ts\" && prettier --write \"src/**/*.{ts,scss,json,html}\"",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
"update": "ncu"
|
"update": "ncu"
|
||||||
},
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/alaingalvan/webgpu-seed.git"
|
"url": "git+https://github.com/schmelczer/webgpu.git"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"webgpu",
|
"webgpu",
|
||||||
"webgl",
|
"simulation",
|
||||||
"example",
|
"physarum",
|
||||||
"seed",
|
"generative-art"
|
||||||
"types",
|
|
||||||
"typescript"
|
|
||||||
],
|
],
|
||||||
"author": "Alain Galvan",
|
"author": "Andras Schmelczer",
|
||||||
"license": "Unlicense",
|
"license": "Unlicense",
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/alaingalvan/webgpu-seed/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://github.com/alaingalvan/webgpu-seed#readme",
|
|
||||||
"devDependencies": {
|
|
||||||
"@webgpu/types": "^0.1.30",
|
|
||||||
"@trivago/prettier-plugin-sort-imports": "^3.3.0",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^5.38.0",
|
|
||||||
"css-loader": "^6.7.1",
|
|
||||||
"eslint": "^8.23.1",
|
|
||||||
"eslint-config-prettier": "^8.5.0",
|
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
|
||||||
"eslint-plugin-unused-imports": "^2.0.0",
|
|
||||||
"html-webpack-plugin": "^5.5.0",
|
|
||||||
"inline-source-webpack-plugin": "^2.0.1",
|
|
||||||
"mini-css-extract-plugin": "^2.6.1",
|
|
||||||
"npm-check-updates": "^16.3.2",
|
|
||||||
"prettier": "^2.7.1",
|
|
||||||
"resolve-url-loader": "^5.0.0",
|
|
||||||
"responsive-loader": "^3.1.1",
|
|
||||||
"sass": "^1.55.0",
|
|
||||||
"sass-loader": "^13.0.2",
|
|
||||||
"sharp": "^0.31.0",
|
|
||||||
"string-replace-loader": "^3.1.0",
|
|
||||||
"svg-inline-loader": "^0.8.2",
|
|
||||||
"terser-webpack-plugin": "^5.3.6",
|
|
||||||
"ts-loader": "^9.4.1",
|
|
||||||
"typescript": "^4.8.3",
|
|
||||||
"webpack": "^5.74.0",
|
|
||||||
"webpack-cli": "^4.10.0",
|
|
||||||
"webpack-dev-server": "^4.11.1"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"gl-matrix": "^3.4.3"
|
"gl-matrix": "^3.4.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^10.0.1",
|
||||||
|
"@ianvs/prettier-plugin-sort-imports": "^4.7.1",
|
||||||
|
"@types/node": "^25.6.0",
|
||||||
|
"@webgpu/types": "^0.1.69",
|
||||||
|
"eslint": "^10.3.0",
|
||||||
|
"eslint-config-prettier": "^10.1.8",
|
||||||
|
"eslint-plugin-prettier": "^5.5.5",
|
||||||
|
"eslint-plugin-unused-imports": "^4.4.1",
|
||||||
|
"globals": "^17.6.0",
|
||||||
|
"npm-check-updates": "^22.1.0",
|
||||||
|
"prettier": "^3.8.3",
|
||||||
|
"sass": "^1.99.0",
|
||||||
|
"typescript": "^6.0.3",
|
||||||
|
"typescript-eslint": "^8.59.1",
|
||||||
|
"vite": "^8.0.10",
|
||||||
|
"vite-plugin-singlefile": "^2.3.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,2 @@
|
||||||
// @ts-ignore: injected by webpack
|
export const isProduction: boolean = import.meta.env.PROD;
|
||||||
export const isProduction: boolean = __IS_PRODUCTION__;
|
export const lastEdit = new Date(__BUILD_DATE__);
|
||||||
// @ts-ignore: injected by webpack
|
|
||||||
export const lastEdit = new Date(__CURRENT_DATE__);
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
|
|
||||||
import { AgentGenerationPipeline } from '../pipelines/agents/agent-generation/agent-generation-pipeline';
|
import { AgentGenerationPipeline } from '../pipelines/agents/agent-generation/agent-generation-pipeline';
|
||||||
import { AgentPipeline } from '../pipelines/agents/agent-pipeline';
|
import { AgentPipeline } from '../pipelines/agents/agent-pipeline';
|
||||||
import { BrushPipeline } from '../pipelines/brush/brush-pipeline';
|
import { BrushPipeline } from '../pipelines/brush/brush-pipeline';
|
||||||
|
|
@ -13,8 +15,6 @@ import { sleep } from '../utils/sleep';
|
||||||
import { GamePresentation } from './game-presentation';
|
import { GamePresentation } from './game-presentation';
|
||||||
import { GameRules } from './game-rules';
|
import { GameRules } from './game-rules';
|
||||||
|
|
||||||
import { vec2 } from 'gl-matrix';
|
|
||||||
|
|
||||||
export default class GameLoop {
|
export default class GameLoop {
|
||||||
private readonly trailMapA: ResizableTexture;
|
private readonly trailMapA: ResizableTexture;
|
||||||
private readonly trailMapB: ResizableTexture;
|
private readonly trailMapB: ResizableTexture;
|
||||||
|
|
@ -170,7 +170,7 @@ export default class GameLoop {
|
||||||
);
|
);
|
||||||
document.documentElement.style.setProperty(
|
document.documentElement.style.setProperty(
|
||||||
'--accent-color',
|
'--accent-color',
|
||||||
`rgb(${accentColor.map((v: number) => v * 255).join(',')})`
|
`rgb(${accentColor[0] * 255},${accentColor[1] * 255},${accentColor[2] * 255})`
|
||||||
);
|
);
|
||||||
|
|
||||||
const deltaTime = this.deltaTimeCalculator.calculateDeltaTimeInSeconds(time);
|
const deltaTime = this.deltaTimeCalculator.calculateDeltaTimeInSeconds(time);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
|
import { vec3 } from 'gl-matrix';
|
||||||
|
|
||||||
import { settings } from '../settings';
|
import { settings } from '../settings';
|
||||||
import { hsl } from '../utils/hsl';
|
import { hsl } from '../utils/hsl';
|
||||||
import { last } from '../utils/last';
|
import { last } from '../utils/last';
|
||||||
import { Random } from '../utils/random';
|
import { Random } from '../utils/random';
|
||||||
|
|
||||||
import { vec3 } from 'gl-matrix';
|
|
||||||
|
|
||||||
const hues = [settings.startColorHue];
|
const hues = [settings.startColorHue];
|
||||||
|
|
||||||
for (let i = 0; i < 100; i++) {
|
for (let i = 0; i < 100; i++) {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
|
|
||||||
import { GenerationCounts } from '../pipelines/agents/agent-generation/generation-counts';
|
import { GenerationCounts } from '../pipelines/agents/agent-generation/generation-counts';
|
||||||
import { settings } from '../settings';
|
import { settings } from '../settings';
|
||||||
import { clamp, clamp01 } from '../utils/clamp';
|
import { clamp, clamp01 } from '../utils/clamp';
|
||||||
import { mix } from '../utils/mix';
|
import { mix } from '../utils/mix';
|
||||||
import { Random } from '../utils/random';
|
import { Random } from '../utils/random';
|
||||||
|
|
||||||
import { vec2 } from 'gl-matrix';
|
|
||||||
|
|
||||||
export interface SpawnAction {
|
export interface SpawnAction {
|
||||||
generation: number;
|
generation: number;
|
||||||
position: vec2;
|
position: vec2;
|
||||||
|
|
@ -76,8 +76,8 @@ export class GameRules {
|
||||||
this.lastSpawnAction = {
|
this.lastSpawnAction = {
|
||||||
generation: this.nextGenerationId,
|
generation: this.nextGenerationId,
|
||||||
position: vec2.fromValues(
|
position: vec2.fromValues(
|
||||||
Random.randomBetween(0, canvasSize.x),
|
Random.randomBetween(0, canvasSize[0]),
|
||||||
Random.randomBetween(0, canvasSize.y)
|
Random.randomBetween(0, canvasSize[1])
|
||||||
),
|
),
|
||||||
radius: this.currentSpawnRadius,
|
radius: this.currentSpawnRadius,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,9 @@ html > body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
cursor: url('../assets/icons/brush.svg') 0 24, auto;
|
cursor:
|
||||||
|
url('../assets/icons/brush.svg') 0 24,
|
||||||
|
auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .errors-container {
|
> .errors-container {
|
||||||
|
|
@ -107,7 +109,8 @@ html > body {
|
||||||
@include on-large-screen {
|
@include on-large-screen {
|
||||||
width: 0;
|
width: 0;
|
||||||
border-radius: 0 var(--border-radius) var(--border-radius) 0;
|
border-radius: 0 var(--border-radius) var(--border-radius) 0;
|
||||||
transition: background-color var(--transition-time),
|
transition:
|
||||||
|
background-color var(--transition-time),
|
||||||
width var(--transition-time);
|
width var(--transition-time);
|
||||||
left: calc(-1 * var(--small-margin));
|
left: calc(-1 * var(--small-margin));
|
||||||
height: 140%;
|
height: 140%;
|
||||||
|
|
@ -118,7 +121,8 @@ html > body {
|
||||||
@include on-small-screen {
|
@include on-small-screen {
|
||||||
height: 0;
|
height: 0;
|
||||||
border-radius: 0 0 var(--border-radius) var(--border-radius);
|
border-radius: 0 0 var(--border-radius) var(--border-radius);
|
||||||
transition: background-color var(--transition-time),
|
transition:
|
||||||
|
background-color var(--transition-time),
|
||||||
height var(--transition-time);
|
height var(--transition-time);
|
||||||
top: calc(-1 * var(--small-margin));
|
top: calc(-1 * var(--small-margin));
|
||||||
width: 140%;
|
width: 140%;
|
||||||
|
|
@ -130,7 +134,8 @@ html > body {
|
||||||
&::after {
|
&::after {
|
||||||
background-color: var(--accent-color);
|
background-color: var(--accent-color);
|
||||||
|
|
||||||
transition: transform var(--transition-time),
|
transition:
|
||||||
|
transform var(--transition-time),
|
||||||
background-color var(--transition-time);
|
background-color var(--transition-time);
|
||||||
|
|
||||||
-webkit-mask-repeat: no-repeat;
|
-webkit-mask-repeat: no-repeat;
|
||||||
|
|
@ -201,7 +206,9 @@ html > body {
|
||||||
|
|
||||||
&,
|
&,
|
||||||
> * {
|
> * {
|
||||||
transition: width var(--transition-time-long), height var(--transition-time-long);
|
transition:
|
||||||
|
width var(--transition-time-long),
|
||||||
|
height var(--transition-time-long);
|
||||||
|
|
||||||
@include on-large-screen {
|
@include on-large-screen {
|
||||||
width: max(500px, 10vw);
|
width: max(500px, 10vw);
|
||||||
|
|
|
||||||
23
src/index.ts
23
src/index.ts
|
|
@ -1,36 +1,19 @@
|
||||||
import '../assets/icons/info.svg';
|
|
||||||
import { isProduction, lastEdit } from './constants';
|
import { isProduction, lastEdit } from './constants';
|
||||||
import GameLoop from './game-loop/game-loop';
|
import GameLoop from './game-loop/game-loop';
|
||||||
import { GameRules } from './game-loop/game-rules';
|
import { GameRules } from './game-loop/game-rules';
|
||||||
|
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
import { CollapsiblePanelAnimator } from './page/collapsible-panel-animator';
|
import { CollapsiblePanelAnimator } from './page/collapsible-panel-animator';
|
||||||
import { FullScreenHandler } from './page/full-screen-handler';
|
import { FullScreenHandler } from './page/full-screen-handler';
|
||||||
import { MenuHider } from './page/menu-hider';
|
import { MenuHider } from './page/menu-hider';
|
||||||
import { setUpSettingsPage } from './page/set-up-settings-page';
|
import { setUpSettingsPage } from './page/set-up-settings-page';
|
||||||
import { SettingsSlider } from './page/settings-slider';
|
import { SettingsSlider } from './page/settings-slider';
|
||||||
import { resetSettings } from './settings';
|
import { resetSettings } from './settings';
|
||||||
import { applyArrayPlugins } from './utils/array';
|
|
||||||
import { DeltaTimeCalculator } from './utils/delta-time-calculator';
|
import { DeltaTimeCalculator } from './utils/delta-time-calculator';
|
||||||
import { ErrorHandler, Severity } from './utils/error-handler';
|
import { ErrorHandler, Severity } from './utils/error-handler';
|
||||||
import { initializeGpu } from './utils/graphics/initialize-gpu';
|
import { initializeGpu } from './utils/graphics/initialize-gpu';
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface Array<T> {
|
|
||||||
x: T;
|
|
||||||
y: T;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ReadonlyArray<T> {
|
|
||||||
x: T;
|
|
||||||
y: T;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Float32Array {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const elements = {
|
const elements = {
|
||||||
aside: document.querySelector('aside') as HTMLDivElement,
|
aside: document.querySelector('aside') as HTMLDivElement,
|
||||||
infoButton: document.querySelector('button.info') as HTMLButtonElement,
|
infoButton: document.querySelector('button.info') as HTMLButtonElement,
|
||||||
|
|
@ -57,8 +40,6 @@ const main = async () => {
|
||||||
let shouldStop = false;
|
let shouldStop = false;
|
||||||
let game: GameLoop | null = null;
|
let game: GameLoop | null = null;
|
||||||
|
|
||||||
applyArrayPlugins();
|
|
||||||
|
|
||||||
ErrorHandler.addOnErrorListener((error, _metadata) => {
|
ErrorHandler.addOnErrorListener((error, _metadata) => {
|
||||||
elements.errorContainer.innerHTML += `
|
elements.errorContainer.innerHTML += `
|
||||||
<pre class="${error.severity}">${error.message}</div>
|
<pre class="${error.severity}">${error.message}</div>
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,16 @@ export class FullScreenHandler {
|
||||||
// on full screen request, only apply it to the target
|
// on full screen request, only apply it to the target
|
||||||
if (e.key === 'F11') {
|
if (e.key === 'F11') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
FullScreenHandler.isInFullScreenMode()
|
if (FullScreenHandler.isInFullScreenMode()) {
|
||||||
? document.exitFullscreen()
|
document.exitFullscreen();
|
||||||
: target.requestFullscreen();
|
} else {
|
||||||
|
target.requestFullscreen();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
addEventListener('fullscreenchange', this.updateButtons.bind(this));
|
addEventListener('fullscreenchange', this.updateButtons.bind(this));
|
||||||
maximizeButton.addEventListener('click', target.requestFullscreen.bind(target));
|
maximizeButton.addEventListener('click', () => target.requestFullscreen());
|
||||||
minimizeButton.addEventListener('click', document.exitFullscreen.bind(document));
|
minimizeButton.addEventListener('click', () => document.exitFullscreen());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static isInFullScreenMode(): boolean {
|
public static isInFullScreenMode(): boolean {
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ import { getWorkgroupCounts } from '../../../utils/graphics/get-workgroup-counts
|
||||||
import { smartCompile } from '../../../utils/graphics/smart-compile';
|
import { smartCompile } from '../../../utils/graphics/smart-compile';
|
||||||
import { CommonState } from '../../common-state/common-state';
|
import { CommonState } from '../../common-state/common-state';
|
||||||
import { AGENT_SIZE_IN_BYTES } from './agent';
|
import { AGENT_SIZE_IN_BYTES } from './agent';
|
||||||
import countingShader from './agent-counting.wgsl';
|
import countingShader from './agent-counting.wgsl?raw';
|
||||||
import firstGenerationShader from './agent-first-generation.wgsl';
|
import firstGenerationShader from './agent-first-generation.wgsl?raw';
|
||||||
import agentSchema from './agent-schema.wgsl';
|
import agentSchema from './agent-schema.wgsl?raw';
|
||||||
import { GenerationCounts } from './generation-counts';
|
import { GenerationCounts } from './generation-counts';
|
||||||
|
|
||||||
export class AgentGenerationPipeline {
|
export class AgentGenerationPipeline {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
|
|
||||||
import { getWorkgroupCounts } from '../../utils/graphics/get-workgroup-counts';
|
import { getWorkgroupCounts } from '../../utils/graphics/get-workgroup-counts';
|
||||||
import { smartCompile } from '../../utils/graphics/smart-compile';
|
import { smartCompile } from '../../utils/graphics/smart-compile';
|
||||||
import { CommonState } from '../common-state/common-state';
|
import { CommonState } from '../common-state/common-state';
|
||||||
import agentSchme from './agent-generation/agent-schema.wgsl';
|
import agentSchme from './agent-generation/agent-schema.wgsl?raw';
|
||||||
import { AgentSettings } from './agent-settings';
|
import { AgentSettings } from './agent-settings';
|
||||||
import shader from './agent.wgsl';
|
import shader from './agent.wgsl?raw';
|
||||||
|
|
||||||
import { vec2 } from 'gl-matrix';
|
|
||||||
|
|
||||||
export class AgentPipeline {
|
export class AgentPipeline {
|
||||||
private static readonly WORKGROUP_SIZE = 64;
|
private static readonly WORKGROUP_SIZE = 64;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
|
|
||||||
import { clamp } from '../../utils/clamp';
|
import { clamp } from '../../utils/clamp';
|
||||||
import { smartCompile } from '../../utils/graphics/smart-compile';
|
import { smartCompile } from '../../utils/graphics/smart-compile';
|
||||||
import { last } from '../../utils/last';
|
import { last } from '../../utils/last';
|
||||||
import { CommonState } from '../common-state/common-state';
|
import { CommonState } from '../common-state/common-state';
|
||||||
import { BrushSettings } from './brush-settings';
|
import { BrushSettings } from './brush-settings';
|
||||||
import shader from './brush.wgsl';
|
import shader from './brush.wgsl?raw';
|
||||||
|
|
||||||
import { vec2 } from 'gl-matrix';
|
|
||||||
|
|
||||||
export class BrushPipeline {
|
export class BrushPipeline {
|
||||||
private static readonly UNIFORM_COUNT = 2;
|
private static readonly UNIFORM_COUNT = 2;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { generateNoise } from '../../utils/graphics/noise';
|
|
||||||
|
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
|
|
||||||
|
import { generateNoise } from '../../utils/graphics/noise';
|
||||||
|
|
||||||
export class CommonState {
|
export class CommonState {
|
||||||
private static readonly UNIFORM_COUNT = 4;
|
private static readonly UNIFORM_COUNT = 4;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { smartCompile } from '../../utils/graphics/smart-compile';
|
|
||||||
import shader from './copy.wgsl';
|
|
||||||
|
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
|
|
||||||
|
import { smartCompile } from '../../utils/graphics/smart-compile';
|
||||||
|
import shader from './copy.wgsl?raw';
|
||||||
|
|
||||||
export class CopyPipeline {
|
export class CopyPipeline {
|
||||||
private static readonly UNIFORM_COUNT = 2;
|
private static readonly UNIFORM_COUNT = 2;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { setUpFullScreenQuad } from '../../utils/graphics/full-screen-quad';
|
import { setUpFullScreenQuad } from '../../utils/graphics/full-screen-quad';
|
||||||
import { smartCompile } from '../../utils/graphics/smart-compile';
|
import { smartCompile } from '../../utils/graphics/smart-compile';
|
||||||
import { CommonState } from '../common-state/common-state';
|
import { CommonState } from '../common-state/common-state';
|
||||||
import shader from './diffuse.wgsl';
|
import shader from './diffuse.wgsl?raw';
|
||||||
import { DiffusionSettings } from './diffusion-settings';
|
import { DiffusionSettings } from './diffusion-settings';
|
||||||
|
|
||||||
export class DiffusionPipeline {
|
export class DiffusionPipeline {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
|
import { vec3 } from 'gl-matrix';
|
||||||
|
|
||||||
import { setUpFullScreenQuad } from '../../utils/graphics/full-screen-quad';
|
import { setUpFullScreenQuad } from '../../utils/graphics/full-screen-quad';
|
||||||
import { smartCompile } from '../../utils/graphics/smart-compile';
|
import { smartCompile } from '../../utils/graphics/smart-compile';
|
||||||
import { CommonState } from '../common-state/common-state';
|
import { CommonState } from '../common-state/common-state';
|
||||||
import { RenderSettings } from './render-settings';
|
import { RenderSettings } from './render-settings';
|
||||||
import shader from './render.wgsl';
|
import shader from './render.wgsl?raw';
|
||||||
|
|
||||||
import { vec3 } from 'gl-matrix';
|
|
||||||
|
|
||||||
export class RenderPipeline {
|
export class RenderPipeline {
|
||||||
private static readonly UNIFORM_COUNT = 13;
|
private static readonly UNIFORM_COUNT = 13;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: local(''),
|
src:
|
||||||
|
local(''),
|
||||||
url('../../assets/fonts/comfortaa-v40-latin-regular.woff2') format('woff2'),
|
url('../../assets/fonts/comfortaa-v40-latin-regular.woff2') format('woff2'),
|
||||||
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||||
url('../../assets/fonts/comfortaa-v40-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
url('../../assets/fonts/comfortaa-v40-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||||
|
|
@ -16,7 +17,8 @@
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: local(''),
|
src:
|
||||||
|
local(''),
|
||||||
url('../../assets/fonts/open-sans-v34-latin-regular.woff2') format('woff2'),
|
url('../../assets/fonts/open-sans-v34-latin-regular.woff2') format('woff2'),
|
||||||
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||||
url('../../assets/fonts/open-sans-v34-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
url('../../assets/fonts/open-sans-v34-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,9 @@ $breakpoint-width: 600px !default;
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin title-font() {
|
@mixin title-font() {
|
||||||
font: 400 3rem 'Comfortaa', sans-serif;
|
font:
|
||||||
|
400 3rem 'Comfortaa',
|
||||||
|
sans-serif;
|
||||||
color: var(--normal-text-color);
|
color: var(--normal-text-color);
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
|
||||||
|
|
@ -76,20 +78,26 @@ $breakpoint-width: 600px !default;
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin sub-title-font() {
|
@mixin sub-title-font() {
|
||||||
font: 400 1.75rem 'Comfortaa', sans-serif;
|
font:
|
||||||
|
400 1.75rem 'Comfortaa',
|
||||||
|
sans-serif;
|
||||||
color: var(--normal-text-color);
|
color: var(--normal-text-color);
|
||||||
hyphens: auto;
|
hyphens: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin main-font() {
|
@mixin main-font() {
|
||||||
font: 400 1.1rem 'Open Sans', sans-serif;
|
font:
|
||||||
|
400 1.1rem 'Open Sans',
|
||||||
|
sans-serif;
|
||||||
color: var(--normal-text-color);
|
color: var(--normal-text-color);
|
||||||
line-height: 1.8;
|
line-height: 1.8;
|
||||||
hyphens: auto;
|
hyphens: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin special-text-font() {
|
@mixin special-text-font() {
|
||||||
font: 400 1rem 'Open Sans', sans-serif;
|
font:
|
||||||
|
400 1rem 'Open Sans',
|
||||||
|
sans-serif;
|
||||||
color: var(--special-text-color);
|
color: var(--special-text-color);
|
||||||
hyphens: auto;
|
hyphens: auto;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
|
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
const setIndexAlias = (name: string, index: number, type: any) => {
|
|
||||||
if (!Object.prototype.hasOwnProperty.call(type.prototype, name)) {
|
|
||||||
Object.defineProperty(type.prototype, name, {
|
|
||||||
get() {
|
|
||||||
return this[index];
|
|
||||||
},
|
|
||||||
set(value) {
|
|
||||||
this[index] = value;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const applyArrayPlugins = () => {
|
|
||||||
setIndexAlias('x', 0, Array);
|
|
||||||
setIndexAlias('y', 1, Array);
|
|
||||||
setIndexAlias('x', 0, Float32Array);
|
|
||||||
setIndexAlias('y', 1, Float32Array);
|
|
||||||
};
|
|
||||||
|
|
@ -1,14 +1,17 @@
|
||||||
import { CopyPipeline } from '../../pipelines/copy/copy-pipeline';
|
|
||||||
|
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
|
|
||||||
|
import { CopyPipeline } from '../../pipelines/copy/copy-pipeline';
|
||||||
|
|
||||||
export class ResizableTexture {
|
export class ResizableTexture {
|
||||||
private texture: GPUTexture;
|
private texture: GPUTexture;
|
||||||
private textureView: GPUTextureView;
|
private textureView: GPUTextureView;
|
||||||
private readonly copyPipeline: CopyPipeline;
|
private readonly copyPipeline: CopyPipeline;
|
||||||
private size: vec2 | null = null;
|
private size: vec2 | null = null;
|
||||||
|
|
||||||
public constructor(private readonly device: GPUDevice, size: vec2) {
|
public constructor(
|
||||||
|
private readonly device: GPUDevice,
|
||||||
|
size: vec2
|
||||||
|
) {
|
||||||
this.copyPipeline = new CopyPipeline(this.device);
|
this.copyPipeline = new CopyPipeline(this.device);
|
||||||
this.resize(size);
|
this.resize(size);
|
||||||
}
|
}
|
||||||
|
|
@ -21,8 +24,8 @@ export class ResizableTexture {
|
||||||
const newTexture = this.device.createTexture({
|
const newTexture = this.device.createTexture({
|
||||||
format: 'rgba16float',
|
format: 'rgba16float',
|
||||||
size: {
|
size: {
|
||||||
width: size.x,
|
width: size[0],
|
||||||
height: size.y,
|
height: size[1],
|
||||||
},
|
},
|
||||||
usage:
|
usage:
|
||||||
GPUTextureUsage.STORAGE_BINDING |
|
GPUTextureUsage.STORAGE_BINDING |
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { rgb } from './rgb';
|
|
||||||
|
|
||||||
import { vec3 } from 'gl-matrix';
|
import { vec3 } from 'gl-matrix';
|
||||||
|
|
||||||
|
import { rgb } from './rgb';
|
||||||
|
|
||||||
export const hsl = (hue: number, saturation: number, lightness: number): vec3 => {
|
export const hsl = (hue: number, saturation: number, lightness: number): vec3 => {
|
||||||
hue /= 360;
|
hue /= 360;
|
||||||
saturation /= 100;
|
saturation /= 100;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ export const persist = <T extends Record<string, number>>(wrapee: T): T => {
|
||||||
const keys = Object.keys(wrapee);
|
const keys = Object.keys(wrapee);
|
||||||
keys.sort();
|
keys.sort();
|
||||||
|
|
||||||
const keysToShortKeys = Object.fromEntries(keys.map((key, i) => [key, key]));
|
const keysToShortKeys = Object.fromEntries(keys.map((key) => [key, key]));
|
||||||
|
|
||||||
const params = new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
const newParams = new URLSearchParams();
|
const newParams = new URLSearchParams();
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,24 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES6",
|
"target": "ES2022",
|
||||||
"lib": [
|
"module": "ESNext",
|
||||||
"es2017",
|
"moduleResolution": "bundler",
|
||||||
"es2017.object",
|
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||||
"es2017.sharedmemory",
|
"types": ["@webgpu/types", "vite/client"],
|
||||||
"es2016",
|
|
||||||
"es2016.array.include",
|
"isolatedModules": true,
|
||||||
"es2015",
|
"noEmit": true,
|
||||||
"es2015.core",
|
"skipLibCheck": true,
|
||||||
"es2015.promise",
|
"resolveJsonModule": true,
|
||||||
"es2015.collection",
|
|
||||||
"es5",
|
|
||||||
"dom"
|
|
||||||
],
|
|
||||||
"module": "commonjs",
|
|
||||||
"declaration": false,
|
|
||||||
"noImplicitAny": false,
|
|
||||||
"removeComments": false,
|
|
||||||
"emitDecoratorMetadata": true,
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"noEmitHelpers": false,
|
|
||||||
"sourceMap": true,
|
|
||||||
"strictNullChecks": false,
|
|
||||||
"jsx": "react",
|
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"allowSyntheticDefaultImports": true
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"strictNullChecks": false,
|
||||||
|
"useUnknownInCatchVariables": false,
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["src/**/*", "definitions.d.ts", "vite.config.ts"]
|
||||||
"definitions.d.ts",
|
|
||||||
"src/**/*",
|
|
||||||
"node_modules/@webgpu/types/**/*"
|
|
||||||
],
|
|
||||||
"compileOnSave": false,
|
|
||||||
"buildOnSave": false
|
|
||||||
}
|
}
|
||||||
17
vite.config.ts
Normal file
17
vite.config.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import { viteSingleFile } from 'vite-plugin-singlefile';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [viteSingleFile()],
|
||||||
|
define: {
|
||||||
|
__BUILD_DATE__: Date.now(),
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
target: 'es2022',
|
||||||
|
cssCodeSplit: false,
|
||||||
|
assetsInlineLimit: Number.MAX_SAFE_INTEGER,
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
open: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -1,95 +0,0 @@
|
||||||
const path = require('path');
|
|
||||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
|
||||||
const TerserPlugin = require('terser-webpack-plugin');
|
|
||||||
const InlineSourceWebpackPlugin = require('inline-source-webpack-plugin');
|
|
||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
|
||||||
const DefinePlugin = require('webpack').DefinePlugin;
|
|
||||||
|
|
||||||
module.exports = (env, argv) => ({
|
|
||||||
devtool: argv.mode === 'development' ? 'inline-source-map' : false,
|
|
||||||
entry: {
|
|
||||||
index: './src/index.ts',
|
|
||||||
},
|
|
||||||
watchOptions: {
|
|
||||||
ignored: '**/node_modules',
|
|
||||||
},
|
|
||||||
optimization: {
|
|
||||||
minimizer: [
|
|
||||||
new TerserPlugin({
|
|
||||||
terserOptions: {
|
|
||||||
module: true,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
template: './src/index.html',
|
|
||||||
}),
|
|
||||||
new MiniCssExtractPlugin(),
|
|
||||||
argv.mode === 'production' &&
|
|
||||||
new InlineSourceWebpackPlugin({
|
|
||||||
compress: true,
|
|
||||||
}),
|
|
||||||
new DefinePlugin({
|
|
||||||
__CURRENT_DATE__: Date.now(),
|
|
||||||
__IS_PRODUCTION__: argv.mode === 'production',
|
|
||||||
}),
|
|
||||||
].filter((v) => v),
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
test: /\.svg$/i,
|
|
||||||
type: 'asset/inline',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.woff2?$/i,
|
|
||||||
type: 'asset/resource',
|
|
||||||
generator: {
|
|
||||||
filename: '[hash:8][ext]',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\/no-change\//i,
|
|
||||||
type: 'asset/resource',
|
|
||||||
generator: {
|
|
||||||
filename: '[name][ext]',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.wgsl$/i,
|
|
||||||
type: 'asset/source',
|
|
||||||
generator: {
|
|
||||||
filename: '[name][ext]',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.scss$/i,
|
|
||||||
use: [
|
|
||||||
MiniCssExtractPlugin.loader,
|
|
||||||
'css-loader',
|
|
||||||
'resolve-url-loader',
|
|
||||||
{
|
|
||||||
loader: 'sass-loader',
|
|
||||||
options: {
|
|
||||||
sourceMap: true, // required by resolve-url-loader
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.ts$/i,
|
|
||||||
use: 'ts-loader',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
extensions: ['.ts', '.js'],
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
clean: true,
|
|
||||||
filename: '[name].js',
|
|
||||||
path: path.resolve(__dirname, 'dist'),
|
|
||||||
publicPath: '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue