mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 15:06:20 +00:00
Align wired chat limits and formatting help UI
This commit is contained in:
@@ -40,11 +40,53 @@ const encodeHTML = (str: string) =>
|
||||
});
|
||||
};
|
||||
|
||||
const formatTag = (content: string, tag: string, replacement: (value: string) => string) =>
|
||||
{
|
||||
const pattern = new RegExp(`\\[${ tag }\\]([\\s\\S]*?)\\[\\/${ tag }\\]`, 'gi');
|
||||
let previous = '';
|
||||
let next = content;
|
||||
let guard = 0;
|
||||
|
||||
while((previous !== next) && (guard < 20))
|
||||
{
|
||||
previous = next;
|
||||
next = next.replace(pattern, (match, value) => replacement(value));
|
||||
guard++;
|
||||
}
|
||||
|
||||
return next;
|
||||
};
|
||||
|
||||
const applyWiredTextMarkup = (content: string) =>
|
||||
{
|
||||
const colorStyles: Record<string, string> = {
|
||||
green: '#008000',
|
||||
cyan: '#008b8b',
|
||||
red: '#d60000',
|
||||
blue: '#005dff',
|
||||
purple: '#7d31b8'
|
||||
};
|
||||
|
||||
let result = content;
|
||||
|
||||
result = formatTag(result, 'b', value => `<strong>${ value }</strong>`);
|
||||
result = formatTag(result, 'i', value => `<em>${ value }</em>`);
|
||||
result = formatTag(result, 'u', value => `<u>${ value }</u>`);
|
||||
|
||||
Object.entries(colorStyles).forEach(([ tag, color ]) =>
|
||||
{
|
||||
result = formatTag(result, tag, value => `<span style="color:${ color }">${ value }</span>`);
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const RoomChatFormatter = (content: string) =>
|
||||
{
|
||||
let result = '';
|
||||
|
||||
content = encodeHTML(content);
|
||||
content = applyWiredTextMarkup(content);
|
||||
//content = (joypixels.shortnameToUnicode(content) as string)
|
||||
|
||||
if(!GetConfigurationValue<boolean>('youtube.publish.disabled', false))
|
||||
@@ -84,5 +126,5 @@ export const RoomChatFormatter = (content: string) =>
|
||||
result = content;
|
||||
}
|
||||
|
||||
return result;
|
||||
return result.replace(/\r\n|\r|\n/g, '<br />');
|
||||
};
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { GetConfigurationValue, LocalizeText, WIRED_STRING_DELIMETER, WiredFurniType } from '../../../../api';
|
||||
import { LocalizeText, WIRED_STRING_DELIMETER, WiredFurniType } from '../../../../api';
|
||||
import { Text } from '../../../../common';
|
||||
import { useWired } from '../../../../hooks';
|
||||
import { NitroInput } from '../../../../layout';
|
||||
import { WiredActionBaseView } from './WiredActionBaseView';
|
||||
import { WiredTextCounter, WiredTextFormattingHelp } from '../common/WiredTextFormattingHelp';
|
||||
import { BOT_SOURCES, WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||
|
||||
const normalizeBotSource = (value: number, hasBotName = false) => (BOT_SOURCES.some(option => (option.value === value)) ? value : (hasBotName ? 100 : 0));
|
||||
@@ -15,6 +16,7 @@ export const WiredActionBotTalkToAvatarView: FC<{}> = props =>
|
||||
const [ talkMode, setTalkMode ] = useState(-1);
|
||||
const [ botSource, setBotSource ] = useState<number>(100);
|
||||
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
|
||||
const maxMessageLength = 100;
|
||||
const [ userSource, setUserSource ] = useState<number>(() =>
|
||||
{
|
||||
if(trigger?.intData?.length > 1) return trigger.intData[1];
|
||||
@@ -59,7 +61,14 @@ export const WiredActionBotTalkToAvatarView: FC<{}> = props =>
|
||||
</div> }
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text bold>{ LocalizeText('wiredfurni.params.message') }</Text>
|
||||
<NitroInput maxLength={ GetConfigurationValue<number>('wired.action.bot.talk.to.avatar.max.length', 64) } type="text" value={ message } onChange={ event => setMessage(event.target.value) } />
|
||||
<textarea
|
||||
className="form-control form-control-sm nitro-wired__resizable-textarea"
|
||||
maxLength={ maxMessageLength }
|
||||
rows={ 4 }
|
||||
value={ message }
|
||||
onChange={ event => setMessage(event.target.value) } />
|
||||
<WiredTextCounter maxLength={ maxMessageLength } value={ message } />
|
||||
<WiredTextFormattingHelp />
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex items-center gap-1">
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { GetConfigurationValue, LocalizeText, WIRED_STRING_DELIMETER, WiredFurniType } from '../../../../api';
|
||||
import { LocalizeText, WIRED_STRING_DELIMETER, WiredFurniType } from '../../../../api';
|
||||
import { Text } from '../../../../common';
|
||||
import { useWired } from '../../../../hooks';
|
||||
import { NitroInput } from '../../../../layout';
|
||||
import { WiredActionBaseView } from './WiredActionBaseView';
|
||||
import { WiredTextCounter, WiredTextFormattingHelp } from '../common/WiredTextFormattingHelp';
|
||||
import { BOT_SOURCES, WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||
|
||||
const normalizeBotSource = (value: number, hasBotName = false) => (BOT_SOURCES.some(option => (option.value === value)) ? value : (hasBotName ? 100 : 0));
|
||||
@@ -15,6 +16,7 @@ export const WiredActionBotTalkView: FC<{}> = props =>
|
||||
const [ talkMode, setTalkMode ] = useState(-1);
|
||||
const [ botSource, setBotSource ] = useState<number>(100);
|
||||
const { trigger = null, setStringParam = null, setIntParams = null } = useWired();
|
||||
const maxMessageLength = 100;
|
||||
|
||||
const save = () =>
|
||||
{
|
||||
@@ -43,7 +45,14 @@ export const WiredActionBotTalkView: FC<{}> = props =>
|
||||
</div> }
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text bold>{ LocalizeText('wiredfurni.params.message') }</Text>
|
||||
<NitroInput maxLength={ GetConfigurationValue<number>('wired.action.bot.talk.max.length', 64) } type="text" value={ message } onChange={ event => setMessage(event.target.value) } />
|
||||
<textarea
|
||||
className="form-control form-control-sm nitro-wired__resizable-textarea"
|
||||
maxLength={ maxMessageLength }
|
||||
rows={ 4 }
|
||||
value={ message }
|
||||
onChange={ event => setMessage(event.target.value) } />
|
||||
<WiredTextCounter maxLength={ maxMessageLength } value={ message } />
|
||||
<WiredTextFormattingHelp />
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex items-center gap-1">
|
||||
|
||||
@@ -1,13 +1,24 @@
|
||||
import { FC, useEffect, useMemo, useState } from 'react';
|
||||
import { GetConfigurationValue, LocalizeText, WiredFurniType } from '../../../../api';
|
||||
import { LocalizeText, WiredFurniType } from '../../../../api';
|
||||
import { Text } from '../../../../common';
|
||||
import { useWired } from '../../../../hooks';
|
||||
import { NitroInput } from '../../../../layout';
|
||||
import { WiredActionBaseView } from './WiredActionBaseView';
|
||||
import { WiredTextCounter, WiredTextFormattingHelp } from '../common/WiredTextFormattingHelp';
|
||||
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||
|
||||
const SHOW_MESSAGE_STYLE_IDS = [ 34, 200, 201, 202, 210, 211, 212, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 250, 251, 252 ];
|
||||
const DEFAULT_SHOW_MESSAGE_STYLE_ID = 34;
|
||||
const SHOW_MESSAGE_MAX_LENGTH = 200;
|
||||
const SHOW_MESSAGE_MAX_LINES = 8;
|
||||
|
||||
const clampShowMessage = (value: string) =>
|
||||
{
|
||||
const normalized = (value ?? '').replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
||||
const lines = normalized.split('\n').slice(0, SHOW_MESSAGE_MAX_LINES);
|
||||
const joined = lines.join('\n');
|
||||
|
||||
return joined.slice(0, SHOW_MESSAGE_MAX_LENGTH);
|
||||
};
|
||||
|
||||
export const WiredActionChatView: FC<{}> = props =>
|
||||
{
|
||||
@@ -21,16 +32,17 @@ export const WiredActionChatView: FC<{}> = props =>
|
||||
return 0;
|
||||
});
|
||||
const bubbleStyleIds = useMemo(() => SHOW_MESSAGE_STYLE_IDS, []);
|
||||
const maxMessageLength = SHOW_MESSAGE_MAX_LENGTH;
|
||||
|
||||
const save = () =>
|
||||
{
|
||||
setStringParam(message);
|
||||
setStringParam(clampShowMessage(message));
|
||||
setIntParams([ userSource, visibilitySelection, bubbleStyle ]);
|
||||
};
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
setMessage(trigger.stringData);
|
||||
setMessage(clampShowMessage(trigger.stringData));
|
||||
if(trigger.intData.length >= 1) setUserSource(trigger.intData[0]);
|
||||
else setUserSource(0);
|
||||
if(trigger.intData.length >= 2) setVisibilitySelection(trigger.intData[1]);
|
||||
@@ -47,7 +59,14 @@ export const WiredActionChatView: FC<{}> = props =>
|
||||
footer={ <WiredSourcesSelector showUsers={ true } userSource={ userSource } onChangeUsers={ setUserSource } /> }>
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text bold>{ LocalizeText('wiredfurni.params.message') }</Text>
|
||||
<NitroInput maxLength={ GetConfigurationValue<number>('wired.action.chat.max.length', 100) } type="text" value={ message } onChange={ event => setMessage(event.target.value) } />
|
||||
<textarea
|
||||
className="form-control form-control-sm nitro-wired__resizable-textarea"
|
||||
maxLength={ maxMessageLength }
|
||||
rows={ 4 }
|
||||
value={ message }
|
||||
onChange={ event => setMessage(clampShowMessage(event.target.value)) } />
|
||||
<WiredTextCounter maxLength={ maxMessageLength } value={ message } />
|
||||
<WiredTextFormattingHelp />
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text bold>{ LocalizeText('wiredfurni.params.show_message.visibility_selection.title') }</Text>
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { FC, useState } from 'react';
|
||||
import { Text } from '../../../../common';
|
||||
|
||||
export const WIRED_TEXT_MESSAGE_MAX_LENGTH = 512;
|
||||
|
||||
export const getWiredTextLineCount = (value: string) =>
|
||||
{
|
||||
if(!value.length) return 0;
|
||||
|
||||
return value.replace(/\r/g, '').split('\n').length;
|
||||
};
|
||||
|
||||
interface WiredTextCounterProps
|
||||
{
|
||||
value: string;
|
||||
maxLength?: number;
|
||||
}
|
||||
|
||||
export const WiredTextCounter: FC<WiredTextCounterProps> = props =>
|
||||
{
|
||||
const { value = '', maxLength = WIRED_TEXT_MESSAGE_MAX_LENGTH } = props;
|
||||
|
||||
return <Text small>{ `${ getWiredTextLineCount(value) } righe - ${ value.length }/${ maxLength } caratteri` }</Text>;
|
||||
};
|
||||
|
||||
export const WiredTextFormattingHelp: FC<{}> = () =>
|
||||
{
|
||||
const [ isOpen, setIsOpen ] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-1 rounded bg-black/5 p-1 text-[11px] leading-[14px]">
|
||||
<button className="w-full text-left" type="button" onClick={ () => setIsOpen(value => !value) }>
|
||||
<Text small bold>{ isOpen ? 'Hide format examples' : 'Show format examples' }</Text>
|
||||
</button>
|
||||
{ isOpen &&
|
||||
<>
|
||||
<Text small>
|
||||
<span className="font-bold">[b]Example Bold[/b]</span>
|
||||
<span> - </span>
|
||||
<span className="italic">[i]Example Italic[/i]</span>
|
||||
<span> - </span>
|
||||
<span className="underline">[u]Example Underline[/u]</span>
|
||||
</Text>
|
||||
<Text small>
|
||||
<span style={ { color: '#008000' } }>[green]Example Green[/green]</span>
|
||||
<span> - </span>
|
||||
<span style={ { color: '#008b8b' } }>[cyan]Example Cyan[/cyan]</span>
|
||||
<span> - </span>
|
||||
<span style={ { color: '#d60000' } }>[red]Example Red[/red]</span>
|
||||
<span> - </span>
|
||||
<span style={ { color: '#005dff' } }>[blue]Example Blue[/blue]</span>
|
||||
<span> - </span>
|
||||
<span style={ { color: '#7d31b8' } }>[purple]Example Purple[/purple]</span>
|
||||
</Text>
|
||||
</> }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user