Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
accfbf3
fix: enable publish button on all routes
lunenas Mar 19, 2026
3699592
fix: render link <a> tags consistently on canvas and generated pages
tristan-mouchet Mar 19, 2026
329183a
feat: add image alt text popover to rich text editor
lunenas Mar 19, 2026
ed49318
ui: matching UI with LinkSettings in right sidebar
lunenas Mar 20, 2026
893f147
fix: don't preselect URL type for new rich text links
lunenas Mar 20, 2026
12becbf
feat: change the way the preview is displayed so it doesn't force the…
tristan-mouchet Mar 20, 2026
b9f67c4
feat: add horizontal rule support to rich text editor
lunenas Mar 20, 2026
6febb19
ui: preview loading
lunenas Mar 20, 2026
250e87a
feat: show border controls for image and separator in rich text
lunenas Mar 20, 2026
f37efa9
feat: show border and shadow controls for rich text image sublayer
lunenas Mar 20, 2026
a71aa43
feat: preview routes to be protected by authentication
tristan-mouchet Mar 20, 2026
49974cc
feat: add outline controls to border settings
lunenas Mar 20, 2026
c0ad92d
Merge branch 'develop' into fix/link-tag-on-canvas
tristan-mouchet Mar 20, 2026
65ee425
fix: render proper HTML tags for CMS-bound rich text on canvas
lunenas Mar 20, 2026
d15ee56
Merge branch 'develop' into feat/rich-text-horizontal-rule
tristan-mouchet Mar 20, 2026
1eeeadc
feat: allow rich-text fields in SEO title and description
lunenas Mar 20, 2026
eadcbcb
fix: resolve og:image URLs against correct domain
tristan-mouchet Mar 23, 2026
9ba26d3
fix: document does not have a main landmark (fix #73)
tristan-mouchet Mar 23, 2026
ec3bf7c
refactor: use file manager for SEO social preview image
tristan-mouchet Mar 23, 2026
270a99f
fix: URL issues and layer selection when switching between Layers and…
tristan-mouchet Mar 23, 2026
a75318b
fix: prevent unneeded multiple URL updates when selecting a page
tristan-mouchet Mar 23, 2026
cd49a17
fix: prevent canvas outlines from sometimes blinking
tristan-mouchet Mar 23, 2026
311a0d8
refactor: preload element library elements and images
tristan-mouchet Mar 23, 2026
226e520
fix: resolve reference field values on canvas
tristan-mouchet Mar 23, 2026
c235f53
fix: show Link sublayer in rich text layers tree
lunenas Mar 23, 2026
78828d5
feat: add reference field link resolution for dynamic pages
tristan-mouchet Mar 23, 2026
eda1ae5
fix: render rich text links correctly on preview and generated pages
tristan-mouchet Mar 23, 2026
1cb903f
Merge branch 'develop' into feat/outline-controls
tristan-mouchet Mar 23, 2026
7294215
fix: CMS options not filtering sub-options (reference fields)
tristan-mouchet Mar 23, 2026
a119b24
Merge pull request #67 from ycode/feat/rich-text-horizontal-rule
liamwalder Mar 24, 2026
5a2fd39
Merge pull request #68 from ycode/feat/outline-controls
liamwalder Mar 24, 2026
fd3ed60
Merge pull request #72 from ycode/feat/seo-rich-text-field-binding
liamwalder Mar 24, 2026
c1d6990
Merge branch 'develop' into feat/seo-image-from-file-manager
liamwalder Mar 24, 2026
d774916
Merge pull request #75 from ycode/feat/seo-image-from-file-manager
liamwalder Mar 24, 2026
f731be9
Merge branch 'develop' into feat/rich-text-image-alt-popover
liamwalder Mar 24, 2026
551b123
Merge branch 'develop' into fix/in-builder-preview-ux
liamwalder Mar 24, 2026
b98dccd
Merge pull request #66 from ycode/fix/in-builder-preview-ux
liamwalder Mar 24, 2026
5a68004
Merge branch 'develop' into fix/link-tag-on-canvas
liamwalder Mar 24, 2026
423463a
Merge pull request #70 from ycode/fix/link-tag-on-canvas
liamwalder Mar 24, 2026
3be18ba
Merge branch 'develop' into refactor/builder-ux-improvements
liamwalder Mar 24, 2026
4cde29a
Merge pull request #77 from ycode/refactor/builder-ux-improvements
liamwalder Mar 24, 2026
7743a3a
Merge branch 'develop' into fix/rich-text-link-style-sublayer
liamwalder Mar 24, 2026
79f81b8
fix: show unique alt text per image in rich text popover
lunenas Mar 24, 2026
ce12a85
Merge pull request #79 from ycode/fix/rich-text-link-style-sublayer
liamwalder Mar 24, 2026
641e411
Merge branch 'develop' into fix/cms-rendering-issue
liamwalder Mar 24, 2026
3c41425
fix: read alt from current selection when popover opens
lunenas Mar 24, 2026
a08e431
ui: sticky formattingToolbar
lunenas Mar 24, 2026
7168acc
Merge pull request #81 from ycode/fix/cms-rendering-issue
liamwalder Mar 24, 2026
14a03eb
Merge branch 'develop' into feat/rich-text-image-alt-popover
liamwalder Mar 24, 2026
6a629ca
Merge pull request #65 from ycode/feat/rich-text-image-alt-popover
liamwalder Mar 24, 2026
cb3f798
Merge branch 'develop' into fix/cms-option-filtering
liamwalder Mar 24, 2026
af4cba2
fix: persist color variable edits from hex input
tristan-mouchet Mar 24, 2026
5e9cb81
Merge pull request #82 from ycode/fix/cms-option-filtering
liamwalder Mar 24, 2026
d27733e
Merge branch 'develop' into fix/rich-text-cms-canvas-tags
liamwalder Mar 24, 2026
f130f17
Merge pull request #71 from ycode/fix/rich-text-cms-canvas-tags
liamwalder Mar 24, 2026
2771935
Merge branch 'develop' into fix/color-variable-update
liamwalder Mar 24, 2026
9b6e8b7
Merge pull request #88 from ycode/fix/color-variable-update
liamwalder Mar 24, 2026
08b48b8
feat: bump version to 0.9.0
liamwalder Mar 24, 2026
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
4 changes: 4 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,10 @@
@apply bg-input px-1.5 py-0.5 rounded-md font-mono;
}

.rich-text-editor-full.ProseMirror hr {
@apply border-t border-border my-4;
}

/* Custom dropdown chevron for styled select elements on published pages */
#ybody select.appearance-none {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23737373' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
Expand Down
7 changes: 3 additions & 4 deletions app/ycode/api/cache/clear-all/route.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { NextRequest, NextResponse } from 'next/server';
import { revalidatePath } from 'next/cache';
import { noCache } from '@/lib/api-response';
import { clearAllCache } from '@/lib/services/cacheService';

/**
* Vercel Cache Invalidation Endpoint
*
* Handles full cache invalidation
* Handles full cache invalidation (data cache tags + layout path)
*/

export async function POST(request: NextRequest) {
try {
// Invalidate all pages
revalidatePath('/', 'layout');
await clearAllCache();

return noCache({
success: true,
Expand Down
131 changes: 131 additions & 0 deletions app/ycode/components/BorderControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ const BorderControls = memo(function BorderControls({ layer, onLayerUpdate, acti
const divideColor = getDesignProperty('borders', 'divideColor') || '';
const hasDivider = !!(divideX || divideY || divideColor || designBorders?.divideX || designBorders?.divideY || designBorders?.divideColor);

const outlineWidth = getDesignProperty('borders', 'outlineWidth') || '';
const outlineColor = getDesignProperty('borders', 'outlineColor') || '';
const outlineOffset = getDesignProperty('borders', 'outlineOffset') || '';
const hasOutline = !!(outlineWidth || outlineColor || designBorders?.outlineWidth || designBorders?.outlineColor);

// Local controlled inputs (prevents repopulation bug)
const inputs = useControlledInputs({
borderRadius,
Expand All @@ -119,6 +124,8 @@ const BorderControls = memo(function BorderControls({ layer, onLayerUpdate, acti
borderLeftWidth,
divideX,
divideY,
outlineWidth,
outlineOffset,
}, extractMeasurementValue);

const [borderRadiusInput, setBorderRadiusInput] = inputs.borderRadius;
Expand All @@ -133,6 +140,8 @@ const BorderControls = memo(function BorderControls({ layer, onLayerUpdate, acti
const [borderLeftWidthInput, setBorderLeftWidthInput] = inputs.borderLeftWidth;
const [divideXInput, setDivideXInput] = inputs.divideX;
const [divideYInput, setDivideYInput] = inputs.divideY;
const [outlineWidthInput, setOutlineWidthInput] = inputs.outlineWidth;
const [outlineOffsetInput, setOutlineOffsetInput] = inputs.outlineOffset;

// Use mode toggle hooks for radius and width
const radiusModeToggle = useModeToggle({
Expand Down Expand Up @@ -297,6 +306,43 @@ const BorderControls = memo(function BorderControls({ layer, onLayerUpdate, acti
]);
};

const handleOutlineWidthChange = (value: string) => {
setOutlineWidthInput(value);
const sanitized = removeSpaces(value);
debouncedUpdateDesignProperty('borders', 'outlineWidth', sanitized || null);
};

const handleOutlineColorChange = (value: string) => {
const sanitized = removeSpaces(value);
debouncedUpdateDesignProperty('borders', 'outlineColor', sanitized || null);
};

const handleOutlineColorImmediate = (value: string) => {
const sanitized = removeSpaces(value);
updateDesignProperty('borders', 'outlineColor', sanitized || null);
};

const handleOutlineOffsetChange = (value: string) => {
setOutlineOffsetInput(value);
const sanitized = removeSpaces(value);
debouncedUpdateDesignProperty('borders', 'outlineOffset', sanitized || null);
};

const handleAddOutline = () => {
updateDesignProperties([
{ category: 'borders', property: 'outlineWidth', value: '1px' },
{ category: 'borders', property: 'outlineColor', value: '#000000' },
]);
};

const handleRemoveOutline = () => {
updateDesignProperties([
{ category: 'borders', property: 'outlineWidth', value: null },
{ category: 'borders', property: 'outlineColor', value: null },
{ category: 'borders', property: 'outlineOffset', value: null },
]);
};

return (
<div className="py-5">
<header className="py-4 -mt-4 flex items-center justify-between">
Expand All @@ -314,6 +360,12 @@ const BorderControls = memo(function BorderControls({ layer, onLayerUpdate, acti
>
Dividers
</DropdownMenuItem>
<DropdownMenuItem
onClick={handleAddOutline}
disabled={hasOutline}
>
Outline
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</header>
Expand Down Expand Up @@ -680,6 +732,85 @@ const BorderControls = memo(function BorderControls({ layer, onLayerUpdate, acti
</div>
)}

{hasOutline && (
<div className="grid grid-cols-3 items-start">
<Label variant="muted" className="h-8">Outline</Label>
<div className="col-span-2 flex items-center gap-2">
<Popover>
<PopoverTrigger asChild>
<Button
variant="input"
size="sm"
className="justify-start flex-1"
>
<div className="flex items-center gap-2">
<div className="size-5 rounded-[6px] shrink-0 -ml-1 relative overflow-hidden outline dark:outline-white/10 outline-offset-[-1px]">
<div className="absolute inset-0 z-20" style={{ background: parseBorderColorToCss(outlineColor, colorVariables) }} />
<div className="absolute inset-0 opacity-15 bg-checkerboard bg-background z-10" />
</div>
<Label variant="muted" className="cursor-pointer">{outlineWidth || '1px'}</Label>
</div>
</Button>
</PopoverTrigger>
<PopoverContent className="w-64 mr-4">
<div className="flex flex-col gap-2">
<div className="grid grid-cols-3">
<Label variant="muted">Width</Label>
<div className="col-span-2">
<Input
stepper
min="0"
step="1"
value={outlineWidthInput}
onChange={(e) => handleOutlineWidthChange(e.target.value)}
placeholder="1"
/>
</div>
</div>
<div className="grid grid-cols-3">
<Label variant="muted">Color</Label>
<div className="col-span-2 *:w-full">
<ColorPropertyField
solidOnly
value={outlineColor || '#000000'}
onChange={handleOutlineColorChange}
onImmediateChange={handleOutlineColorImmediate}
layer={layer}
onLayerUpdate={onLayerUpdate}
designProperty="outlineColor"
fieldGroups={fieldGroups}
allFields={allFields}
collections={collections}
/>
</div>
</div>
<div className="grid grid-cols-3">
<Label variant="muted">Offset</Label>
<div className="col-span-2">
<Input
stepper
step="1"
value={outlineOffsetInput}
onChange={(e) => handleOutlineOffsetChange(e.target.value)}
placeholder="0"
/>
</div>
</div>
</div>
</PopoverContent>
</Popover>
<span
role="button"
tabIndex={0}
className="p-0.5 rounded-sm opacity-70 hover:opacity-100 transition-opacity cursor-pointer"
onClick={handleRemoveOutline}
>
<Icon name="x" className="size-2.5" />
</span>
</div>
</div>
)}

</div>

</div>
Expand Down
19 changes: 17 additions & 2 deletions app/ycode/components/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { getCanvasIframeHtml } from '@/lib/canvas-utils';
import { CanvasPortalProvider } from '@/lib/canvas-portal-context';
import { cn } from '@/lib/utils';
import { loadSwiperCss } from '@/lib/slider-utils';
import { resolveReferenceFieldsSync } from '@/lib/collection-utils';
import { useEditorStore } from '@/stores/useEditorStore';
import { useFontsStore } from '@/stores/useFontsStore';
import { useColorVariablesStore } from '@/stores/useColorVariablesStore';
Expand Down Expand Up @@ -298,6 +299,19 @@ export default function Canvas({
return serializeLayers(layers, components, editingComponentVariables);
}, [layers, components, editingComponentVariables]);

// Enrich page collection item data with reference field dotted keys
// so variables like "refFieldId.targetFieldId" resolve on canvas
const enrichedPageCollectionItemData = useMemo(() => {
const values = pageCollectionItem?.values;
if (!values || !pageCollectionFields?.length) return values || null;
return resolveReferenceFieldsSync(
values,
pageCollectionFields,
collectionItems,
collectionFields
);
}, [pageCollectionItem?.values, pageCollectionFields, collectionItems, collectionFields]);

// Collect layer IDs that should be hidden on canvas (display: hidden with on-load)
const editorHiddenLayerIds = useMemo(() => {
if (disableEditorHiddenLayers) return undefined;
Expand Down Expand Up @@ -472,7 +486,7 @@ export default function Canvas({
hoveredLayerId={effectiveHoveredLayerId}
pageId={pageId}
pageCollectionItemId={pageCollectionItem?.id}
pageCollectionItemData={pageCollectionItem?.values || null}
pageCollectionItemData={enrichedPageCollectionItemData}
onLayerClick={handleLayerClick}
onLayerUpdate={onLayerUpdate}
onLayerHover={handleLayerHover}
Expand All @@ -495,7 +509,8 @@ export default function Canvas({
editingComponentId,
editingComponentVariables,
pageId,
pageCollectionItem,
pageCollectionItem?.id,
enrichedPageCollectionItemData,
handleLayerClick,
onLayerUpdate,
handleLayerHover,
Expand Down
Loading