mirror of
https://github.com/imgfloat/server.git
synced 2026-02-05 03:39:26 +00:00
Add additional marketplace options on launch
This commit is contained in:
6
.gitattributes
vendored
6
.gitattributes
vendored
@@ -15,3 +15,9 @@ doc/raw.png filter=lfs diff=lfs merge=lfs -text
|
|||||||
doc/raw.xcf filter=lfs diff=lfs merge=lfs -text
|
doc/raw.xcf filter=lfs diff=lfs merge=lfs -text
|
||||||
dev/marketplace-scripts/rotating-logo/attachments/rotate.png filter=lfs diff=lfs merge=lfs -text
|
dev/marketplace-scripts/rotating-logo/attachments/rotate.png filter=lfs diff=lfs merge=lfs -text
|
||||||
dev/marketplace-scripts/rotating-logo/logo.png filter=lfs diff=lfs merge=lfs -text
|
dev/marketplace-scripts/rotating-logo/logo.png filter=lfs diff=lfs merge=lfs -text
|
||||||
|
doc/marketplace-scripts/checkerboard-test/logo.png filter=lfs diff=lfs merge=lfs -text
|
||||||
|
doc/marketplace-scripts/circle-grid/logo.png filter=lfs diff=lfs merge=lfs -text
|
||||||
|
doc/marketplace-scripts/color-bars/logo.png filter=lfs diff=lfs merge=lfs -text
|
||||||
|
doc/marketplace-scripts/mobius-strip/logo.png filter=lfs diff=lfs merge=lfs -text
|
||||||
|
doc/marketplace-scripts/rotating-logo/attachments/rotate.png filter=lfs diff=lfs merge=lfs -text
|
||||||
|
doc/marketplace-scripts/rotating-logo/logo.png filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|||||||
4
doc/marketplace-scripts/checkerboard-test/metadata.json
Normal file
4
doc/marketplace-scripts/checkerboard-test/metadata.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "Checkerboard test",
|
||||||
|
"description": "Animated checkerboard pattern with a moving offset to spot scaling artifacts."
|
||||||
|
}
|
||||||
30
doc/marketplace-scripts/checkerboard-test/source.js
Normal file
30
doc/marketplace-scripts/checkerboard-test/source.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
function init() {}
|
||||||
|
|
||||||
|
function tick(context, state) {
|
||||||
|
const { ctx, width, height, deltaMs } = context;
|
||||||
|
if (!ctx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const speed = 0.02;
|
||||||
|
const offset = ((state.offset || 0) + (deltaMs || 0) * speed) % 40;
|
||||||
|
state.offset = offset;
|
||||||
|
|
||||||
|
const squareSize = 40;
|
||||||
|
ctx.clearRect(0, 0, width, height);
|
||||||
|
for (let y = -squareSize; y < height + squareSize; y += squareSize) {
|
||||||
|
for (let x = -squareSize; x < width + squareSize; x += squareSize) {
|
||||||
|
const isDark = ((x + y) / squareSize) % 2 === 0;
|
||||||
|
ctx.fillStyle = isDark ? "#101828" : "#e2e8f0";
|
||||||
|
ctx.fillRect(x + offset, y + offset, squareSize, squareSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.strokeStyle = "#ef4444";
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(width / 2, 0);
|
||||||
|
ctx.lineTo(width / 2, height);
|
||||||
|
ctx.moveTo(0, height / 2);
|
||||||
|
ctx.lineTo(width, height / 2);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
4
doc/marketplace-scripts/circle-grid/metadata.json
Normal file
4
doc/marketplace-scripts/circle-grid/metadata.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "Circle grid",
|
||||||
|
"description": "Concentric circles and calibration marks to test geometry and focus."
|
||||||
|
}
|
||||||
42
doc/marketplace-scripts/circle-grid/source.js
Normal file
42
doc/marketplace-scripts/circle-grid/source.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
function init() {}
|
||||||
|
|
||||||
|
function tick(context, state) {
|
||||||
|
const { ctx, width, height, deltaMs } = context;
|
||||||
|
if (!ctx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ctx.clearRect(0, 0, width, height);
|
||||||
|
|
||||||
|
const centerX = width / 2;
|
||||||
|
const centerY = height / 2;
|
||||||
|
const maxRadius = Math.min(width, height) * 0.45;
|
||||||
|
const pulse = 0.02 * (deltaMs || 0);
|
||||||
|
state.phase = (state.phase || 0) + pulse;
|
||||||
|
|
||||||
|
ctx.strokeStyle = "#38bdf8";
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
for (let radius = maxRadius; radius > 0; radius -= maxRadius / 6) {
|
||||||
|
ctx.globalAlpha = 0.2 + (radius / maxRadius) * 0.6;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
ctx.globalAlpha = 1;
|
||||||
|
|
||||||
|
ctx.strokeStyle = "#22c55e";
|
||||||
|
ctx.lineWidth = 3;
|
||||||
|
const sweep = (Math.sin(state.phase) + 1) / 2;
|
||||||
|
const sweepRadius = maxRadius * (0.3 + sweep * 0.7);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(centerX, centerY, sweepRadius, 0, Math.PI * 2);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.strokeStyle = "#f8fafc";
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(centerX, 0);
|
||||||
|
ctx.lineTo(centerX, height);
|
||||||
|
ctx.moveTo(0, centerY);
|
||||||
|
ctx.lineTo(width, centerY);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
BIN
doc/marketplace-scripts/color-bars/logo.png
LFS
Normal file
BIN
doc/marketplace-scripts/color-bars/logo.png
LFS
Normal file
Binary file not shown.
4
doc/marketplace-scripts/color-bars/metadata.json
Normal file
4
doc/marketplace-scripts/color-bars/metadata.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "Color bars",
|
||||||
|
"description": "Classic SMPTE-style color bars test pattern for verifying color reproduction."
|
||||||
|
}
|
||||||
38
doc/marketplace-scripts/color-bars/source.js
Normal file
38
doc/marketplace-scripts/color-bars/source.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
function init() {}
|
||||||
|
|
||||||
|
function tick(context) {
|
||||||
|
const { ctx, width, height } = context;
|
||||||
|
if (!ctx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ctx.clearRect(0, 0, width, height);
|
||||||
|
const topHeight = height * 0.7;
|
||||||
|
const barWidth = width / 7;
|
||||||
|
const colors = [
|
||||||
|
"#ffffff",
|
||||||
|
"#ffff00",
|
||||||
|
"#00ffff",
|
||||||
|
"#00ff00",
|
||||||
|
"#ff00ff",
|
||||||
|
"#ff0000",
|
||||||
|
"#0000ff",
|
||||||
|
];
|
||||||
|
colors.forEach((color, index) => {
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.fillRect(index * barWidth, 0, barWidth, topHeight);
|
||||||
|
});
|
||||||
|
|
||||||
|
const middleHeight = height * 0.15;
|
||||||
|
const middleColors = ["#0000ff", "#000000", "#ff00ff", "#000000", "#00ffff", "#000000", "#ffffff"];
|
||||||
|
middleColors.forEach((color, index) => {
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.fillRect(index * barWidth, topHeight, barWidth, middleHeight);
|
||||||
|
});
|
||||||
|
|
||||||
|
const bottomHeight = height - topHeight - middleHeight;
|
||||||
|
const bottomColors = ["#2b2b2b", "#ffffff", "#2b2b2b", "#00ffff", "#2b2b2b", "#ff00ff", "#2b2b2b"];
|
||||||
|
bottomColors.forEach((color, index) => {
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.fillRect(index * barWidth, topHeight + middleHeight, barWidth, bottomHeight);
|
||||||
|
});
|
||||||
|
}
|
||||||
BIN
doc/marketplace-scripts/mobius-strip/logo.png
LFS
Normal file
BIN
doc/marketplace-scripts/mobius-strip/logo.png
LFS
Normal file
Binary file not shown.
4
doc/marketplace-scripts/mobius-strip/metadata.json
Normal file
4
doc/marketplace-scripts/mobius-strip/metadata.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "Rotating Möbius strip",
|
||||||
|
"description": "Procedural Möbius strip rendering with rotation for geometry inspection."
|
||||||
|
}
|
||||||
72
doc/marketplace-scripts/mobius-strip/source.js
Normal file
72
doc/marketplace-scripts/mobius-strip/source.js
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
function init() {}
|
||||||
|
|
||||||
|
function tick(context, state) {
|
||||||
|
const { ctx, width, height, deltaMs } = context;
|
||||||
|
if (!ctx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const dt = (deltaMs || 16) * 0.001;
|
||||||
|
state.angle = (state.angle || 0) + dt * 0.6;
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, width, height);
|
||||||
|
|
||||||
|
const centerX = width / 2;
|
||||||
|
const centerY = height / 2;
|
||||||
|
const scale = Math.min(width, height) * 0.32;
|
||||||
|
|
||||||
|
const points = [];
|
||||||
|
const segments = 140;
|
||||||
|
const halfWidth = 0.35;
|
||||||
|
|
||||||
|
for (let i = 0; i <= segments; i += 1) {
|
||||||
|
const t = (i / segments) * Math.PI * 2;
|
||||||
|
const cosT = Math.cos(t);
|
||||||
|
const sinT = Math.sin(t);
|
||||||
|
|
||||||
|
for (const side of [-halfWidth, halfWidth]) {
|
||||||
|
const cosHalf = Math.cos(t / 2);
|
||||||
|
const sinHalf = Math.sin(t / 2);
|
||||||
|
const radius = 1 + (side * cosHalf) / 2;
|
||||||
|
const x = radius * cosT;
|
||||||
|
const y = radius * sinT;
|
||||||
|
const z = (side * sinHalf) / 2;
|
||||||
|
|
||||||
|
const y1 = y * Math.cos(state.angle) - z * Math.sin(state.angle);
|
||||||
|
const z1 = y * Math.sin(state.angle) + z * Math.cos(state.angle);
|
||||||
|
const x2 = x * Math.cos(state.angle * 0.7) - z1 * Math.sin(state.angle * 0.7);
|
||||||
|
const z2 = x * Math.sin(state.angle * 0.7) + z1 * Math.cos(state.angle * 0.7);
|
||||||
|
|
||||||
|
const depth = 2.6;
|
||||||
|
const perspective = depth / (depth - z2);
|
||||||
|
const screenX = centerX + x2 * scale * perspective;
|
||||||
|
const screenY = centerY + y1 * scale * perspective;
|
||||||
|
points.push({ screenX, screenY, depth: z2 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
for (let i = 0; i < points.length - 2; i += 2) {
|
||||||
|
const a = points[i];
|
||||||
|
const b = points[i + 1];
|
||||||
|
const brightness = Math.max(0.25, (a.depth + 1.5) / 3);
|
||||||
|
ctx.strokeStyle = `rgba(56, 189, 248, ${brightness})`;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(a.screenX, a.screenY);
|
||||||
|
ctx.lineTo(b.screenX, b.screenY);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.strokeStyle = "rgba(248, 250, 252, 0.7)";
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.beginPath();
|
||||||
|
for (let i = 0; i < points.length; i += 2) {
|
||||||
|
const p = points[i];
|
||||||
|
if (i === 0) {
|
||||||
|
ctx.moveTo(p.screenX, p.screenY);
|
||||||
|
} else {
|
||||||
|
ctx.lineTo(p.screenX, p.screenY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
BIN
doc/marketplace-scripts/rotating-logo/attachments/rotate.png
LFS
Normal file
BIN
doc/marketplace-scripts/rotating-logo/attachments/rotate.png
LFS
Normal file
Binary file not shown.
BIN
doc/marketplace-scripts/rotating-logo/logo.png
LFS
Normal file
BIN
doc/marketplace-scripts/rotating-logo/logo.png
LFS
Normal file
Binary file not shown.
@@ -643,6 +643,7 @@ public class ChannelDirectoryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
script.setAttachments(loadScriptAttachments(asset.getBroadcaster(), asset.getId(), null));
|
script.setAttachments(loadScriptAttachments(asset.getBroadcaster(), asset.getId(), null));
|
||||||
|
scriptAssetRepository.save(script);
|
||||||
AssetView view = AssetView.fromScript(asset.getBroadcaster(), asset, script);
|
AssetView view = AssetView.fromScript(asset.getBroadcaster(), asset, script);
|
||||||
messagingTemplate.convertAndSend(topicFor(targetBroadcaster), AssetEvent.created(targetBroadcaster, view));
|
messagingTemplate.convertAndSend(topicFor(targetBroadcaster), AssetEvent.created(targetBroadcaster, view));
|
||||||
return Optional.of(view);
|
return Optional.of(view);
|
||||||
|
|||||||
@@ -196,9 +196,9 @@ public class MarketplaceScriptSeedLoader {
|
|||||||
if (rootPath != null && !rootPath.isBlank()) {
|
if (rootPath != null && !rootPath.isBlank()) {
|
||||||
return Path.of(rootPath);
|
return Path.of(rootPath);
|
||||||
}
|
}
|
||||||
Path devPath = Path.of("dev", "marketplace-scripts");
|
Path docsPath = Path.of("doc", "marketplace-scripts");
|
||||||
if (Files.isDirectory(devPath)) {
|
if (Files.isDirectory(docsPath)) {
|
||||||
return devPath;
|
return docsPath;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -236,7 +236,8 @@
|
|||||||
|
|
||||||
.modal .modal-inner .marketplace-list {
|
.modal .modal-inner .marketplace-list {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
grid-template-columns: repeat(auto-fit, 240px);
|
||||||
|
grid-auto-rows: 240px;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
}
|
}
|
||||||
@@ -250,7 +251,8 @@
|
|||||||
border: 1px solid rgba(148, 163, 184, 0.3);
|
border: 1px solid rgba(148, 163, 184, 0.3);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background-color: rgba(15, 23, 42, 0.6);
|
background-color: rgba(15, 23, 42, 0.6);
|
||||||
min-height: 240px;
|
width: 240px;
|
||||||
|
height: 240px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal .modal-inner .marketplace-logo {
|
.modal .modal-inner .marketplace-logo {
|
||||||
@@ -273,11 +275,31 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal .modal-inner .marketplace-content strong {
|
||||||
|
display: block;
|
||||||
|
max-width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal .modal-inner .marketplace-content p {
|
.modal .modal-inner .marketplace-content p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: rgba(226, 232, 240, 0.8);
|
color: rgba(226, 232, 240, 0.8);
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal .modal-inner .marketplace-content small {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal .modal-inner .marketplace-actions {
|
.modal .modal-inner .marketplace-actions {
|
||||||
|
|||||||
@@ -1482,6 +1482,14 @@ export function createAdminConsole({
|
|||||||
|
|
||||||
function createPreviewElement(asset) {
|
function createPreviewElement(asset) {
|
||||||
if (isCodeAsset(asset)) {
|
if (isCodeAsset(asset)) {
|
||||||
|
if (asset.logoUrl) {
|
||||||
|
const img = document.createElement("img");
|
||||||
|
img.className = "asset-preview";
|
||||||
|
img.src = asset.logoUrl;
|
||||||
|
img.alt = asset.name || "Script logo";
|
||||||
|
img.loading = "lazy";
|
||||||
|
return img;
|
||||||
|
}
|
||||||
const icon = document.createElement("div");
|
const icon = document.createElement("div");
|
||||||
icon.className = "asset-preview code-icon";
|
icon.className = "asset-preview code-icon";
|
||||||
icon.innerHTML = '<i class="fa-solid fa-code" aria-hidden="true"></i>';
|
icon.innerHTML = '<i class="fa-solid fa-code" aria-hidden="true"></i>';
|
||||||
|
|||||||
Reference in New Issue
Block a user