Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
f0a689d
ui: add NodePanelHeader and wire to text/image; fix title padding and…
kaochannel154 Oct 23, 2025
300072e
image-gen: align layout with text-gen; move ModelPicker into PromptPa…
kaochannel154 Oct 24, 2025
e09f670
ui: extract GenerateCtaButton and adopt in text/image panels; unify m…
kaochannel154 Oct 24, 2025
78bddf4
generation-view: fix lightbox backdrop to use bg-background/95; size …
kaochannel154 Oct 24, 2025
52ddd5a
chore(biome): format image generation panel and fix unused var warning
kaochannel154 Oct 24, 2025
c7d5d36
fix(image-gen panel): use deleteNode for consistent deletion behavior…
kaochannel154 Oct 24, 2025
1626ca4
chore(biome): remove redundant Fragments, fix a11y in Lightbox, organ…
kaochannel154 Oct 24, 2025
7032d98
chore(biome): fix remaining lint warnings (imports, unused vars), org…
kaochannel154 Oct 24, 2025
a0f56d7
fix(biome): replace <img> with next/image (avatar, image-card); remov…
kaochannel154 Oct 24, 2025
6aaf58a
fix(biome): replace <img> with next/image in workspace tour; rename u…
kaochannel154 Oct 24, 2025
4574188
chore(knip): drop unused pluralize deps; replace tour <img> with next…
kaochannel154 Oct 24, 2025
c1882c5
chore: update pnpm-lock.yaml after removing unused deps (knip)
kaochannel154 Oct 24, 2025
466fcb0
Update docs/packages-license.md
github-actions[bot] Oct 24, 2025
d97b444
chore(knip): remove unused input-panel; fix unused export by prefix; …
kaochannel154 Oct 24, 2025
502a220
chore(knip): stop exporting unused useSourceCategories; comment out u…
kaochannel154 Oct 24, 2025
2ca9501
fix(ui): replace <img> with next/image in Lightbox; format files with…
kaochannel154 Oct 24, 2025
9da0d1a
chore(knip): remove unused image-generation source hook and utils exp…
kaochannel154 Oct 24, 2025
bf40ba3
chore(knip): remove unused utils.ts and stop re-export to satisfy kni…
kaochannel154 Oct 24, 2025
b756e36
fix(image): avoid double slash when building generated image URLs in …
kaochannel154 Oct 24, 2025
febe6da
chore(knip): remove type re-export and stop exporting types from imag…
kaochannel154 Oct 24, 2025
5390930
chore(knip): inline ConnectedSource type into hook and delete unused …
kaochannel154 Oct 24, 2025
cd4ac7a
Image gen: add tier-based gating in ModelPicker (gray out + disable);…
kaochannel154 Oct 27, 2025
55421dc
fix: resolve merge conflicts in ModelPicker and WebPage panel
kaochannel154 Oct 28, 2025
5712004
Update docs/packages-license.md
github-actions[bot] Oct 28, 2025
858ff1e
refactor: clean up image generation panel code
kaochannel154 Oct 28, 2025
7e99fef
chore(knip): remove unused use-model-eligibility hook
kaochannel154 Oct 28, 2025
350a3b6
revert: keep image pathname with leading slash per PR review (UI-only)
kaochannel154 Oct 29, 2025
f5ebc1b
merge: resolve conflict in docs/packages-license.md by taking main (U…
kaochannel154 Oct 29, 2025
25cd72f
Update docs/packages-license.md
github-actions[bot] Oct 29, 2025
8208db9
Update docs link to webpage node
shige Oct 29, 2025
2ad3c15
Update docs link to GitHub vector store node
shige Oct 29, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Button } from "@giselle-internal/ui/button";
import { SiGithub } from "@icons-pack/react-simple-icons";
import type { components } from "@octokit/openapi-types";
import { TriangleAlertIcon } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { GitHubAppInstallButton } from "@/packages/components/github-app-install-button";
Expand Down Expand Up @@ -165,9 +166,11 @@ function Installation({ installation }: InstallationProps) {
<div className="rounded-lg px-4 pt-4 pb-6 flex flex-col bg-gradient-to-b from-[#202530] to-[#12151f] border border-border-muted shadow-[0_2px_8px_rgba(5,10,20,0.4),0_1px_2px_rgba(0,0,0,0.3)]">
<div className="flex items-center gap-x-2">
{avatarUrl && (
<img
<Image
src={avatarUrl}
alt={displayName}
width={24}
height={24}
className="w-6 h-6 rounded-full"
/>
)}
Expand Down
24 changes: 1 addition & 23 deletions docs/packages-license.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@


## Summary
* 844 MIT
* 842 MIT
* 186 Apache 2.0
* 46 ISC
* 27 New BSD
Expand Down Expand Up @@ -4658,17 +4658,6 @@ LGPL-3.0-or-later permitted



<a name="@types/pluralize"></a>
### @types/pluralize v0.0.33
####

##### Paths
* /home/runner/work/giselle/giselle

<a href="http://opensource.org/licenses/mit-license">MIT</a> permitted



<a name="@types/pngjs"></a>
### @types/pngjs v6.0.5
####
Expand Down Expand Up @@ -9832,17 +9821,6 @@ BlueOak-1.0.0 permitted



<a name="pluralize"></a>
### pluralize v8.0.0
####

##### Paths
* /home/runner/work/giselle/giselle

<a href="http://opensource.org/licenses/mit-license">MIT</a> permitted



<a name="pngjs"></a>
### pngjs v7.0.0
####
Expand Down
59 changes: 29 additions & 30 deletions internal-packages/ui/components/model-picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -186,36 +186,35 @@ export function ModelPicker({
{group.label}
</div>
) : null}
{group.models.map((m) => (
<button
key={`${group.provider}-${m.id}`}
type="button"
disabled={m.disabled}
onClick={() => {
if (m.disabled) return;
onSelect(group.provider, m.id);
setOpen(false);
}}
className={clsx(
"flex gap-[12px] items-center p-[4px] rounded-[4px] text-left",
"hover:bg-white/5 focus:bg-white/5 cursor-pointer",
"disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-transparent disabled:focus:bg-transparent",
)}
title={
m.disabled
? (m.disabledReason ??
"Upgrade to Pro to use this model.")
: undefined
}
>
<div className="flex items-center gap-[8px]">
<p className="text-[14px] text-left text-nowrap">
{m.label || m.id}
</p>
{m.badge}
</div>
</button>
))}
{group.models.map((m) => {
const isDisabled = Boolean(m.disabled);
return (
<button
key={`${group.provider}-${m.id}`}
type="button"
onClick={() => {
if (isDisabled) return;
onSelect(group.provider, m.id);
setOpen(false);
}}
aria-disabled={isDisabled}
className={clsx(
"flex gap-[12px] items-center p-[4px] rounded-[4px] text-left",
isDisabled
? "opacity-50 cursor-not-allowed"
: "hover:bg-white/5 focus:bg-white/5 cursor-pointer",
)}
>
<div className="flex items-center gap-[8px]">
<p className="text-[14px] text-left text-nowrap">
{m.label || m.id}
</p>
{m.badge}
</div>
{/* disabled reason text intentionally not shown (design: gray-out only) */}
</button>
);
})}
Comment on lines +189 to +217
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use disabled attribute instead of aria-disabled for better accessibility.

The current implementation uses aria-disabled while still keeping the button focusable and in the keyboard tab order. When isDisabled is true, keyboard users can focus the button but activation does nothing, creating a confusing experience.

According to WAI-ARIA best practices, aria-disabled should only be used when an element needs to remain in the tab order for context. For truly non-interactive items, the HTML disabled attribute is more appropriate as it removes the element from the tab order entirely.

Apply this diff to use the native disabled attribute:

 										{group.models.map((m) => {
 											const isDisabled = Boolean(m.disabled);
 											return (
 												<button
 													key={`${group.provider}-${m.id}`}
 													type="button"
+													disabled={isDisabled}
 													onClick={() => {
-														if (isDisabled) return;
 														onSelect(group.provider, m.id);
 														setOpen(false);
 													}}
-													aria-disabled={isDisabled}
 													className={clsx(
 														"flex gap-[12px] items-center p-[4px] rounded-[4px] text-left",
 														isDisabled
-															? "opacity-50 cursor-not-allowed"
-															: "hover:bg-white/5 focus:bg-white/5 cursor-pointer",
+															? "opacity-50 cursor-not-allowed disabled:pointer-events-none"
+															: "hover:bg-white/5 focus:bg-white/5 cursor-pointer enabled:hover:bg-white/5",
 													)}
 												>

Note: If you need disabled items to remain in tab order for specific UX reasons, please document that decision and ensure proper keyboard event handling (Enter/Space keys) is implemented.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{group.models.map((m) => {
const isDisabled = Boolean(m.disabled);
return (
<button
key={`${group.provider}-${m.id}`}
type="button"
onClick={() => {
if (isDisabled) return;
onSelect(group.provider, m.id);
setOpen(false);
}}
aria-disabled={isDisabled}
className={clsx(
"flex gap-[12px] items-center p-[4px] rounded-[4px] text-left",
isDisabled
? "opacity-50 cursor-not-allowed"
: "hover:bg-white/5 focus:bg-white/5 cursor-pointer",
)}
>
<div className="flex items-center gap-[8px]">
<p className="text-[14px] text-left text-nowrap">
{m.label || m.id}
</p>
{m.badge}
</div>
{/* disabled reason text intentionally not shown (design: gray-out only) */}
</button>
);
})}
{group.models.map((m) => {
const isDisabled = Boolean(m.disabled);
return (
<button
key={`${group.provider}-${m.id}`}
type="button"
disabled={isDisabled}
onClick={() => {
onSelect(group.provider, m.id);
setOpen(false);
}}
className={clsx(
"flex gap-[12px] items-center p-[4px] rounded-[4px] text-left",
isDisabled
? "opacity-50 cursor-not-allowed disabled:pointer-events-none"
: "hover:bg-white/5 focus:bg-white/5 cursor-pointer enabled:hover:bg-white/5",
)}
>
<div className="flex items-center gap-[8px]">
<p className="text-[14px] text-left text-nowrap">
{m.label || m.id}
</p>
{m.badge}
</div>
{/* disabled reason text intentionally not shown (design: gray-out only) */}
</button>
);
})}
🤖 Prompt for AI Agents
internal-packages/ui/components/model-picker.tsx lines 189-217: replace the use
of aria-disabled with the native disabled attribute on the <button> when
isDisabled is true so the button is removed from tab order and behaves natively
for keyboard users; keep the early return in onClick (or rely on the disabled
attribute) and adjust className logic so disabled state still applies opacity
and cursor-not-allowed while removing hover/focus styles for that state; ensure
no other code relies on the element remaining focusable (if it must remain
focusable, document and implement explicit keyboard handling instead).

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Disabled Buttons Still Keyboard Accessible

Disabled model picker buttons are still focusable and keyboard-activatable (Enter/Space) because the disabled attribute was replaced with aria-disabled. Since aria-disabled only provides an accessibility label, this creates an inconsistent user experience where buttons appear disabled but can still be interacted with via keyboard.

Fix in Cursor Fix in Web

</div>
))}
</div>
Expand Down
2 changes: 0 additions & 2 deletions internal-packages/workflow-designer-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
"marked": "catalog:",
"motion": "catalog:",
"next": "catalog:",
"pluralize": "catalog:",
"radix-ui": "catalog:",
"react": "catalog:",
"react-dom": "catalog:",
Expand All @@ -43,7 +42,6 @@
"@giselle/giselle-sdk-tsconfig": "workspace:*",
"@types/fast-levenshtein": "catalog:",
"@types/node": "catalog:",
"@types/pluralize": "catalog:",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"ai": "catalog:",
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,14 @@ import {
} from "@giselle-sdk/giselle/react";
import { useCallback } from "react";
import { Button } from "../../../ui/button";
import {
PropertiesPanelContent,
PropertiesPanelHeader,
PropertiesPanelRoot,
} from "../ui";
import { PropertiesPanelContent, PropertiesPanelRoot } from "../ui";
import { NodePanelHeader } from "../ui/node-panel-header";
import { GitHubActionPropertiesPanel } from "./github-action-properties-panel";
import { useConnectedInputs } from "./lib";

export function ActionNodePropertiesPanel({ node }: { node: ActionNode }) {
const { data, updateNodeData, setUiNodeState } = useWorkflowDesigner();
const { data, updateNodeData, deleteNode, setUiNodeState } =
useWorkflowDesigner();
const { isValid, connectedInputs } = useConnectedInputs(node.id, node.inputs);
const { createAndStartGenerationRunner } = useNodeGenerations({
nodeId: node.id,
Expand Down Expand Up @@ -55,19 +53,19 @@ export function ActionNodePropertiesPanel({ node }: { node: ActionNode }) {
]);
return (
<PropertiesPanelRoot>
<PropertiesPanelHeader
<NodePanelHeader
node={node}
onChangeName={(name) => {
updateNodeData(node, { name });
}}
action={
node.content.command.state.status === "unconfigured" ? null : (
<Button type="button" onClick={handleClick}>
Run Action
</Button>
)
}
onChangeName={(name) => updateNodeData(node, { name })}
docsUrl="https://docs.giselles.ai/en/glossary/action-node"
onDelete={() => deleteNode(node.id)}
/>
{node.content.command.state.status !== "unconfigured" && (
<div className="px-[16px] py-[8px] border-b border-inverse/10">
<Button type="button" onClick={handleClick} className="w-full">
Run Action
</Button>
</div>
)}
<PropertiesPanelContent>
<PropertiesPanel node={node} />
</PropertiesPanelContent>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import type { FileCategory, FileNode } from "@giselle-sdk/data-type";
import { useWorkflowDesigner } from "@giselle-sdk/giselle/react";
import {
PropertiesPanelContent,
PropertiesPanelHeader,
PropertiesPanelRoot,
} from "../ui";
import { PropertiesPanelContent, PropertiesPanelRoot } from "../ui";
import { NodePanelHeader } from "../ui/node-panel-header";
import { FilePanel } from "./file-panel";
import type { FileTypeConfig } from "./file-panel-type";

Expand All @@ -24,15 +21,15 @@ const fileType: Record<FileCategory, FileTypeConfig> = {
};

export function FileNodePropertiesPanel({ node }: { node: FileNode }) {
const { updateNodeData } = useWorkflowDesigner();
const { updateNodeData, deleteNode } = useWorkflowDesigner();

return (
<PropertiesPanelRoot>
<PropertiesPanelHeader
<NodePanelHeader
node={node}
onChangeName={(name) => {
updateNodeData(node, { name });
}}
onChangeName={(name) => updateNodeData(node, { name })}
docsUrl="https://docs.giselles.ai/en/glossary/file-node"
onDelete={() => deleteNode(node.id)}
/>
<PropertiesPanelContent>
<FilePanel node={node} config={fileType[node.content.category]} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,78 +4,68 @@ import {
useWorkflowDesigner,
} from "@giselle-sdk/giselle/react";
import clsx from "clsx/lite";
import { Maximize2 } from "lucide-react";
import { useCallback } from "react";
import { GenerateImageIcon } from "../../../icons";
import { EmptyState } from "../../../ui/empty-state";
import { GenerationView } from "../../../ui/generation-view";

function Empty({ onGenerate }: { onGenerate?: () => void }) {
function Empty() {
return (
<div className="bg-inverse/10 h-full rounded-[8px] flex justify-center items-center text-black-400">
<div className="relative bg-inverse/10 min-h-[250px] rounded-[8px] flex justify-center items-center text-black-400">
<EmptyState
icon={<GenerateImageIcon width={24} height={24} />}
title="Nothing generated yet."
description="Generate with the current Prompt or adjust the Prompt and the results will be displayed."
description="Generate or adjust the Prompt to see results."
className="text-black-400"
>
{onGenerate && (
<button
type="button"
onClick={onGenerate}
className="flex items-center justify-center px-[24px] py-[12px] mt-[16px] bg-[#141519] text-white rounded-[9999px] border border-border/15 transition-all hover:bg-[#1e1f26] hover:border-border/25 hover:translate-y-[-1px] cursor-pointer font-sans font-[500] text-[14px]"
>
<span className="mr-[8px] generate-star">✦</span>
Generate with the Current Prompt
</button>
)}
<style jsx>{`
.generate-star {
display: inline-block;
}
button:hover .generate-star {
animation: rotateStar 0.7s ease-in-out;
}
@keyframes rotateStar {
0% {
transform: rotate(0deg) scale(1);
}
50% {
transform: rotate(180deg) scale(1.5);
}
100% {
transform: rotate(360deg) scale(1);
}
}
`}</style>
</EmptyState>
/>
</div>
);
}

export function GenerationPanel({
node,
onClickGenerateButton,
onExpand,
isExpanded,
}: {
node: ImageGenerationNode;
onClickGenerateButton?: () => void;
onExpand?: () => void;
isExpanded?: boolean;
}) {
Comment on lines 26 to 36
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify: Is the generate CTA intentionally removed from the empty state?

onClickGenerateButton is no longer used and <Empty /> has no CTA. If generation is now triggered solely from elsewhere (e.g., a global GenerateCtaButton), all call sites should drop onClickGenerateButton. Otherwise, reintroduce the CTA.

Also applies to: 49-51


🏁 Script executed:

#!/bin/bash
# Find usages of GenerationPanel and check if onClickGenerateButton is still passed.
rg -nP --type=ts --type=tsx -C2 '\bGenerationPanel\s*\('
rg -nP --type=ts --type=tsx -C2 'onClickGenerateButton='

Length of output: 55


</script>

🏁 Script executed:

#!/bin/bash
# Search for GenerationPanel usages without --type flag
rg -nP '\bGenerationPanel\s*\(' -g '*.ts' -g '*.tsx' -C2

echo "---"

# Search for onClickGenerateButton parameter passing
rg -nP 'onClickGenerateButton=' -g '*.ts' -g '*.tsx' -C2

echo "---"

# Inspect the file to see lines 49-51
cat internal-packages/workflow-designer-ui/src/editor/properties-panel/image-generation-node-properties-panel/generation-panel.tsx | sed -n '1,60p'

Length of output: 8156


</script>

🏁 Script executed:

#!/bin/bash
# Get full file content to see where _handleGenerate is used
wc -l internal-packages/workflow-designer-ui/src/editor/properties-panel/image-generation-node-properties-panel/generation-panel.tsx

echo "---Full File---"
cat internal-packages/workflow-designer-ui/src/editor/properties-panel/image-generation-node-properties-panel/generation-panel.tsx

echo "---"
# Search for _handleGenerate usage
rg '_handleGenerate' internal-packages/workflow-designer-ui/src/editor/properties-panel/image-generation-node-properties-panel/generation-panel.tsx -n

echo "---"
# Check GenerationView component definition
fd GenerationView.tsx -g '*.tsx' | head -5

Length of output: 3004


Remove unused onClickGenerateButton callback or add CTA button in Empty state

The onClickGenerateButton parameter is still being passed by all call sites (generateImage, generateText, generate), but the _handleGenerate callback defined at line 43 is never invoked. The Empty() component (lines 13–22) has no CTA button, and neither does the rendered GenerationView. Either:

  1. Remove onClickGenerateButton from the function signature and all call sites, or
  2. Add a button in the Empty component that calls _handleGenerate() to trigger generation
🤖 Prompt for AI Agents
internal-packages/workflow-designer-ui/src/editor/properties-panel/image-generation-node-properties-panel/generation-panel.tsx
lines 26-36: the onClickGenerateButton prop is passed from callers but never
used because the Empty view has no CTA; add a visible CTA button in the Empty()
component that calls the existing _handleGenerate (or invokes
onClickGenerateButton if you prefer using props) so generation actually
triggers, and ensure the button is accessible (label/aria) and styled
consistently with other panel buttons; alternatively, if you decide not to
expose a CTA, remove onClickGenerateButton from this component and all call
sites to avoid dead code.

const { data } = useWorkflowDesigner();
const { currentGeneration } = useNodeGenerations({
nodeId: node.id,
origin: { type: "studio", workspaceId: data.id },
});

const handleGenerate = useCallback(() => {
const _handleGenerate = useCallback(() => {
if (onClickGenerateButton) {
onClickGenerateButton();
}
}, [onClickGenerateButton]);
Comment on lines +43 to 47
Copy link

Copilot AI Oct 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The _handleGenerate callback is defined but never used. This unused code should be removed.

Suggested change
const _handleGenerate = useCallback(() => {
if (onClickGenerateButton) {
onClickGenerateButton();
}
}, [onClickGenerateButton]);

Copilot uses AI. Check for mistakes.

if (currentGeneration === undefined) {
return <Empty onGenerate={handleGenerate} />;
return <Empty />;
}
return (
<div className="flex flex-col bg-inverse/10 h-full rounded-[8px] py-[8px]">
<div
className={clsx(
"relative flex flex-col bg-inverse/10 rounded-[8px] py-[8px]",
isExpanded ? "flex-1 min-h-0" : "min-h-[250px]",
)}
>
{onExpand && (
<button
type="button"
onClick={onExpand}
className="absolute bottom-[8px] right-[8px] size-[32px] rounded-full bg-inverse/10 hover:bg-inverse/20 flex items-center justify-center transition-colors group z-10"
aria-label="Expand"
>
<Maximize2 className="size-[16px] text-inverse group-hover:text-inverse/80" />
</button>
)}
<div
className={clsx(
"border-b border-white-400/20 py-[4px] px-[16px] flex items-center gap-[8px]",
Expand Down
Loading