diff --git a/public/global.d.ts b/public/global.d.ts index f23e05597..ea96ddffb 100644 --- a/public/global.d.ts +++ b/public/global.d.ts @@ -1,17 +1,14 @@ // Global namespace modules declare var DOMPurify; declare var droll; -declare var Fuse; declare var Handlebars; declare var hljs; declare var localforage; -declare var moment; declare var pdfjsLib; declare var Popper; declare var showdown; declare var showdownKatex; declare var SVGInject; -declare var toastr; declare var Readability; declare var isProbablyReaderable; declare var ePub; @@ -22,13 +19,1342 @@ interface JQuery { pagination(method: 'getCurrentPageNum'): number; pagination(method: string, options?: any): JQuery; pagination(options?: any): JQuery; - transition(options?: any): JQuery; - select2(options?: any): JQuery; - sortable(options?: any): JQuery; + transition(options?: any, complete?: function): JQuery; autocomplete(options?: any): JQuery; autocomplete(method: string, options?: any): JQuery; slider(options?: any): JQuery; slider(method: string, func: string, options?: any): JQuery; cropper(options?: any): JQuery; izoomify(options?: any): JQuery; + + //#region select2 + + /** + * Initializes or modifies a select2 instance with provided options + * + * @param options - Configuration options for the select2 instance + * @returns The jQuery object for chaining + */ + select2(options?: Select2Options): JQuery; + + /** + * Retrieves data currently selected in the select2 instance + * + * @param field - A string specifying the 'data' method + * @returns An array of selected items + */ + select2(field: 'data'): any[]; + + /** + * Calls the specified select2 method + * + * @param method - The name of the select2 method to invoke + * @returns The jQuery object for chaining + */ + select2(method: 'open' | 'close' | 'destroy' | 'focus' | 'val', value?: any): JQuery; + + //#endregion + + //#region sortable + + /** + * Initializes or updates a sortable instance with the provided options + * + * @param options - Configuration options for the sortable instance + * @returns The jQuery object for chaining + */ + sortable(options?: SortableOptions): JQuery; + + /** + * Calls a sortable method to perform actions on the instance + * + * @param method - The name of the sortable method to invoke + * @returns The jQuery object for chaining + */ + sortable(method: 'destroy' | 'disable' | 'enable' | 'refresh' | 'toArray'): JQuery; + + /** + * Retrieves the sortable's instance object. If the element does not have an associated instance, undefined is returned. + * + * @returns The instance of the sortable object + */ + sortable(method: 'instance'): object; + + /** + * Retrieves the current option value for the specified option + * + * @param method - The string 'option' to retrieve an option value + * @param optionName - The name of the option to retrieve + * @returns The value of the specified option + */ + sortable(method: 'option', optionName: string): any; + + /** + * Sets the value of the specified option + * + * @param method - The string 'option' to set an option value + * @param optionName - The name of the option to set + * @param value - The value to assign to the option + * @returns The jQuery object for chaining + */ + sortable(method: 'option', optionName: string, value: any): JQuery; + + /** + * Sets multiple options using an object + * + * @param method - The string 'option' to set options + * @param options - An object containing multiple option key-value pairs + * @returns The jQuery object for chaining + */ + sortable(method: 'option', options: SortableOptions): JQuery; + + //#endregion +} + +//#region Fuse + +/** + * Fuse.js provides fast and flexible fuzzy searching + * @constructor + * @param list - The list of items to search through + * @param options - Configuration options for the search algorithm + */ +declare var Fuse: { + new(list: any[], options?: FuseOptions): FuseInstance; +}; + +/** Instead of providing a (nested) key as a string, an object can be defined that can specify weight and a custom get function */ +interface FuseKey { + /** + * The name of they key. Supports nested paths. + */ + name: string; + /** + * You can allocate a weight to keys to give them higher (or lower) values in search results. The weight value has to be greater than 0. When a weight isn't provided, it will default to 1. + * @default 1 + */ + weight?: number; + /** + * Function to retrieve an object's value at the specified path. The default searches nested paths. + * @default (obj: T, path: string | string[]) => string | string[] + */ + getFn?: (any) => string; +} + +/** Configuration options for the Fuse search algorithm */ +interface FuseOptions { + /** + * List of keys that will be searched. Supports nested paths, weighted search, and searching in arrays of strings and objects. + * @default [] + */ + keys?: string[] | FuseKey[]; + + /** + * How much distance one character can be from another to be considered a match. + * @default 100 + */ + distance?: number; + + /** + * At what point the match algorithm gives up. A threshold of 0.0 requires a perfect match, while 1.0 matches everything. + * @default 0.6 + */ + threshold?: number; + + /** + * Whether the score should be included in the result set. A score of 0 indicates a perfect match, while a score of 1 indicates a complete mismatch. + * @default false + */ + includeScore?: boolean; + + /** + * Indicates whether comparisons should be case-sensitive. + * @default false + */ + isCaseSensitive?: boolean; + + /** + * Whether the matches should be included in the result set. When true, each record in the result set will include the indices of matched characters. + * @default false + */ + includeMatches?: boolean; + + /** + * Only matches whose length exceeds this value will be returned. + * @default 1 + */ + minMatchCharLength?: number; + + /** + * Whether to sort the result list by score. + * @default true + */ + shouldSort?: boolean; + + /** + * When true, the matching function will continue to the end of a search pattern even if a perfect match has already been found. + * @default false + */ + findAllMatches?: boolean; + + /** + * Determines approximately where in the text the pattern is expected to be found. + * @default 0 + */ + location?: number; + + /** + * When true, search will ignore location and distance, so it won't matter where in the string the pattern appears. + * @default false + */ + ignoreLocation?: boolean; + + /** + * When true, it enables the use of Unix-like search commands. + * @default false + */ + useExtendedSearch?: boolean; + + /** + * Function to retrieve an object's value at the specified path. The default searches nested paths. + * @default (obj: T, path: string | string[]) => string | string[] + */ + getFn?: (obj: any, path: string | string[]) => string | string[]; + + /** + * Function to sort the results. The default sorts by ascending relevance score. + * @default (a, b) => number + */ + sortFn?: (a: any, b: any) => number; + + /** + * When true, the calculation for the relevance score will ignore the field-length norm. + * @default false + */ + ignoreFieldNorm?: boolean; + + /** + * Determines how much the field-length norm affects scoring. 0 is equivalent to ignoring the field-length norm, while higher values increase the effect. + * @default 1 + */ + fieldNormWeight?: number; +} + + +/** Represents an individual Fuse search result */ +interface FuseResult { + /** The original item that was matched */ + item: any; + /** The index of the item from the original input collection that was searched */ + refIndex: number; + /** The search score, where 0 is a perfect match and 1 is the worst */ + score?: number; + /** Optional list of matched search keys */ + matches?: Array<{ key: string; indices: [number, number][] }>; +} + +/** Represents a Fuse instance, used for performing searches */ +interface FuseInstance { + /** + * Searches through the list using the specified query. + * @param query - The search term or phrase to use + * @returns An array of search results matching the query + */ + search(query: string): FuseResult[]; +} + +//#endregion + +//#region select2 + +/** Options for configuring a select2 instance */ +interface Select2Options { + /** + * Provides support for ajax data sources + * @param params - Parameters including the search term + * @param callback - A callback function to handle the results + * @default null + */ + ajax?: { + url: string; + dataType?: string; + delay?: number; + data?: (params: any) => any; + processResults?: (data: any, params: any) => any; + } | { transport: (params, success, failure) => any }; + + /** + * Provides support for clearable selections + * @default false + */ + allowClear?: boolean; + + /** + * See Using Select2 with AMD or CommonJS loaders + * @default './i18n/' + */ + amdLanguageBase?: string; + + /** + * Controls whether the dropdown is closed after a selection is made + * @default true + */ + closeOnSelect?: boolean; + + /** + * Allows rendering dropdown options from an array + * @default null + */ + data?: object[]; + + /** + * Used to override the built-in DataAdapter + * @default SelectAdapter + */ + dataAdapter?: SelectAdapter; + + /** + * Enable debugging messages in the browser console + * @default false + */ + debug?: boolean; + + /** + * Sets the dir attribute on the selection and dropdown containers to indicate the direction of the text + * @default 'ltr' + */ + dir?: string; + + /** + * When set to true, the select control will be disabled + * @default false + */ + disabled?: boolean; + + /** + * Used to override the built-in DropdownAdapter + * @default DropdownAdapter + */ + dropdownAdapter?: DropdownAdapter; + + /** + * @default false + */ + dropdownAutoWidth?: boolean; + + /** + * Adds additional CSS classes to the dropdown container. The helper :all: can be used to add all CSS classes present on the original element + * @default '' + */ + selectionCssClass?: string; + + /** + * Implements automatic selection when the dropdown is closed + * @default false + */ + selectOnClose?: boolean; + + sorter?: function; + + /** + * When set to `true`, allows the user to create new tags that aren't pre-populated + * Used to enable free text responses + * @default false + */ + tags?: boolean | object[]; + + /** + * Customizes the way that search results are rendered + * @param item - The item object to format + * @returns The formatted representation + * @default null + */ + templateResult?: (item: any) => JQuery | string; + + /** + * Customizes the way that selections are rendered + * @param item - The selected item object to format + * @returns The formatted representation + * @default null + */ + templateSelection?: (item: any) => JQuery | string; + + /** + * Allows you to set the theme + * @default 'default' + */ + theme?: string; + + /** + * A callback that handles automatic tokenization of free-text entry + * @default null + */ + tokenizer?: (input: { _type: string, term: string }, selection: { options: object }, callback: (Select2Option) => any) => { term: string }; + + /** + * The list of characters that should be used as token separators + * @default null + */ + tokenSeparators?: string[]; + + /** + * Supports customization of the container width + * @default 'resolve' + */ + width?: string; + + /** + * If true, resolves issue for multiselects using closeOnSelect: false that caused the list of results to scroll to the first selection after each select/unselect + * @default false + */ + scrollAfterSelect?: boolean; + + /** + * Extends Select2 v4 plugin by adding an option to set a placeholder for the 'search' input field + * [Custom Field] + * @default '' + */ + searchInputPlaceholder?: string; + + /** + * Extends select2 plugin by adding a custom css class for the 'searcH' input field + * [Custom Field] + * @default '' + */ + searchInputCssClass?: string; +} + +//#endregion + +//#region sortable + +/** Options for configuring a sortable instance */ +interface SortableOptions { + /** + * When set, prevents the sortable items from being dragged unless clicked with a delay + * @default 0 + */ + delay?: number; + + /** + * Class name for elements to handle sorting. Elements with this class can be dragged to sort. + * @default '' + */ + handle?: string; + + /** + * Whether to allow sorting between different connected lists + * @default false + */ + connectWith?: string | boolean; + + /** + * Function called when sorting starts + * @param event - The event object + * @param ui - The UI object containing the helper and position information + */ + start?: (event: Event, ui: SortableUI) => void; + + /** + * Function called when sorting stops + * @param event - The event object + * @param ui - The UI object containing the helper and position information + */ + stop?: (event: Event, ui: SortableUI) => void; + + /** + * Function called when sorting updates + * @param event - The event object + * @param ui - The UI object containing the helper and position information + */ + update?: (event: Event, ui: SortableUI) => void; + + /** + * Specifies which items inside the element should be sortable + * @default '> *' + */ + items?: string; +} + +/** UI object passed to sortable event handlers */ +interface SortableUI { + /** jQuery object representing the helper element */ + helper: JQuery; + /** The current position of the helper element */ + position: { top: number; left: number }; + /** Original position of the helper element */ + originalPosition: { top: number; left: number }; + /** jQuery object representing the item being sorted */ + item: JQuery; + /** The placeholder element used during sorting */ + placeholder: JQuery; +} + +//#endregion + +// MIT Licence. Copied from: +// https://github.com/moment/moment/blob/develop/ts3.1-typings/moment.d.ts +/** + * @param strict Strict parsing disables the deprecated fallback to the native Date constructor when + * parsing a string. + */ +declare function moment(inp?: moment.MomentInput, strict?: boolean): moment.Moment; +/** + * @param strict Strict parsing requires that the format and input match exactly, including delimiters. + * Strict parsing is frequently the best parsing option. For more information about choosing strict vs + * forgiving parsing, see the [parsing guide](https://momentjs.com/guides/#/parsing/). + */ +declare function moment(inp?: moment.MomentInput, format?: moment.MomentFormatSpecification, strict?: boolean): moment.Moment; +/** + * @param strict Strict parsing requires that the format and input match exactly, including delimiters. + * Strict parsing is frequently the best parsing option. For more information about choosing strict vs + * forgiving parsing, see the [parsing guide](https://momentjs.com/guides/#/parsing/). + */ +declare function moment(inp?: moment.MomentInput, format?: moment.MomentFormatSpecification, language?: string, strict?: boolean): moment.Moment; + +declare namespace moment { + type RelativeTimeKey = 's' | 'ss' | 'm' | 'mm' | 'h' | 'hh' | 'd' | 'dd' | 'w' | 'ww' | 'M' | 'MM' | 'y' | 'yy'; + type CalendarKey = 'sameDay' | 'nextDay' | 'lastDay' | 'nextWeek' | 'lastWeek' | 'sameElse' | string; + type LongDateFormatKey = 'LTS' | 'LT' | 'L' | 'LL' | 'LLL' | 'LLLL' | 'lts' | 'lt' | 'l' | 'll' | 'lll' | 'llll'; + + interface Locale { + calendar(key?: CalendarKey, m?: Moment, now?: Moment): string; + + longDateFormat(key: LongDateFormatKey): string; + invalidDate(): string; + ordinal(n: number): string; + + preparse(inp: string): string; + postformat(inp: string): string; + relativeTime(n: number, withoutSuffix: boolean, + key: RelativeTimeKey, isFuture: boolean): string; + pastFuture(diff: number, absRelTime: string): string; + set(config: Object): void; + + months(): string[]; + months(m: Moment, format?: string): string; + monthsShort(): string[]; + monthsShort(m: Moment, format?: string): string; + monthsParse(monthName: string, format: string, strict: boolean): number; + monthsRegex(strict: boolean): RegExp; + monthsShortRegex(strict: boolean): RegExp; + + week(m: Moment): number; + firstDayOfYear(): number; + firstDayOfWeek(): number; + + weekdays(): string[]; + weekdays(m: Moment, format?: string): string; + weekdaysMin(): string[]; + weekdaysMin(m: Moment): string; + weekdaysShort(): string[]; + weekdaysShort(m: Moment): string; + weekdaysParse(weekdayName: string, format: string, strict: boolean): number; + weekdaysRegex(strict: boolean): RegExp; + weekdaysShortRegex(strict: boolean): RegExp; + weekdaysMinRegex(strict: boolean): RegExp; + + isPM(input: string): boolean; + meridiem(hour: number, minute: number, isLower: boolean): string; + } + + interface StandaloneFormatSpec { + format: string[]; + standalone: string[]; + isFormat?: RegExp; + } + + interface WeekSpec { + dow: number; + doy?: number; + } + + type CalendarSpecVal = string | ((m?: MomentInput, now?: Moment) => string); + interface CalendarSpec { + sameDay?: CalendarSpecVal; + nextDay?: CalendarSpecVal; + lastDay?: CalendarSpecVal; + nextWeek?: CalendarSpecVal; + lastWeek?: CalendarSpecVal; + sameElse?: CalendarSpecVal; + + // any additional properties might be used with moment.calendarFormat + [x: string]: CalendarSpecVal | undefined; + } + + type RelativeTimeSpecVal = ( + string | + ((n: number, withoutSuffix: boolean, + key: RelativeTimeKey, isFuture: boolean) => string) + ); + type RelativeTimeFuturePastVal = string | ((relTime: string) => string); + + interface RelativeTimeSpec { + future?: RelativeTimeFuturePastVal; + past?: RelativeTimeFuturePastVal; + s?: RelativeTimeSpecVal; + ss?: RelativeTimeSpecVal; + m?: RelativeTimeSpecVal; + mm?: RelativeTimeSpecVal; + h?: RelativeTimeSpecVal; + hh?: RelativeTimeSpecVal; + d?: RelativeTimeSpecVal; + dd?: RelativeTimeSpecVal; + w?: RelativeTimeSpecVal; + ww?: RelativeTimeSpecVal; + M?: RelativeTimeSpecVal; + MM?: RelativeTimeSpecVal; + y?: RelativeTimeSpecVal; + yy?: RelativeTimeSpecVal; + } + + interface LongDateFormatSpec { + LTS: string; + LT: string; + L: string; + LL: string; + LLL: string; + LLLL: string; + + // lets forget for a sec that any upper/lower permutation will also work + lts?: string; + lt?: string; + l?: string; + ll?: string; + lll?: string; + llll?: string; + } + + type MonthWeekdayFn = (momentToFormat: Moment, format?: string) => string; + type WeekdaySimpleFn = (momentToFormat: Moment) => string; + + interface LocaleSpecification { + months?: string[] | StandaloneFormatSpec | MonthWeekdayFn; + monthsShort?: string[] | StandaloneFormatSpec | MonthWeekdayFn; + + weekdays?: string[] | StandaloneFormatSpec | MonthWeekdayFn; + weekdaysShort?: string[] | StandaloneFormatSpec | WeekdaySimpleFn; + weekdaysMin?: string[] | StandaloneFormatSpec | WeekdaySimpleFn; + + meridiemParse?: RegExp; + meridiem?: (hour: number, minute:number, isLower: boolean) => string; + + isPM?: (input: string) => boolean; + + longDateFormat?: LongDateFormatSpec; + calendar?: CalendarSpec; + relativeTime?: RelativeTimeSpec; + invalidDate?: string; + ordinal?: (n: number) => string; + ordinalParse?: RegExp; + + week?: WeekSpec; + + // Allow anything: in general any property that is passed as locale spec is + // put in the locale object so it can be used by locale functions + [x: string]: any; + } + + interface MomentObjectOutput { + years: number; + /* One digit */ + months: number; + /* Day of the month */ + date: number; + hours: number; + minutes: number; + seconds: number; + milliseconds: number; + } + interface argThresholdOpts { + ss?: number; + s?: number; + m?: number; + h?: number; + d?: number; + w?: number | null; + M?: number; + } + + interface Duration { + clone(): Duration; + + humanize(argWithSuffix?: boolean, argThresholds?: argThresholdOpts): string; + + humanize(argThresholds?: argThresholdOpts): string; + + abs(): Duration; + + as(units: unitOfTime.Base): number; + get(units: unitOfTime.Base): number; + + milliseconds(): number; + asMilliseconds(): number; + + seconds(): number; + asSeconds(): number; + + minutes(): number; + asMinutes(): number; + + hours(): number; + asHours(): number; + + days(): number; + asDays(): number; + + weeks(): number; + asWeeks(): number; + + months(): number; + asMonths(): number; + + years(): number; + asYears(): number; + + add(inp?: DurationInputArg1, unit?: DurationInputArg2): Duration; + subtract(inp?: DurationInputArg1, unit?: DurationInputArg2): Duration; + + locale(): string; + locale(locale: LocaleSpecifier): Duration; + localeData(): Locale; + + toISOString(): string; + toJSON(): string; + + isValid(): boolean; + + /** + * @deprecated since version 2.8.0 + */ + lang(locale: LocaleSpecifier): Moment; + /** + * @deprecated since version 2.8.0 + */ + lang(): Locale; + /** + * @deprecated + */ + toIsoString(): string; + } + + interface MomentRelativeTime { + future: any; + past: any; + s: any; + ss: any; + m: any; + mm: any; + h: any; + hh: any; + d: any; + dd: any; + M: any; + MM: any; + y: any; + yy: any; + } + + interface MomentLongDateFormat { + L: string; + LL: string; + LLL: string; + LLLL: string; + LT: string; + LTS: string; + + l?: string; + ll?: string; + lll?: string; + llll?: string; + lt?: string; + lts?: string; + } + + interface MomentParsingFlags { + empty: boolean; + unusedTokens: string[]; + unusedInput: string[]; + overflow: number; + charsLeftOver: number; + nullInput: boolean; + invalidMonth: string | null; + invalidFormat: boolean; + userInvalidated: boolean; + iso: boolean; + parsedDateParts: any[]; + meridiem: string | null; + } + + interface MomentParsingFlagsOpt { + empty?: boolean; + unusedTokens?: string[]; + unusedInput?: string[]; + overflow?: number; + charsLeftOver?: number; + nullInput?: boolean; + invalidMonth?: string; + invalidFormat?: boolean; + userInvalidated?: boolean; + iso?: boolean; + parsedDateParts?: any[]; + meridiem?: string | null; + } + + interface MomentBuiltinFormat { + __momentBuiltinFormatBrand: any; + } + + type MomentFormatSpecification = string | MomentBuiltinFormat | (string | MomentBuiltinFormat)[]; + + namespace unitOfTime { + type Base = ( + "year" | "years" | "y" | + "month" | "months" | "M" | + "week" | "weeks" | "w" | + "day" | "days" | "d" | + "hour" | "hours" | "h" | + "minute" | "minutes" | "m" | + "second" | "seconds" | "s" | + "millisecond" | "milliseconds" | "ms" + ); + + type _quarter = "quarter" | "quarters" | "Q"; + type _isoWeek = "isoWeek" | "isoWeeks" | "W"; + type _date = "date" | "dates" | "D"; + type DurationConstructor = Base | _quarter; + + type DurationAs = Base; + + type StartOf = Base | _quarter | _isoWeek | _date | null; + + type Diff = Base | _quarter; + + type MomentConstructor = Base | _date; + + type All = Base | _quarter | _isoWeek | _date | + "weekYear" | "weekYears" | "gg" | + "isoWeekYear" | "isoWeekYears" | "GG" | + "dayOfYear" | "dayOfYears" | "DDD" | + "weekday" | "weekdays" | "e" | + "isoWeekday" | "isoWeekdays" | "E"; + } + + interface MomentInputObject { + years?: number; + year?: number; + y?: number; + + months?: number; + month?: number; + M?: number; + + days?: number; + day?: number; + d?: number; + + dates?: number; + date?: number; + D?: number; + + hours?: number; + hour?: number; + h?: number; + + minutes?: number; + minute?: number; + m?: number; + + seconds?: number; + second?: number; + s?: number; + + milliseconds?: number; + millisecond?: number; + ms?: number; + } + + interface DurationInputObject extends MomentInputObject { + quarters?: number; + quarter?: number; + Q?: number; + + weeks?: number; + week?: number; + w?: number; + } + + interface MomentSetObject extends MomentInputObject { + weekYears?: number; + weekYear?: number; + gg?: number; + + isoWeekYears?: number; + isoWeekYear?: number; + GG?: number; + + quarters?: number; + quarter?: number; + Q?: number; + + weeks?: number; + week?: number; + w?: number; + + isoWeeks?: number; + isoWeek?: number; + W?: number; + + dayOfYears?: number; + dayOfYear?: number; + DDD?: number; + + weekdays?: number; + weekday?: number; + e?: number; + + isoWeekdays?: number; + isoWeekday?: number; + E?: number; + } + + interface FromTo { + from: MomentInput; + to: MomentInput; + } + + type MomentInput = Moment | Date | string | number | (number | string)[] | MomentInputObject | null | undefined; + type DurationInputArg1 = Duration | number | string | FromTo | DurationInputObject | null | undefined; + type DurationInputArg2 = unitOfTime.DurationConstructor; + type LocaleSpecifier = string | Moment | Duration | string[] | boolean; + + interface MomentCreationData { + input: MomentInput; + format?: MomentFormatSpecification; + locale: Locale; + isUTC: boolean; + strict?: boolean; + } + + interface Moment extends Object { + format(format?: string): string; + + startOf(unitOfTime: unitOfTime.StartOf): Moment; + endOf(unitOfTime: unitOfTime.StartOf): Moment; + + add(amount?: DurationInputArg1, unit?: DurationInputArg2): Moment; + /** + * @deprecated reverse syntax + */ + add(unit: unitOfTime.DurationConstructor, amount: number|string): Moment; + + subtract(amount?: DurationInputArg1, unit?: DurationInputArg2): Moment; + /** + * @deprecated reverse syntax + */ + subtract(unit: unitOfTime.DurationConstructor, amount: number|string): Moment; + + calendar(): string; + calendar(formats: CalendarSpec): string; + calendar(time?: MomentInput, formats?: CalendarSpec): string; + + clone(): Moment; + + /** + * @return Unix timestamp in milliseconds + */ + valueOf(): number; + + // current date/time in local mode + local(keepLocalTime?: boolean): Moment; + isLocal(): boolean; + + // current date/time in UTC mode + utc(keepLocalTime?: boolean): Moment; + isUTC(): boolean; + /** + * @deprecated use isUTC + */ + isUtc(): boolean; + + parseZone(): Moment; + isValid(): boolean; + invalidAt(): number; + + hasAlignedHourOffset(other?: MomentInput): boolean; + + creationData(): MomentCreationData; + parsingFlags(): MomentParsingFlags; + + year(y: number): Moment; + year(): number; + /** + * @deprecated use year(y) + */ + years(y: number): Moment; + /** + * @deprecated use year() + */ + years(): number; + quarter(): number; + quarter(q: number): Moment; + quarters(): number; + quarters(q: number): Moment; + month(M: number|string): Moment; + month(): number; + /** + * @deprecated use month(M) + */ + months(M: number|string): Moment; + /** + * @deprecated use month() + */ + months(): number; + day(d: number|string): Moment; + day(): number; + days(d: number|string): Moment; + days(): number; + date(d: number): Moment; + date(): number; + /** + * @deprecated use date(d) + */ + dates(d: number): Moment; + /** + * @deprecated use date() + */ + dates(): number; + hour(h: number): Moment; + hour(): number; + hours(h: number): Moment; + hours(): number; + minute(m: number): Moment; + minute(): number; + minutes(m: number): Moment; + minutes(): number; + second(s: number): Moment; + second(): number; + seconds(s: number): Moment; + seconds(): number; + millisecond(ms: number): Moment; + millisecond(): number; + milliseconds(ms: number): Moment; + milliseconds(): number; + weekday(): number; + weekday(d: number): Moment; + isoWeekday(): number; + isoWeekday(d: number|string): Moment; + weekYear(): number; + weekYear(d: number): Moment; + isoWeekYear(): number; + isoWeekYear(d: number): Moment; + week(): number; + week(d: number): Moment; + weeks(): number; + weeks(d: number): Moment; + isoWeek(): number; + isoWeek(d: number): Moment; + isoWeeks(): number; + isoWeeks(d: number): Moment; + weeksInYear(): number; + isoWeeksInYear(): number; + isoWeeksInISOWeekYear(): number; + dayOfYear(): number; + dayOfYear(d: number): Moment; + + from(inp: MomentInput, suffix?: boolean): string; + to(inp: MomentInput, suffix?: boolean): string; + fromNow(withoutSuffix?: boolean): string; + toNow(withoutPrefix?: boolean): string; + + diff(b: MomentInput, unitOfTime?: unitOfTime.Diff, precise?: boolean): number; + + toArray(): [number, number, number, number, number, number, number]; + toDate(): Date; + toISOString(keepOffset?: boolean): string; + inspect(): string; + toJSON(): string; + unix(): number; + + isLeapYear(): boolean; + /** + * @deprecated in favor of utcOffset + */ + zone(): number; + zone(b: number|string): Moment; + utcOffset(): number; + utcOffset(b: number|string, keepLocalTime?: boolean): Moment; + isUtcOffset(): boolean; + daysInMonth(): number; + isDST(): boolean; + + zoneAbbr(): string; + zoneName(): string; + + isBefore(inp?: MomentInput, granularity?: unitOfTime.StartOf): boolean; + isAfter(inp?: MomentInput, granularity?: unitOfTime.StartOf): boolean; + isSame(inp?: MomentInput, granularity?: unitOfTime.StartOf): boolean; + isSameOrAfter(inp?: MomentInput, granularity?: unitOfTime.StartOf): boolean; + isSameOrBefore(inp?: MomentInput, granularity?: unitOfTime.StartOf): boolean; + isBetween(a: MomentInput, b: MomentInput, granularity?: unitOfTime.StartOf, inclusivity?: "()" | "[)" | "(]" | "[]"): boolean; + + /** + * @deprecated as of 2.8.0, use locale + */ + lang(language: LocaleSpecifier): Moment; + /** + * @deprecated as of 2.8.0, use locale + */ + lang(): Locale; + + locale(): string; + locale(locale: LocaleSpecifier): Moment; + + localeData(): Locale; + + /** + * @deprecated no reliable implementation + */ + isDSTShifted(): boolean; + + // NOTE(constructor): Same as moment constructor + /** + * @deprecated as of 2.7.0, use moment.min/max + */ + max(inp?: MomentInput, format?: MomentFormatSpecification, strict?: boolean): Moment; + /** + * @deprecated as of 2.7.0, use moment.min/max + */ + max(inp?: MomentInput, format?: MomentFormatSpecification, language?: string, strict?: boolean): Moment; + + // NOTE(constructor): Same as moment constructor + /** + * @deprecated as of 2.7.0, use moment.min/max + */ + min(inp?: MomentInput, format?: MomentFormatSpecification, strict?: boolean): Moment; + /** + * @deprecated as of 2.7.0, use moment.min/max + */ + min(inp?: MomentInput, format?: MomentFormatSpecification, language?: string, strict?: boolean): Moment; + + get(unit: unitOfTime.All): number; + set(unit: unitOfTime.All, value: number): Moment; + set(objectLiteral: MomentSetObject): Moment; + + toObject(): MomentObjectOutput; + } + + export var version: string; + export var fn: Moment; + + // NOTE(constructor): Same as moment constructor + /** + * @param strict Strict parsing disables the deprecated fallback to the native Date constructor when + * parsing a string. + */ + export function utc(inp?: MomentInput, strict?: boolean): Moment; + /** + * @param strict Strict parsing requires that the format and input match exactly, including delimiters. + * Strict parsing is frequently the best parsing option. For more information about choosing strict vs + * forgiving parsing, see the [parsing guide](https://momentjs.com/guides/#/parsing/). + */ + export function utc(inp?: MomentInput, format?: MomentFormatSpecification, strict?: boolean): Moment; + /** + * @param strict Strict parsing requires that the format and input match exactly, including delimiters. + * Strict parsing is frequently the best parsing option. For more information about choosing strict vs + * forgiving parsing, see the [parsing guide](https://momentjs.com/guides/#/parsing/). + */ + export function utc(inp?: MomentInput, format?: MomentFormatSpecification, language?: string, strict?: boolean): Moment; + + export function unix(timestamp: number): Moment; + + export function invalid(flags?: MomentParsingFlagsOpt): Moment; + export function isMoment(m: any): m is Moment; + export function isDate(m: any): m is Date; + export function isDuration(d: any): d is Duration; + + /** + * @deprecated in 2.8.0 + */ + export function lang(language?: string): string; + /** + * @deprecated in 2.8.0 + */ + export function lang(language?: string, definition?: Locale): string; + + export function locale(language?: string): string; + export function locale(language?: string[]): string; + export function locale(language?: string, definition?: LocaleSpecification | null | undefined): string; + + export function localeData(key?: string | string[]): Locale; + + export function duration(inp?: DurationInputArg1, unit?: DurationInputArg2): Duration; + + // NOTE(constructor): Same as moment constructor + export function parseZone(inp?: MomentInput, format?: MomentFormatSpecification, strict?: boolean): Moment; + export function parseZone(inp?: MomentInput, format?: MomentFormatSpecification, language?: string, strict?: boolean): Moment; + + export function months(): string[]; + export function months(index: number): string; + export function months(format: string): string[]; + export function months(format: string, index: number): string; + export function monthsShort(): string[]; + export function monthsShort(index: number): string; + export function monthsShort(format: string): string[]; + export function monthsShort(format: string, index: number): string; + + export function weekdays(): string[]; + export function weekdays(index: number): string; + export function weekdays(format: string): string[]; + export function weekdays(format: string, index: number): string; + export function weekdays(localeSorted: boolean): string[]; + export function weekdays(localeSorted: boolean, index: number): string; + export function weekdays(localeSorted: boolean, format: string): string[]; + export function weekdays(localeSorted: boolean, format: string, index: number): string; + export function weekdaysShort(): string[]; + export function weekdaysShort(index: number): string; + export function weekdaysShort(format: string): string[]; + export function weekdaysShort(format: string, index: number): string; + export function weekdaysShort(localeSorted: boolean): string[]; + export function weekdaysShort(localeSorted: boolean, index: number): string; + export function weekdaysShort(localeSorted: boolean, format: string): string[]; + export function weekdaysShort(localeSorted: boolean, format: string, index: number): string; + export function weekdaysMin(): string[]; + export function weekdaysMin(index: number): string; + export function weekdaysMin(format: string): string[]; + export function weekdaysMin(format: string, index: number): string; + export function weekdaysMin(localeSorted: boolean): string[]; + export function weekdaysMin(localeSorted: boolean, index: number): string; + export function weekdaysMin(localeSorted: boolean, format: string): string[]; + export function weekdaysMin(localeSorted: boolean, format: string, index: number): string; + + export function min(moments: Moment[]): Moment; + export function min(...moments: Moment[]): Moment; + export function max(moments: Moment[]): Moment; + export function max(...moments: Moment[]): Moment; + + /** + * Returns unix time in milliseconds. Overwrite for profit. + */ + export function now(): number; + + export function defineLocale(language: string, localeSpec: LocaleSpecification | null): Locale; + export function updateLocale(language: string, localeSpec: LocaleSpecification | null): Locale; + + export function locales(): string[]; + + export function normalizeUnits(unit: unitOfTime.All): string; + export function relativeTimeThreshold(threshold: string): number | boolean; + export function relativeTimeThreshold(threshold: string, limit: number): boolean; + export function relativeTimeRounding(fn: (num: number) => number): boolean; + export function relativeTimeRounding(): (num: number) => number; + export function calendarFormat(m: Moment, now: Moment): string; + + export function parseTwoDigitYear(input: string): number; + /** + * Constant used to enable explicit ISO_8601 format parsing. + */ + export var ISO_8601: MomentBuiltinFormat; + export var RFC_2822: MomentBuiltinFormat; + + export var defaultFormat: string; + export var defaultFormatUtc: string; + + export var suppressDeprecationWarnings: boolean; + export var deprecationHandler: ((name: string | null, msg: string) => void) | null | undefined; + + export var HTML5_FMT: { + DATETIME_LOCAL: string, + DATETIME_LOCAL_SECONDS: string, + DATETIME_LOCAL_MS: string, + DATE: string, + TIME: string, + TIME_SECONDS: string, + TIME_MS: string, + WEEK: string, + MONTH: string + }; + +} + +declare global { + const moment: typeof moment; } diff --git a/public/index.html b/public/index.html index a515020d1..ec9b8f7a1 100644 --- a/public/index.html +++ b/public/index.html @@ -1239,7 +1239,7 @@ -
+
Min Length diff --git a/public/script.js b/public/script.js index 29ce43104..84d84a06d 100644 --- a/public/script.js +++ b/public/script.js @@ -288,6 +288,11 @@ DOMPurify.addHook('uponSanitizeElement', (node, _, config) => { return; } + // Replace line breaks with
in unknown elements + if (node instanceof HTMLUnknownElement) { + node.innerHTML = node.innerHTML.replaceAll('\n', '
'); + } + const isMediaAllowed = isExternalMediaAllowed(); if (isMediaAllowed) { return; @@ -458,6 +463,7 @@ let currentVersion = '0.0.0'; export const default_ch_mes = 'Hello'; let generatedPromptCache = ''; let generation_started = new Date(); +/** @type {import('scripts/char-data.js').v1CharData[]} */ export let characters = []; export let this_chid; let saveCharactersPage = 0; @@ -783,7 +789,6 @@ export let novelai_setting_names; let abortController; //css -var css_mes_bg = $('
').css('background'); var css_send_form_display = $('
').css('display'); const MAX_GENERATION_LOOPS = 5; @@ -811,6 +816,28 @@ $.ajaxPrefilter((options, originalOptions, xhr) => { xhr.setRequestHeader('X-CSRF-Token', token); }); +/** + * Pings the STserver to check if it is reachable. + * @returns {Promise} True if the server is reachable, false otherwise. + */ +export async function pingServer() { + try { + const result = await fetch('api/ping', { + method: 'GET', + headers: getRequestHeaders(), + }); + + if (!result.ok) { + return false; + } + + return true; + } catch (error) { + console.error('Error pinging server', error); + return false; + } +} + async function firstLoadInit() { try { const tokenResponse = await fetch('/csrf-token'); @@ -3077,6 +3104,15 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro } if (!dryRun) { + // Ping server to make sure it is still alive + const pingResult = await pingServer(); + + if (!pingResult) { + unblockGeneration(type); + toastr.error('Verify that the server is running and accessible.', 'ST Server cannot be reached' ); + throw new Error('Server unreachable'); + } + // Hide swipes if not in a dry run. hideSwipeButtons(); // If generated any message, set the flag to indicate it can't be recreated again. @@ -8580,19 +8616,17 @@ jQuery(async function () { } $('.mes').children('.del_checkbox').each(function () { $(this).prop('checked', false); - $(this).parent().css('background', css_mes_bg); + $(this).parent().removeClass('selected'); }); - $(this).css('background', '#600'); //sets the bg of the mes selected for deletion + $(this).addClass('selected'); //sets the bg of the mes selected for deletion var i = Number($(this).attr('mesid')); //checks the message ID in the chat this_del_mes = i; + //as long as the current message ID is less than the total chat length while (i < chat.length) { - //as long as the current message ID is less than the total chat length - $('.mes[mesid=\'' + i + '\']').css('background', '#600'); //sets the bg of the all msgs BELOW the selected .mes - $('.mes[mesid=\'' + i + '\']') - .children('.del_checkbox') - .prop('checked', true); + //sets the bg of the all msgs BELOW the selected .mes + $(`.mes[mesid="${i}"]`).addClass('selected'); + $(`.mes[mesid="${i}"]`).children('.del_checkbox').prop('checked', true); i++; - //console.log(i); } }); @@ -9196,7 +9230,7 @@ jQuery(async function () { $('.del_checkbox').each(function () { $(this).css('display', 'none'); $(this).parent().children('.for_checkbox').css('display', 'block'); - $(this).parent().css('background', css_mes_bg); + $(this).parent().removeClass('selected'); $(this).prop('checked', false); }); showSwipeButtons(); @@ -9211,22 +9245,19 @@ jQuery(async function () { $('.del_checkbox').each(function () { $(this).css('display', 'none'); $(this).parent().children('.for_checkbox').css('display', 'block'); - $(this).parent().css('background', css_mes_bg); + $(this).parent().removeClass('selected'); $(this).prop('checked', false); }); if (this_del_mes >= 0) { - $('.mes[mesid=\'' + this_del_mes + '\']') - .nextAll('div') - .remove(); - $('.mes[mesid=\'' + this_del_mes + '\']').remove(); + $(`.mes[mesid="${this_del_mes}"]`).nextAll('div').remove(); + $(`.mes[mesid="${this_del_mes}"]`).remove(); chat.length = this_del_mes; await saveChatConditional(); - var $textchat = $('#chat'); - $textchat.scrollTop($textchat[0].scrollHeight); + chatElement.scrollTop(chatElement[0].scrollHeight); eventSource.emit(event_types.MESSAGE_DELETED, chat.length); + $('#chat .mes').removeClass('last_mes'); $('#chat .mes').last().addClass('last_mes'); - $('#chat .mes').eq(-2).removeClass('last_mes'); } else { console.log('this_del_mes is not >= 0, not deleting'); } diff --git a/public/scripts/char-data.js b/public/scripts/char-data.js new file mode 100644 index 000000000..fa2f03382 --- /dev/null +++ b/public/scripts/char-data.js @@ -0,0 +1,94 @@ +/** + * @typedef {object} v2DataWorldInfoEntry + * @property {string[]} keys - An array of primary keys associated with the entry. + * @property {string[]} secondary_keys - An array of secondary keys associated with the entry (optional). + * @property {string} comment - A human-readable description or explanation for the entry. + * @property {string} content - The main content or data associated with the entry. + * @property {boolean} constant - Indicates if the entry's content is fixed and unchangeable. + * @property {boolean} selective - Indicates if the entry's inclusion is controlled by specific conditions. + * @property {number} insertion_order - Defines the order in which the entry is inserted during processing. + * @property {boolean} enabled - Controls whether the entry is currently active and used. + * @property {string} position - Specifies the location or context where the entry applies. + * @property {v2DataWorldInfoEntryExtensionInfos} extensions - An object containing additional details for extensions associated with the entry. + * @property {number} id - A unique identifier assigned to the entry. + */ +/** + * @typedef {object} v2DataWorldInfoEntryExtensionInfos + * @property {number} position - The order in which the extension is applied relative to other extensions. + * @property {boolean} exclude_recursion - Prevents the extension from being applied recursively. + * @property {number} probability - The chance (between 0 and 1) of the extension being applied. + * @property {boolean} useProbability - Determines if the `probability` property is used. + * @property {number} depth - The maximum level of nesting allowed for recursive application of the extension. + * @property {number} selectiveLogic - Defines the logic used to determine if the extension is applied selectively. + * @property {string} group - A category or grouping for the extension. + * @property {boolean} group_override - Overrides any existing group assignment for the extension. + * @property {number} group_weight - A value used for prioritizing extensions within the same group. + * @property {boolean} prevent_recursion - Completely disallows recursive application of the extension. + * @property {number} scan_depth - The maximum depth to search for matches when applying the extension. + * @property {boolean} match_whole_words - Specifies if only entire words should be matched during extension application. + * @property {boolean} use_group_scoring - Indicates if group weight is considered when selecting extensions. + * @property {boolean} case_sensitive - Controls whether case sensitivity is applied during matching for the extension. + * @property {string} automation_id - An identifier used for automation purposes related to the extension. + * @property {number} role - The specific function or purpose of the extension. + * @property {boolean} vectorized - Indicates if the extension is optimized for vectorized processing. + * @property {number} display_index - The order in which the extension should be displayed for user interfaces. + */ + +/** + * @typedef {object} v2WorldInfoBook + * @property {string} name - the name of the book + * @property {v2DataWorldInfoEntry[]} entries - the entries of the book + */ + +/** + * @typedef {object} v2CharData + * @property {string} name - The character's name. + * @property {string} description - A brief description of the character. + * @property {string} character_version - The character's data version. + * @property {string} personality - A short summary of the character's personality traits. + * @property {string} scenario - A description of the character's background or setting. + * @property {string} first_mes - The character's opening message in a conversation. + * @property {string} mes_example - An example message demonstrating the character's conversation style. + * @property {string} creator_notes - Internal notes or comments left by the character's creator. + * @property {string[]} tags - A list of keywords or labels associated with the character. + * @property {string} system_prompt - The system prompt used to interact with the character. + * @property {string} post_history_instructions - Instructions for handling the character's conversation history. + * @property {string} creator - The name of the person who created the character. + * @property {string[]} alternate_greetings - Additional greeting messages the character can use. + * @property {v2WorldInfoBook} character_book - Data about the character's world or story (if applicable). + * @property {v2CharDataExtensionInfos} extensions - Additional details specific to the character. + */ +/** + * @typedef {object} v2CharDataExtensionInfos + * @property {number} talkativeness - A numerical value indicating the character's propensity to talk. + * @property {boolean} fav - A flag indicating whether the character is a favorite. + * @property {string} world - The fictional world or setting where the character exists (if applicable). + * @property {object} depth_prompt - Prompts used to explore the character's depth and complexity. + * @property {number} depth_prompt.depth - The level of detail or nuance targeted by the prompt. + * @property {string} depth_prompt.prompt - The actual prompt text used for deeper character interaction. + * @property {"system" | "user" | "assistant"} depth_prompt.role - The role the character takes on during the prompted interaction (system, user, or assistant). + * // Non-standard extensions added by external tools + * @property {string} [pygmalion_id] - The unique identifier assigned to the character by the Pygmalion.chat. + * @property {{full_path: string}} [chub] - The Chub-specific data associated with the character. + */ + +/** + * @typedef {object} v1CharData + * @property {string} name - the name of the character + * @property {string} description - the description of the character + * @property {string} personality - a short personality description of the character + * @property {string} scenario - a scenario description of the character + * @property {string} first_mes - the first message in the conversation + * @property {string} mes_example - the example message in the conversation + * @property {string} creatorcomment - creator's notes of the character + * @property {string[]} tags - the tags of the character + * @property {number} talkativeness - talkativeness + * @property {boolean|string} fav - fav + * @property {string} create_date - create_date + * @property {v2CharData} data - v2 data extension + * // Non-standard extensions added by the ST server (not part of the original data) + * @property {string} chat - name of the current chat file chat + * @property {string} avatar - file name of the avatar image (acts as a unique identifier) + * @property {string} json_data - the full raw JSON data of the character + */ +export default 0;// now this file is a module diff --git a/public/scripts/extensions/expressions/settings.html b/public/scripts/extensions/expressions/settings.html index e8b1484b2..abb0219ce 100644 --- a/public/scripts/extensions/expressions/settings.html +++ b/public/scripts/extensions/expressions/settings.html @@ -64,10 +64,6 @@
-

- Sprite set:  -

-

Hint: Create new folder in the /characters/ folder of your user data directory and name it as the name of the character. Put images with expressions there. File names should follow the pattern: [expression_label].[image_format]

+

+ Sprite set:  +

+
+
diff --git a/public/scripts/macros.js b/public/scripts/macros.js index d38b1b163..759d91ed1 100644 --- a/public/scripts/macros.js +++ b/public/scripts/macros.js @@ -259,6 +259,26 @@ function diceRollReplace(input, invalidRollPlaceholder = '') { }); } +/** + * Returns the difference between two times. Works with any time format acceptable by moment(). + * Can work with {{date}} {{time}} macros + * @param {string} input - The string to replace time difference macros in. + * @returns {string} The string with replaced time difference macros. + */ +function timeDiffReplace(input) { + const timeDiffPattern = /{{timeDiff::(.*?)::(.*?)}}/gi; + + const output = input.replace(timeDiffPattern, (_match, matchPart1, matchPart2) => { + const time1 = moment(matchPart1); + const time2 = moment(matchPart2); + + const timeDifference = moment.duration(time1.diff(time2)); + return timeDifference.humanize(); + }); + + return output; +} + /** * Substitutes {{macro}} parameters in a string. * @param {string} content - The string to substitute parameters in. @@ -330,6 +350,7 @@ export function evaluateMacros(content, env) { const utcTime = moment().utc().utcOffset(utcOffset).format('LT'); return utcTime; }); + content = timeDiffReplace(content); content = bannedWordsReplace(content); content = randomReplace(content); content = pickReplace(content, rawContent); diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js index 0bdcf5e94..ec2a5868e 100644 --- a/public/scripts/power-user.js +++ b/public/scripts/power-user.js @@ -1865,7 +1865,7 @@ function highlightDefaultContext() { /** * Fuzzy search characters by a search term * @param {string} searchValue - The search term - * @returns {{item?: *, refIndex: number, score: number}[]} Results as items with their score + * @returns {FuseResult[]} Results as items with their score */ export function fuzzySearchCharacters(searchValue) { // @ts-ignore @@ -1898,7 +1898,7 @@ export function fuzzySearchCharacters(searchValue) { * Fuzzy search world info entries by a search term * @param {*[]} data - WI items data array * @param {string} searchValue - The search term - * @returns {{item?: *, refIndex: number, score: number}[]} Results as items with their score + * @returns {FuseResult[]} Results as items with their score */ export function fuzzySearchWorldInfo(data, searchValue) { // @ts-ignore @@ -1927,7 +1927,7 @@ export function fuzzySearchWorldInfo(data, searchValue) { * Fuzzy search persona entries by a search term * @param {*[]} data - persona data array * @param {string} searchValue - The search term - * @returns {{item?: *, refIndex: number, score: number}[]} Results as items with their score + * @returns {FuseResult[]} Results as items with their score */ export function fuzzySearchPersonas(data, searchValue) { data = data.map(x => ({ key: x, name: power_user.personas[x] ?? '', description: power_user.persona_descriptions[x]?.description ?? '' })); @@ -1951,7 +1951,7 @@ export function fuzzySearchPersonas(data, searchValue) { /** * Fuzzy search tags by a search term * @param {string} searchValue - The search term - * @returns {{item?: *, refIndex: number, score: number}[]} Results as items with their score + * @returns {FuseResult[]} Results as items with their score */ export function fuzzySearchTags(searchValue) { // @ts-ignore @@ -1973,7 +1973,7 @@ export function fuzzySearchTags(searchValue) { /** * Fuzzy search groups by a search term * @param {string} searchValue - The search term - * @returns {{item?: *, refIndex: number, score: number}[]} Results as items with their score + * @returns {FuseResult[]} Results as items with their score */ export function fuzzySearchGroups(searchValue) { // @ts-ignore diff --git a/public/scripts/templates/macros.html b/public/scripts/templates/macros.html index dbb50db80..3d5fa6937 100644 --- a/public/scripts/templates/macros.html +++ b/public/scripts/templates/macros.html @@ -33,6 +33,7 @@
  • {{isodate}} – the current ISO date (YYYY-MM-DD)
  • {{datetimeformat …}} – the current date/time in the specified format, e. g. for German date/time: {{datetimeformat DD.MM.YYYY HH:mm}}
  • {{time_UTC±#}} – the current time in the specified UTC time zone offset, e.g. UTC-4 or UTC+2
  • +
  • {{timeDiff::(time1)::(time2)}} – the time difference between time1 and time2. Accepts time and date macros. (Ex: {{timeDiff::{{isodate}} {{time}}::2024/5/11 12:30:00}})
  • {{idle_duration}} – the time since the last user message was sent
  • {{bias "text here"}} – sets a behavioral bias for the AI until the next user input. Quotes around the text are important.
  • {{roll:(formula)}} – rolls a dice. (ex: >{{roll:1d6}} will roll a 6-sided dice and return a number between 1 and 6)
  • diff --git a/public/scripts/textgen-settings.js b/public/scripts/textgen-settings.js index 588a52fb5..8cd76769c 100644 --- a/public/scripts/textgen-settings.js +++ b/public/scripts/textgen-settings.js @@ -328,15 +328,20 @@ function getTokenizerForTokenIds() { } /** - * @returns {string} String with comma-separated banned token IDs + * @typedef {{banned_tokens: string, banned_strings: string[]}} TokenBanResult + * @returns {TokenBanResult} String with comma-separated banned token IDs */ function getCustomTokenBans() { if (!settings.banned_tokens && !textgenerationwebui_banned_in_macros.length) { - return ''; + return { + banned_tokens: '', + banned_strings: [], + }; } const tokenizer = getTokenizerForTokenIds(); - const result = []; + const banned_tokens = []; + const banned_strings = []; const sequences = settings.banned_tokens .split('\n') .concat(textgenerationwebui_banned_in_macros) @@ -358,24 +363,31 @@ function getCustomTokenBans() { const tokens = JSON.parse(line); if (Array.isArray(tokens) && tokens.every(t => Number.isInteger(t))) { - result.push(...tokens); + banned_tokens.push(...tokens); } else { throw new Error('Not an array of integers'); } } catch (err) { console.log(`Failed to parse bad word token list: ${line}`, err); } + } else if (line.startsWith('"') && line.endsWith('"')) { + // Remove the enclosing quotes + + banned_strings.push(line.slice(1, -1)) } else { try { const tokens = getTextTokens(tokenizer, line); - result.push(...tokens); + banned_tokens.push(...tokens); } catch { console.log(`Could not tokenize raw text: ${line}`); } } } - return result.filter(onlyUnique).map(x => String(x)).join(','); + return { + banned_tokens: banned_tokens.filter(onlyUnique).map(x => String(x)).join(','), + banned_strings: banned_strings, + }; } /** @@ -697,10 +709,7 @@ jQuery(function () { $(`#${id}_counter_textgenerationwebui`).val(value); settings[id] = value; //special handling for vLLM/Aphrodite using -1 as disabled instead of 0 - if ($(this).attr('id') === 'top_k_textgenerationwebui' && - (settings.type === textgen_types.VLLM || - settings.type === textgen_types.APHRODITE) && - value === 0) { + if ($(this).attr('id') === 'top_k_textgenerationwebui' && [INFERMATICAI, APHRODITE, VLLM].includes(settings.type) && value === 0) { settings[id] = -1; $(this).val(-1); } @@ -987,6 +996,8 @@ export function isJsonSchemaSupported() { export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate, isContinue, cfgValues, type) { const canMultiSwipe = !isContinue && !isImpersonate && type !== 'quiet'; + const { banned_tokens, banned_strings } = getCustomTokenBans(); + let params = { 'prompt': finalPrompt, 'model': getTextGenModel(), @@ -1033,8 +1044,9 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate, 'mirostat_tau': settings.mirostat_tau, 'mirostat_eta': settings.mirostat_eta, 'custom_token_bans': [APHRODITE, MANCER].includes(settings.type) ? - toIntArray(getCustomTokenBans()) : - getCustomTokenBans(), + toIntArray(banned_tokens) : + banned_tokens, + 'banned_strings': banned_strings, 'api_type': settings.type, 'api_server': getTextGenServer(), 'legacy_api': settings.legacy_api && (settings.type === OOBA || settings.type === APHRODITE), diff --git a/public/scripts/utils.js b/public/scripts/utils.js index 33b51ee42..2a5ab9752 100644 --- a/public/scripts/utils.js +++ b/public/scripts/utils.js @@ -612,8 +612,8 @@ const dateCache = new Map(); /** * Cached version of moment() to avoid re-parsing the same date strings. * Important: Moment objects are mutable, so use clone() before modifying them! - * @param {any} timestamp String or number representing a date. - * @returns {*} Moment object + * @param {string|number} timestamp String or number representing a date. + * @returns {moment.Moment} Moment object */ export function timestampToMoment(timestamp) { if (dateCache.has(timestamp)) { @@ -663,8 +663,8 @@ function parseTimestamp(timestamp) { /** * Compare two moment objects for sorting. - * @param {*} a The first moment object. - * @param {*} b The second moment object. + * @param {moment.Moment} a The first moment object. + * @param {moment.Moment} b The second moment object. * @returns {number} A negative number if a is before b, a positive number if a is after b, or 0 if they are equal. */ export function sortMoments(a, b) { diff --git a/public/style.css b/public/style.css index 7cd1f5ddf..7ac30cfdf 100644 --- a/public/style.css +++ b/public/style.css @@ -813,6 +813,10 @@ body .panelControlBar { position: relative; } +#chat .mes.selected{ + background-color: rgb(from var(--SmartThemeQuoteColor) r g b / .5); +} + .mes q:before, .mes q:after { content: ''; diff --git a/src/endpoints/content-manager.js b/src/endpoints/content-manager.js index d5aec6d17..a2a81615e 100644 --- a/src/endpoints/content-manager.js +++ b/src/endpoints/content-manager.js @@ -3,7 +3,7 @@ const path = require('path'); const express = require('express'); const fetch = require('node-fetch').default; const sanitize = require('sanitize-filename'); -const { getConfigValue } = require('../util'); +const { getConfigValue, color } = require('../util'); const { jsonParser } = require('../express-common'); const writeFileAtomicSync = require('write-file-atomic').sync; const contentDirectory = path.join(process.cwd(), 'default/content'); @@ -94,8 +94,11 @@ function getDefaultPresetFile(filename) { * @param {ContentItem[]} contentIndex Content index * @param {import('../users').UserDirectoryList} directories User directories * @param {string[]} forceCategories List of categories to force check (even if content check is skipped) + * @returns {Promise} Whether any content was added */ async function seedContentForUser(contentIndex, directories, forceCategories) { + let anyContentAdded = false; + if (!fs.existsSync(directories.root)) { fs.mkdirSync(directories.root, { recursive: true }); } @@ -134,9 +137,11 @@ async function seedContentForUser(contentIndex, directories, forceCategories) { fs.cpSync(contentPath, targetPath, { recursive: true, force: false }); console.log(`Content file ${contentItem.filename} copied to ${contentTarget}`); + anyContentAdded = true; } writeFileAtomicSync(contentLogPath, contentLog.join('\n')); + return anyContentAdded; } /** @@ -147,15 +152,27 @@ async function seedContentForUser(contentIndex, directories, forceCategories) { */ async function checkForNewContent(directoriesList, forceCategories = []) { try { - if (getConfigValue('skipContentCheck', false) && forceCategories?.length === 0) { + const contentCheckSkip = getConfigValue('skipContentCheck', false); + if (contentCheckSkip && forceCategories?.length === 0) { return; } const contentIndexText = fs.readFileSync(contentIndexPath, 'utf8'); const contentIndex = JSON.parse(contentIndexText); + let anyContentAdded = false; for (const directories of directoriesList) { - await seedContentForUser(contentIndex, directories, forceCategories); + const seedResult = await seedContentForUser(contentIndex, directories, forceCategories); + + if (seedResult) { + anyContentAdded = true; + } + } + + if (anyContentAdded && !contentCheckSkip && forceCategories?.length === 0) { + console.log(); + console.log(`${color.blue('If you don\'t want to receive content updates in the future, set')} ${color.yellow('skipContentCheck')} ${color.blue('to true in the config.yaml file.')}`); + console.log(); } } catch (err) { console.log('Content check failed', err);