Skip to content

Commit

Permalink
feat(app,api) add new border type glass option (#611)
Browse files Browse the repository at this point in the history
* feat: add glass border type option
* feat: add borderType property
* feat: api schema validation borderType
* fix types
* update types
* fix lint and integration test
* update styles
* Add border type select
* update window style form settings icon
  • Loading branch information
riccardoperra authored Apr 25, 2024
1 parent 9e537bc commit e94e8a2
Show file tree
Hide file tree
Showing 29 changed files with 165 additions and 13 deletions.
6 changes: 6 additions & 0 deletions .changeset/red-zoos-march.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@codeimage/api": minor
"@codeimage/app": minor
---

feat(app,api) add new border type glass option
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "SnippetTerminal" ADD COLUMN "borderType" TEXT;
1 change: 1 addition & 0 deletions apps/api/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ model SnippetTerminal {
showGlassReflection Boolean @default(false)
opacity Float @default(100)
alternativeTheme Boolean @default(false)
borderType String?
}

model SnippetEditorOptions {
Expand Down
10 changes: 10 additions & 0 deletions apps/api/src/common/typebox/enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {TString, Type} from '@sinclair/typebox';

export const enumLiteral = <T extends string>(values: T[]): TString => {
const literals = values.map(value => Type.Literal(value));
// TODO: validation should work but type must work as a string...
return Type.Intersect([
Type.Union(literals),
Type.String(),
]) as unknown as TString;
};
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export function makePrismaProjectRepository(
showWatermark: data.terminal.showWatermark,
textColor: data.terminal.textColor,
type: data.terminal.type,
borderType: data.terminal.borderType,
},
},
},
Expand Down Expand Up @@ -188,6 +189,7 @@ export function makePrismaProjectRepository(
showWatermark: data.terminal.showWatermark,
textColor: data.terminal.textColor,
type: data.terminal.type,
borderType: data.terminal.borderType,
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export function createProjectRequestMapper(
data.terminal.alternativeTheme ??
SnippetTerminalCreateRequestSchema.properties.alternativeTheme.default,
shadow: data.terminal.shadow ?? null,
borderType: data.terminal.borderType ?? null,
},
editorOptions: {
fontWeight: data.editorOptions.fontWeight,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export function createCompleteProjectGetByIdResponseMapper(
accentVisible: data.terminal.accentVisible,
alternativeTheme: data.terminal.alternativeTheme,
shadow: data.terminal.shadow,
borderType: data.terminal.borderType as 'glass' | null,
},
editorOptions: {
id: data.editorOptions.id,
Expand Down
2 changes: 2 additions & 0 deletions apps/api/src/modules/project/schema/project-create.schema.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {Static, Type} from '@sinclair/typebox';
import {Nullable} from '../../../common/typebox/nullable.js';
import {SnippetTerminalBorderType} from './project.schema.js';

export const SnippetFrameCreateRequestSchema = Type.Object(
{
Expand Down Expand Up @@ -45,6 +46,7 @@ export const SnippetTerminalCreateRequestSchema = Type.Object(
opacity: Nullable(Type.Number({minimum: 0, maximum: 100, default: 100})),
showHeader: Type.Boolean(),
showWatermark: Nullable(Type.Boolean({default: true})),
borderType: Nullable(SnippetTerminalBorderType),
},
{title: 'SnippetTerminalCreateRequest'},
);
Expand Down
2 changes: 2 additions & 0 deletions apps/api/src/modules/project/schema/project-update.schema.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {Static, Type} from '@sinclair/typebox';
import {Nullable} from '../../../common/typebox/nullable.js';
import {SnippetTerminalBorderType} from './project.schema.js';

export const SnippetFrameUpdateRequestSchema = Type.Object(
{
Expand Down Expand Up @@ -40,6 +41,7 @@ const SnippetTerminalUpdateRequestSchema = Type.Object(
showWatermark: Type.Boolean(),
textColor: Nullable(Type.String()),
type: Type.String(),
borderType: Nullable(SnippetTerminalBorderType),
},
{title: 'SnippetTerminalUpdateRequest'},
);
Expand Down
4 changes: 4 additions & 0 deletions apps/api/src/modules/project/schema/project.schema.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {Type} from '@sinclair/typebox';
import {enumLiteral} from '../../../common/typebox/enum.js';
import {Nullable} from '../../../common/typebox/nullable.js';

export const BaseProjectResponseSchema = Type.Object(
Expand Down Expand Up @@ -34,6 +35,8 @@ export const BaseSnippetFrameSchema = Type.Object({
opacity: Type.Number(),
});

export const SnippetTerminalBorderType = enumLiteral(['glass'] as const);

export const BaseSnippetTerminalSchema = Type.Object({
id: Type.String({format: 'uuid'}),
showHeader: Type.Boolean(),
Expand All @@ -46,6 +49,7 @@ export const BaseSnippetTerminalSchema = Type.Object({
showGlassReflection: Type.Boolean(),
opacity: Type.Number(),
alternativeTheme: Type.Boolean(),
borderType: Nullable(SnippetTerminalBorderType),
});

export const BaseSnippetEditorOptionsSchema = Type.Object({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ test('should map ProjectCreateRequest to Prisma ProjectCreateRequest with defaul
shadow: null,
textColor: null,
accentVisible: null,
borderType: null,
},
name: 'Untitled',
editors: [],
Expand Down Expand Up @@ -53,6 +54,7 @@ test('should map ProjectCreateRequest to Prisma ProjectCreateRequest with defaul
showWatermark: true,
opacity: 100,
showHeader: true,
borderType: null,
},
editors: [],
name: 'Untitled',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ test('should map Prisma ProjectGetByIdResponse to schema ProjectGetByIdResponse'
shadow: null,
textColor: null,
accentVisible: true,
borderType: 'glass',
},
editorOptionsId: 'editorOptionsId',
terminalId: 'terminalId',
Expand Down Expand Up @@ -75,6 +76,7 @@ test('should map Prisma ProjectGetByIdResponse to schema ProjectGetByIdResponse'
showWatermark: false,
opacity: 100,
showHeader: true,
borderType: 'glass',
},
editorOptions: {
id: 'editorOptionsId',
Expand Down
4 changes: 3 additions & 1 deletion apps/api/test/routes/v1/project/update.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ test<TestContext>('POST /v1/project/:id [Update Project] -> 200', async context
alternativeTheme: true,
accentVisible: false,
type: 'windows',
borderType: 'glass',
},
};

Expand Down Expand Up @@ -125,7 +126,8 @@ test<TestContext>('POST /v1/project/:id [Update Project] -> 200', async context
alternativeTheme: true,
accentVisible: false,
type: 'windows',
} as ProjectUpdateResponse['terminal'],
borderType: 'glass',
} satisfies ProjectUpdateResponse['terminal'],
'return updated terminal',
);
assert.deepStrictEqual(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const badge = style({
position: 'absolute',
left: '100%',
top: '50%',
transform: `translateX(10px) translateY(-50%)`,
transform: `translateX(2px) translateY(-50%)`,
borderRadius: themeTokens.radii.lg,
whiteSpace: 'nowrap',
});
1 change: 1 addition & 0 deletions apps/codeimage/src/components/Frame/ManagedFrame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export function ManagedFrame() {
showWatermark={terminal.showWatermark}
opacity={terminal.opacity}
alternativeTheme={terminal.alternativeTheme}
borderType={terminal.borderType}
themeId={editor.state.options.themeId}
>
<Show when={getActiveEditorStore().editor()}>
Expand Down
1 change: 1 addition & 0 deletions apps/codeimage/src/components/Frame/PreviewFrame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ export function PreviewFrame(props: VoidProps<PreviewFrameProps>) {
showWatermark={terminal.showWatermark}
opacity={terminal.opacity}
alternativeTheme={terminal.alternativeTheme}
borderType={terminal.borderType}
themeId={editor.state.options.themeId}
>
<Show when={getActiveEditorStore().editor()}>
Expand Down
25 changes: 25 additions & 0 deletions apps/codeimage/src/components/Icons/SettingsIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {SvgIconProps} from '@codeimage/ui';
import {SvgIcon} from '@codeui/kit';

export function SettingsIcon(props: SvgIconProps) {
return (
<SvgIcon
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
{...props}
>
<path
fill-rule="evenodd"
d="M12 6.75a5.25 5.25 0 0 1 6.775-5.025.75.75 0 0 1 .313 1.248l-3.32 3.319c.063.475.276.934.641 1.299.365.365.824.578 1.3.64l3.318-3.319a.75.75 0 0 1 1.248.313 5.25 5.25 0 0 1-5.472 6.756c-1.018-.086-1.87.1-2.309.634L7.344 21.3A3.298 3.298 0 1 1 2.7 16.657l8.684-7.151c.533-.44.72-1.291.634-2.309A5.342 5.342 0 0 1 12 6.75ZM4.117 19.125a.75.75 0 0 1 .75-.75h.008a.75.75 0 0 1 .75.75v.008a.75.75 0 0 1-.75.75h-.008a.75.75 0 0 1-.75-.75v-.008Z"
clip-rule="evenodd"
/>
<path d="m10.076 8.64-2.201-2.2V4.874a.75.75 0 0 0-.364-.643l-3.75-2.25a.75.75 0 0 0-.916.113l-.75.75a.75.75 0 0 0-.113.916l2.25 3.75a.75.75 0 0 0 .643.364h1.564l2.062 2.062 1.575-1.297Z" />
<path
fill-rule="evenodd"
d="m12.556 17.329 4.183 4.182a3.375 3.375 0 0 0 4.773-4.773l-3.306-3.305a6.803 6.803 0 0 1-1.53.043c-.394-.034-.682-.006-.867.042a.589.589 0 0 0-.167.063l-3.086 3.748Zm3.414-1.36a.75.75 0 0 1 1.06 0l1.875 1.876a.75.75 0 1 1-1.06 1.06L15.97 17.03a.75.75 0 0 1 0-1.06Z"
clip-rule="evenodd"
/>
</SvgIcon>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export function PresetPreview(props: PresetPreviewProps) {
accentVisible={props.data.terminal.accentVisible}
textColor={props.data.terminal.textColor}
showHeader={props.data.terminal.showHeader}
borderType={props.data.terminal.borderType}
showGlassReflection={props.data.terminal.showGlassReflection}
showWatermark={false}
opacity={props.data.terminal.opacity}
Expand Down
44 changes: 44 additions & 0 deletions apps/codeimage/src/components/PropertyEditor/WindowStyleForm.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import {useI18n} from '@codeimage/locale';
import {getTerminalState} from '@codeimage/store/editor/terminal';
import {VersionStore} from '@codeimage/store/version/version.store';
import {createSelectOptions, Select} from '@codeui/kit';
import {shadowsLabel} from '@core/configuration/shadow';
import {getUmami} from '@core/constants/umami';
import {SegmentedField} from '@ui/SegmentedField/SegmentedField';
import {SkeletonLine} from '@ui/Skeleton/Skeleton';
import {createMemo, ParentComponent, Show} from 'solid-js';
import {provideState} from 'statebuilder';
import {AppLocaleEntries} from '../../i18n';
import {TerminalControlField} from './controls/TerminalControlField/TerminalControlField';
import {PanelHeader} from './PanelHeader';
Expand All @@ -14,6 +16,7 @@ import {SuspenseEditorItem} from './SuspenseEditorItem';

export const WindowStyleForm: ParentComponent = () => {
const terminal = getTerminalState();
const versionStore = provideState(VersionStore);
const [t] = useI18n<AppLocaleEntries>();

const terminalShadows = createMemo(
Expand All @@ -25,6 +28,17 @@ export const WindowStyleForm: ParentComponent = () => {
valueKey: 'value',
});

const borderTypeSelect = createSelectOptions(
[
{label: 'None', value: 'none'},
{label: 'Glass', value: 'glass'},
],
{
key: 'label',
valueKey: 'value',
},
);

return (
<>
<PanelHeader label={t('frame.terminal')} />
Expand Down Expand Up @@ -151,6 +165,36 @@ export const WindowStyleForm: ParentComponent = () => {
</SuspenseEditorItem>
</TwoColumnPanelRow>
</PanelRow>
<PanelRow
for={'frameSelectShadow'}
feature={'borderType'}
label={t('frame.border')}
>
<TwoColumnPanelRow>
<SuspenseEditorItem
fallback={<SkeletonLine width={'100%'} height={'24px'} />}
>
<Select
options={borderTypeSelect.options()}
{...borderTypeSelect.props()}
{...borderTypeSelect.controlled(
() => terminal.state.borderType ?? 'none',
border => {
const isNone = border === 'none';
versionStore.see('borderType', false);
getUmami().track('change-border', {
border: border ?? 'none',
});
terminal.setBorder(isNone ? null : border ?? null);
},
)}
aria-label={'Border'}
size={'xs'}
id={'frameSelectBorder'}
/>
</SuspenseEditorItem>
</TwoColumnPanelRow>
</PanelRow>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import {getRootEditorStore} from '@codeimage/store/editor';
import {getTerminalState} from '@codeimage/store/editor/terminal';
import {VersionStore} from '@codeimage/store/version/version.store';
import {Box, RadioBlock} from '@codeimage/ui';
import {As, Checkbox, icons} from '@codeui/kit';
import {As, Checkbox} from '@codeui/kit';
import {TERMINAL_SHADOWS} from '@core/configuration/shadow';
import {AVAILABLE_TERMINAL_THEMES} from '@core/configuration/terminal-themes';
import {createSignal, For, JSXElement, onMount, Suspense} from 'solid-js';
import {Dynamic} from 'solid-js/web';
import {provideState} from 'statebuilder';
import {SettingsIcon} from '../../../Icons/SettingsIcon';
import {SidebarPopover} from '../../SidebarPopover/SidebarPopover';
import {SidebarPopoverTitle} from '../../SidebarPopover/SidebarPopoverTitle';
import * as styles from './TerminalControlField.css';
Expand Down Expand Up @@ -66,10 +67,11 @@ export function TerminalControlField(
opacity={100}
themeId={editorState.options.themeId}
showGlassReflection={false}
borderType={null}
/>
</Suspense>
</Box>
<icons.SelectorIcon class={styles.inputIcon} />
<SettingsIcon class={styles.inputIcon} />
</As>
}
onOpenChange={setOpen}
Expand Down Expand Up @@ -101,6 +103,7 @@ export function TerminalControlField(
opacity={100}
themeId={editorState.options.themeId}
showGlassReflection={terminalState.state.showGlassReflection}
borderType={null}
/>
</Suspense>
</Box>
Expand Down
6 changes: 5 additions & 1 deletion apps/codeimage/src/components/Terminal/TerminalHost.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,16 @@ export const TerminalHost: FlowComponent<TerminalHostProps> = props => {
data-header-visible={props.showHeader}
data-accent-header={props.accentVisible && !props.alternativeTheme}
data-fallback-inactive-tab={tabTheme()?.shouldFallbackInactiveColor}
data-custom-border={props.borderType === 'glass' ? 'glass' : null}
style={assignInlineVars({
[styles.terminalVars.headerBackgroundColor]:
tabTheme()?.background ?? '',
[styles.terminalVars.backgroundColor]: background(),
[styles.terminalVars.textColor]: props.textColor,
[styles.terminalVars.boxShadow]: props.shadow ?? 'unset',
[styles.terminalVars.boxShadow]:
props.shadow && props.shadow !== 'unset'
? props.shadow
: '0 0 0 0 rgb(0, 0, 0, 0)',
[styles.terminalVars.tabTextColor]: tabTheme()?.textColor ?? '',
[styles.terminalVars.tabAccentActiveBackground]:
tabTheme().activeTabBackground ?? '',
Expand Down
14 changes: 12 additions & 2 deletions apps/codeimage/src/components/Terminal/terminal.css.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {themeVars} from '@codeimage/ui';
import {createTheme, fallbackVar, style} from '@vanilla-extract/css';
import {createTheme, createVar, fallbackVar, style} from '@vanilla-extract/css';

export const [terminalTheme, terminalVars] = createTheme({
headerHeight: '50px',
Expand All @@ -17,6 +17,11 @@ export const [terminalTheme, terminalVars] = createTheme({
tabTextColor: 'unset',
});

const glassBorderDark = `0 0 0 1px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0,0,0,.90), inset 0 0 0 1.5px rgba(255, 255, 255, 0.4)`;
const glassBorderLight = `0 0 15px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.1), 0 0 0 1px rgb(0,0,0,.05), inset 0 0 0 1px rgba(255, 255, 255, 0.15)`;

const glassBorderVar = createVar();

export const wrapper = style([
terminalTheme,
{
Expand All @@ -36,13 +41,18 @@ export const wrapper = style([
'&[data-theme-mode=light]': {
vars: {
[terminalVars.headerColor]: `255, 255, 255`,
[glassBorderVar]: glassBorderLight,
},
},
'&[data-theme-mode=dark] &': {
'&[data-theme-mode=dark]': {
vars: {
[glassBorderVar]: glassBorderDark,
[terminalVars.headerColor]: `0, 0, 0`,
},
},
'&[data-custom-border=glass]': {
boxShadow: `${glassBorderVar}, ${terminalVars.boxShadow}`,
},
},
},
]);
Expand Down
Loading

0 comments on commit e94e8a2

Please sign in to comment.