Skip to content
Draft
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
6 changes: 4 additions & 2 deletions modules/coreI18n/src/main/key.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1537,7 +1537,6 @@ object I18nKey:
val `playAgainstComputer`: I18nKey = "playAgainstComputer"
val `gameMode`: I18nKey = "gameMode"
val `createLobbyGame`: I18nKey = "createLobbyGame"
val `youPlayAs`: I18nKey = "youPlayAs"
val `toInviteSomeoneToPlayGiveThisUrl`: I18nKey = "toInviteSomeoneToPlayGiveThisUrl"
val `gameOver`: I18nKey = "gameOver"
val `waitingForOpponent`: I18nKey = "waitingForOpponent"
Expand Down Expand Up @@ -1689,6 +1688,7 @@ object I18nKey:
val `minutesPerSide`: I18nKey = "minutesPerSide"
val `variant`: I18nKey = "variant"
val `variants`: I18nKey = "variants"
val `variantsDescription`: I18nKey = "variantsDescription"
val `timeControl`: I18nKey = "timeControl"
val `realTime`: I18nKey = "realTime"
val `correspondence`: I18nKey = "correspondence"
Expand Down Expand Up @@ -1759,6 +1759,7 @@ object I18nKey:
val `standard`: I18nKey = "standard"
val `customPosition`: I18nKey = "customPosition"
val `unlimited`: I18nKey = "unlimited"
val `unlimitedDescription`: I18nKey = "unlimitedDescription"
val `mode`: I18nKey = "mode"
val `casual`: I18nKey = "casual"
val `rated`: I18nKey = "rated"
Expand Down Expand Up @@ -1835,7 +1836,6 @@ object I18nKey:
val `pasteTheFenStringHere`: I18nKey = "pasteTheFenStringHere"
val `pasteThePgnStringHere`: I18nKey = "pasteThePgnStringHere"
val `orUploadPgnFile`: I18nKey = "orUploadPgnFile"
val `fromPosition`: I18nKey = "fromPosition"
val `continueFromHere`: I18nKey = "continueFromHere"
val `toStudy`: I18nKey = "toStudy"
val `importGame`: I18nKey = "importGame"
Expand Down Expand Up @@ -3005,6 +3005,8 @@ object I18nKey:
val `racingKingsTitle`: I18nKey = "variant:racingKingsTitle"
val `crazyhouse`: I18nKey = "variant:crazyhouse"
val `crazyhouseTitle`: I18nKey = "variant:crazyhouseTitle"
val `fromPosition`: I18nKey = "variant:fromPosition"
val `fromPositionTitle`: I18nKey = "variant:fromPositionTitle"

object video:
val `chessVideos`: I18nKey = "video:chessVideos"
Expand Down
4 changes: 2 additions & 2 deletions translation/source/site.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
<string name="playAgainstComputer">Play against computer</string>
<string name="gameMode">Game mode</string>
<string name="createLobbyGame">Create lobby game</string>
<string name="youPlayAs">You play as</string>
<string name="toInviteSomeoneToPlayGiveThisUrl">To invite someone to play, give this URL</string>
<string name="gameOver">Game Over</string>
<string name="waitingForOpponent">Waiting for opponent</string>
Expand Down Expand Up @@ -205,6 +204,7 @@
<string name="minutesPerSide">Minutes per side</string>
<string name="variant">Variant</string>
<string name="variants">Variants</string>
<string name="variantsDescription">More ways to play</string>
<string name="timeControl">Time control</string>
<string name="realTime">Real time</string>
<string name="correspondence">Correspondence</string>
Expand Down Expand Up @@ -299,6 +299,7 @@
<string name="standard">Standard</string>
<string name="customPosition">Custom position</string>
<string name="unlimited">Unlimited</string>
<string name="unlimitedDescription">Take all the time you need</string>
<string name="mode">Mode</string>
<string name="casual">Casual</string>
<string name="rated">Rated</string>
Expand Down Expand Up @@ -427,7 +428,6 @@
<string name="pasteTheFenStringHere">Paste the FEN text here</string>
<string name="pasteThePgnStringHere">Paste the PGN text here</string>
<string name="orUploadPgnFile">Or upload a PGN file</string>
<string name="fromPosition">From position</string>
<string name="continueFromHere">Continue from here</string>
<string name="toStudy">Study</string>
<string name="importGame">Import game</string>
Expand Down
2 changes: 2 additions & 0 deletions translation/source/variant.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@
<string name="racingKingsTitle">Get your king to the other side of the board to win.</string>
<string name="crazyhouse">Crazyhouse</string>
<string name="crazyhouseTitle">Captured pieces can be dropped back on the board instead of moving a piece.</string>
<string name="fromPosition">From Position</string>
<string name="fromPositionTitle">Standard chess from a custom position</string>
</resources>
12 changes: 8 additions & 4 deletions ui/@types/lichess/i18n.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3527,8 +3527,6 @@ interface I18n {
freeOnlineChess: string;
/** Friends */
friends: string;
/** From position */
fromPosition: string;
/** Game aborted */
gameAborted: string;
/** Game as GIF */
Expand Down Expand Up @@ -4495,6 +4493,8 @@ interface I18n {
unknownDueToRounding: string;
/** Unlimited */
unlimited: string;
/** Take all the time you need */
unlimitedDescription: string;
/** Unsubscribe */
unsubscribe: string;
/** Until */
Expand Down Expand Up @@ -4533,6 +4533,8 @@ interface I18n {
variantLoss: string;
/** Variants */
variants: string;
/** More ways to play */
variantsDescription: string;
/** Variant win */
variantWin: string;
/** Variation arrows let you navigate without using the move list. */
Expand Down Expand Up @@ -4711,8 +4713,6 @@ interface I18n {
youHaveJoinedTeamX: I18nFormat;
/** You need an account to do that */
youNeedAnAccountToDoThat: string;
/** You play as */
youPlayAs: string;
/** You play the black pieces */
youPlayTheBlackPieces: string;
/** You play the white pieces */
Expand Down Expand Up @@ -5887,6 +5887,10 @@ interface I18n {
crazyhouse: string;
/** Captured pieces can be dropped back on the board instead of moving a piece. */
crazyhouseTitle: string;
/** From Position */
fromPosition: string;
/** Standard chess from a custom position */
fromPositionTitle: string;
/** Horde */
horde: string;
/** One side has a large number of pawns, the other has a normal army. */
Expand Down
1 change: 1 addition & 0 deletions ui/botPlay/src/setup/setupCtrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export default class SetupCtrl {
s.increment,
0,
this.redraw,
[],
);
}

Expand Down
5 changes: 0 additions & 5 deletions ui/lib/css/setup/_colorChoice.scss
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
.color-picker {
--button-size: 40px;
--king-size: 32px;
&__button {
width: 50px !important;
height: var(--button-size) !important;
padding: 4px 8px !important;

i {
display: block;
width: var(--king-size);
Expand Down
5 changes: 5 additions & 0 deletions ui/lib/src/setup/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@
export type InputValue = number;
// Visible value computed from the input value
export type RealValue = number;

export interface ClockConfig {
lim: number;
inc: number;
}
5 changes: 4 additions & 1 deletion ui/lib/src/setup/timeControl.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { propWithEffect, type Prop } from '@/index';
import type { InputValue, RealValue } from './interfaces';
import type { ClockConfig, InputValue, RealValue } from './interfaces';
import { clockToSpeed } from '@/game';

export type TimeMode = 'realTime' | 'correspondence' | 'unlimited';
Expand All @@ -14,6 +14,7 @@ export class TimeControl {
readonly timeV: Prop<InputValue>,
readonly incrementV: Prop<InputValue>,
readonly daysV: Prop<InputValue>,
readonly presets: ClockConfig[],
) {}

time: () => RealValue = () => timeVToTime(this.timeV());
Expand Down Expand Up @@ -50,13 +51,15 @@ export const timeControlFromStoredValues = (
inc: RealValue,
days: RealValue,
onChange: () => void,
presets: ClockConfig[],
): TimeControl =>
new TimeControl(
mode,
modes,
propWithEffect(sliderInitVal(time, timeVToTime, 100, 14), onChange),
propWithEffect(sliderInitVal(inc, incrementVToIncrement, 100, 5), onChange),
propWithEffect(sliderInitVal(days, daysVToDays, 20, 7), onChange),
presets,
);

export const timeModes: { id: number; key: TimeMode; name: string }[] = [
Expand Down
29 changes: 15 additions & 14 deletions ui/lib/src/setup/view/color.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,21 @@ export const blindModeColorPicker = (colorProp: ColorProp): VNode[] => [
];

export const colorButtons = (colorProp: ColorProp): VNode =>
hl('div.radio-pane', [
i18n.site.youPlayAs,
hl('div.config-group', [
hl('div.label', i18n.site.side),
hl(
'group.radio.color-picker',
colors.map(({ key, name }) => [
hl(`input#color-picker-${key}`, {
attrs: { name: 'color', type: 'radio', value: key, checked: key === colorProp() },
on: { change: () => colorProp(key) },
}),
hl(
`label.color-picker__button.${key}`,
{ attrs: { title: name, for: `color-picker-${key}` } },
hl('i'),
),
]),
'group.radio.color-picker.color-cards',
colors.map(({ key, name }) =>
hl('div', [
hl(`input#color-picker-${key}`, {
attrs: { name: 'color', type: 'radio', value: key, checked: colorProp() === key },
on: { change: () => colorProp(key) },
}),
hl(`label.card-radio`, { attrs: { for: `color-picker-${key}` } }, [
hl('div.color-picker__button', { class: { [key]: true } }, hl('i')),
hl('span.text', name),
]),
]),
),
),
]);
114 changes: 85 additions & 29 deletions ui/lib/src/setup/view/timeControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import type { InputValue } from '../interfaces';
import {
timeModes,
sliderTimes,
sliderInitVal,
timeVToTime,
incrementVToIncrement,
daysVToDays,
type TimeControl,
Expand Down Expand Up @@ -95,35 +97,89 @@ const inputRange = (min: number, max: number, prop: Prop<InputValue>, classes?:
hl('input.range', {
class: classes,
attrs: { type: 'range', min, max, value: prop() },
hook: {
update: (_: VNode, vnode: VNode) => {
const el = vnode.elm as HTMLInputElement;
el.value = prop().toString();
},
},
on: { input: (e: Event) => prop(parseFloat((e.target as HTMLInputElement).value)) },
});

export const timePickerAndSliders = (tc: TimeControl, minimumTimeRequiredIfReal: number = 0): VNode =>
hl(
'div.config-group',
site.blindMode
? blindModeTimePickers(tc)
: [
renderTimeModePicker(tc),
tc.mode() === 'realTime' &&
hl('div.time-choice.range', [
`${i18n.site.minutesPerSide}: `,
hl('span', showTime(tc.time())),
inputRange(0, 38, tc.timeV, {
failure: !tc.realTimeValid(minimumTimeRequiredIfReal),
}),
]),
tc.mode() === 'realTime'
? hl('div.increment-choice.range', [
`${i18n.site.incrementInSeconds}: `,
hl('span', `${tc.increment()}`),
inputRange(0, 30, tc.incrementV, { failure: !tc.realTimeValid(minimumTimeRequiredIfReal) }),
])
: tc.mode() === 'correspondence' &&
hl('div.days-choice.range', [
`${i18n.site.daysPerTurn}: `,
hl('span', `${tc.days()}`),
inputRange(1, 7, tc.daysV),
]),
],
);
export const timePickerAndSliders = (tc: TimeControl, minimumTimeRequiredIfReal: number = 0): VNode => {
if (site.blindMode) return hl('div.config-group', blindModeTimePickers(tc));

const activeMode = tc.mode();
const showTabs = tc.canSelectMode();

const tabs = showTabs
? hl(
'div.tabs-horiz',
tc.modes.map(mode =>
hl(
'span',
{
class: { active: activeMode === mode },
on: { click: () => tc.mode(mode) },
},
timeModes.find(m => m.key === mode)?.name || mode,
),
),
)
: null;

let panelContent: VNode | null = null;

if (activeMode === 'realTime') {
const [tcTime, tcIncrement] = [tc.time(), tc.increment()];
panelContent = hl('div.time-panel', [
hl('div.sliders-grid', [
hl('div.slider-container', [
hl('div.label-row', [hl('label', i18n.site.minutesPerSide), hl('span.val-box', showTime(tcTime))]),
inputRange(0, 38, tc.timeV, {
failure: !tc.realTimeValid(minimumTimeRequiredIfReal),
}),
]),
hl('div.slider-separator', '+'),
hl('div.slider-container', [
hl('div.label-row', [
hl('span.val-box', tcIncrement.toString()),
hl('label', i18n.site.incrementInSeconds),
]),
inputRange(0, 30, tc.incrementV, { failure: !tc.realTimeValid(minimumTimeRequiredIfReal) }),
]),
]),
hl(
'div.presets',
tc.presets.map(p =>
hl(
'button.preset-btn',
{
class: {
active: tcTime === p.lim && tcIncrement === p.inc,
},
on: {
click: () => {
tc.timeV(sliderInitVal(p.lim, timeVToTime, 100, 9));
tc.incrementV(sliderInitVal(p.inc, incrementVToIncrement, 100, 0));
},
},
},
`${showTime(p.lim)}+${p.inc}`,
),
),
),
]);
} else if (activeMode === 'correspondence') {
panelContent = hl('div.time-panel', [
hl('div.slider-container.full-width', [
hl('div.label-row', [hl('label', i18n.site.daysPerTurn), hl('span.val-box', tc.days().toString())]),
inputRange(1, 7, tc.daysV),
]),
]);
} else if (activeMode === 'unlimited') {
panelContent = hl('div.time-panel', i18n.site.unlimitedDescription);
}

return hl('div.config-group.time-control-tabs', [tabs, panelContent]);
};
Loading
Loading