Skip to content

v1.62.2 #783

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ jobs:
node-version: "22"
- uses: pnpm/action-setup@v3
with:
version: 8
version: 10
- name: Install Playwright
run: |
pnpm add -g playwright@1.45.2
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
name: Install pnpm
id: pnpm-install
with:
version: 8
version: 10
- name: Install Dependencies
run: pnpm install
- name: Build shared
Expand Down
3 changes: 1 addition & 2 deletions app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "app",
"version": "1.62.1",
"version": "1.62.2",
"main": "module/module.js",
"license": "MIT",
"scripts": {
Expand Down Expand Up @@ -196,7 +196,6 @@
"minimist": "^1.2.5",
"msw": "^1.2.3",
"playwright": "1.45.2",
"react@latest": "link:@@testing-library/react@latest",
"source-map-explorer": "^2.5.2",
"supabase": "^1.82.1",
"tailwindcss": "^3.2.6",
Expand Down
Binary file added app/public/images/watermark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions app/public/images/watermark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
133 changes: 91 additions & 42 deletions app/src/components/LoadTemplateDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export function LoadTemplateDialog() {
const [layout, setLayout] = useState(true);
// Whether to load the content or not
const [replaceContent, setReplaceContent] = useState(getContentInitialValue);
const [showConfirmation, setShowConfirmation] = useState(false);
const disabled = !layout && !replaceContent;

/**
Expand All @@ -39,14 +40,19 @@ export function LoadTemplateDialog() {
setTemplate(null);
setLayout(true);
setReplaceContent(getContentInitialValue);
setShowConfirmation(false);
}, []);

const load = useCallback(async () => {
if (replaceContent && !showConfirmation) {
setShowConfirmation(true);
return;
}
loadTemplate(template, replaceContent, () => {
reset();
setOpen(false);
});
}, [template, replaceContent, reset]);
}, [template, replaceContent, reset, showConfirmation]);

return (
<Dialog.Root
Expand Down Expand Up @@ -76,7 +82,6 @@ export function LoadTemplateDialog() {
<div className="grid gap-1 sm:flex justify-between items-baseline">
<Dialog.Title className="text-xl font-bold flex items-baseline">
<PiShapesDuotone className="mr-2 translate-y-1" />

<span className="mr-4">
<Trans>Examples</Trans>
</span>
Expand All @@ -98,7 +103,7 @@ export function LoadTemplateDialog() {
<ArrowLeft className="mr-2" />
<Trans>Back</Trans>
</button>
<div className="grid sm:grid-cols-[2fr,1fr] gap-6">
<div className="grid sm:grid-cols-[minmax(0,2fr),minmax(0,1fr)] gap-6">
<div className="h-full w-full overflow-hidden rounded-lg shadow-md">
<img
src={`/template-screenshots/${template}.png`}
Expand All @@ -107,45 +112,89 @@ export function LoadTemplateDialog() {
/>
</div>
<div className="self-center grid gap-3">
<h2 className="text-lg mb-2">Options</h2>
<Option id="layout" checked={layout} set={setLayout}>
<Trans>Load layout and styles</Trans>
</Option>
<Option
id="content"
checked={replaceContent}
set={setReplaceContent}
>
<Trans>Load default content</Trans>
</Option>
<div
className={classNames(
"text-xs text-neutral-500 rounded transition-opacity duration-200 flex gap-1 justify-start items-center",
{
"opacity-100": replaceContent,
"opacity-0": !replaceContent,
}
)}
>
{replaceContent ? (
<>
<WarningCircle size={16} className="shrink-0" />
<Trans>This will replace the current content.</Trans>
</>
) : (
<>&nbsp;</>
)}
</div>
<Button2
color="blue"
disabled={disabled}
onClick={load}
className="mt-2"
data-session-activity="Load Template: Load"
data-template={template}
>
<Trans>Load</Trans>
</Button2>
{showConfirmation ? (
<>
<div className="grid gap-2 bg-yellow-50 dark:bg-yellow-900/20 p-4 rounded-lg border border-yellow-200 dark:border-yellow-800">
<div className="flex gap-2 items-center text-yellow-800 dark:text-yellow-200">
<WarningCircle size={20} className="shrink-0" />
<h2 className="font-semibold">
<Trans>Are you sure?</Trans>
</h2>
</div>
<p className="text-sm text-yellow-800/90 dark:text-yellow-200/90">
<Trans>
This will replace your current chart content with
the template content.
</Trans>
</p>
</div>
<div className="grid gap-2">
<Button2
color="blue"
onClick={load}
data-session-activity="Load Template: Confirm Load"
data-template={template}
className="w-full"
>
<Trans>Yes, Replace Content</Trans>
</Button2>
<Button2
color="default"
onClick={() => {
setShowConfirmation(false);
}}
data-session-activity="Load Template: Cancel Confirmation"
className="w-full"
>
<Trans>Cancel</Trans>
</Button2>
</div>
</>
) : (
<>
<h2 className="text-lg mb-2">Options</h2>
<Option id="layout" checked={layout} set={setLayout}>
<Trans>Load layout and styles</Trans>
</Option>
<Option
id="content"
checked={replaceContent}
set={setReplaceContent}
>
<Trans>Load default content</Trans>
</Option>
<div
className={classNames(
"text-xs text-neutral-500 rounded transition-opacity duration-200 flex gap-1 justify-start items-center",
{
"opacity-100": replaceContent,
"opacity-0": !replaceContent,
}
)}
>
{replaceContent ? (
<>
<WarningCircle size={16} className="shrink-0" />
<Trans>
This will replace the current content.
</Trans>
</>
) : (
<>&nbsp;</>
)}
</div>
<Button2
color="blue"
disabled={disabled}
onClick={load}
className="mt-2"
data-session-activity="Load Template: Load"
data-template={template}
>
<Trans>Load</Trans>
</Button2>
</>
)}
</div>
</div>
</div>
Expand Down
59 changes: 47 additions & 12 deletions app/src/components/downloads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ import { saveAs } from "file-saver";
import { UNAUTH_IMG_SCALE } from "../lib/constants";
import { cytoscape } from "../lib/cytoscape";
import { getBackground } from "../lib/toTheme";
import {
WATERMARK_BASE64,
WATERMARK_ORIGINAL_WIDTH,
WATERMARK_ORIGINAL_HEIGHT,
WATERMARK_WIDTH_PERCENTAGE,
WATERMARK_MARGIN,
} from "../lib/constants";

// padding, gets divided in half
const PADDING = 60;
Expand Down Expand Up @@ -205,17 +212,21 @@ export async function getCanvas({
// take the blob and draw it on center of canvas
const img = new Image();
img.src = window.URL.createObjectURL(blob);

return new Promise((resolve) => {
img.onload = () => {
img.onload = async () => {
ctx.drawImage(img, PADDING / 2, PADDING / 2);
window.URL.revokeObjectURL(img.src);
// add watermark
if (watermark)
addWatermark({

// add watermark if needed
if (watermark) {
await addWatermark({
ctx,
width: canvas.width,
height: canvas.height,
});
}

resolve({
canvas,
type,
Expand All @@ -237,14 +248,38 @@ async function addWatermark({
width: number;
height: number;
}) {
// get a size that is 3% of the canvas height
const heightRelativeSize = Math.floor(height * 0.03);
const widthRelativeSize = Math.floor(width * 0.05);
// take the smaller of the two
const size = Math.min(heightRelativeSize, widthRelativeSize);
ctx.font = `${Math.floor(size)}px Helvetica`;
ctx.fillStyle = "#000000";
ctx.fillText("flowchart.fun", 5, height - size / 2);
return new Promise<void>((resolve) => {
// Create a new image for the watermark
const watermarkImage = new Image();

// Set up image load handler
watermarkImage.onload = () => {
// Calculate watermark dimensions
const targetWidth = Math.floor(width * WATERMARK_WIDTH_PERCENTAGE);
const scale = targetWidth / WATERMARK_ORIGINAL_WIDTH;
const targetHeight = Math.floor(WATERMARK_ORIGINAL_HEIGHT * scale);

// Position watermark in bottom-left corner with margin
const x = WATERMARK_MARGIN;
const y = height - targetHeight - WATERMARK_MARGIN;

// Draw watermark with calculated dimensions
ctx.globalAlpha = 0.8; // Adjust transparency if needed
ctx.drawImage(watermarkImage, x, y, targetWidth, targetHeight);
ctx.globalAlpha = 1.0; // Reset transparency

resolve();
};

// Handle loading errors
watermarkImage.onerror = () => {
console.error("Failed to load watermark image");
resolve(); // Resolve anyway to not block export
};

// Set image source from base64
watermarkImage.src = `data:image/png;base64,${WATERMARK_BASE64}`;
});
}

export function downloadCanvas({
Expand Down
Loading
Loading