mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-29 22:01:47 +02:00
OAuth 2 (#158)
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
@apply w-full block text-base;
|
||||
|
||||
/* Regular cursor */
|
||||
|
||||
.cm-cursor {
|
||||
@apply border-text !important;
|
||||
/* Widen the cursor a bit */
|
||||
@@ -12,6 +13,7 @@
|
||||
}
|
||||
|
||||
/* Vim-mode cursor */
|
||||
|
||||
.cm-fat-cursor {
|
||||
@apply outline-0 bg-text !important;
|
||||
@apply text-surface !important;
|
||||
@@ -181,7 +183,7 @@
|
||||
@apply hidden !important;
|
||||
}
|
||||
|
||||
&.cm-singleline .cm-line {
|
||||
&.cm-singleline * {
|
||||
@apply cursor-default;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defaultKeymap, historyField } from '@codemirror/commands';
|
||||
import { defaultKeymap, historyField, indentWithTab } from '@codemirror/commands';
|
||||
import { foldState, forceParsing } from '@codemirror/language';
|
||||
import type { EditorStateConfig, Extension } from '@codemirror/state';
|
||||
import { Compartment, EditorState } from '@codemirror/state';
|
||||
@@ -34,14 +34,22 @@ import { TemplateVariableDialog } from '../../TemplateVariableDialog';
|
||||
import { IconButton } from '../IconButton';
|
||||
import { HStack } from '../Stacks';
|
||||
import './Editor.css';
|
||||
import { baseExtensions, getLanguageExtension, multiLineExtensions } from './extensions';
|
||||
import {
|
||||
baseExtensions,
|
||||
getLanguageExtension,
|
||||
multiLineExtensions,
|
||||
readonlyExtensions,
|
||||
} from './extensions';
|
||||
import type { GenericCompletionConfig } from './genericCompletion';
|
||||
import { singleLineExtensions } from './singleLine';
|
||||
|
||||
// VSCode's Tab actions mess with the single-line editor tab actions, so remove it.
|
||||
const vsCodeWithoutTab = vscodeKeymap.filter((k) => k.key !== 'Tab');
|
||||
|
||||
const keymapExtensions: Record<EditorKeymap, Extension> = {
|
||||
vim: vim(),
|
||||
emacs: emacs(),
|
||||
vscode: keymap.of(vscodeKeymap),
|
||||
vscode: keymap.of(vsCodeWithoutTab),
|
||||
default: [],
|
||||
};
|
||||
|
||||
@@ -68,6 +76,7 @@ export interface EditorProps {
|
||||
onKeyDown?: (e: KeyboardEvent) => void;
|
||||
singleLine?: boolean;
|
||||
wrapLines?: boolean;
|
||||
disableTabIndent?: boolean;
|
||||
format?: (v: string) => Promise<string>;
|
||||
autocomplete?: GenericCompletionConfig;
|
||||
autocompleteVariables?: boolean;
|
||||
@@ -85,9 +94,9 @@ const emptyExtension: Extension = [];
|
||||
export const Editor = forwardRef<EditorView | undefined, EditorProps>(function Editor(
|
||||
{
|
||||
readOnly,
|
||||
type = 'text',
|
||||
type,
|
||||
heightMode,
|
||||
language = 'text',
|
||||
language,
|
||||
autoFocus,
|
||||
autoSelect,
|
||||
placeholder,
|
||||
@@ -101,6 +110,7 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
onBlur,
|
||||
onKeyDown,
|
||||
className,
|
||||
disabled,
|
||||
singleLine,
|
||||
format,
|
||||
autocomplete,
|
||||
@@ -108,6 +118,7 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
autocompleteVariables,
|
||||
actions,
|
||||
wrapLines,
|
||||
disableTabIndent,
|
||||
hideGutter,
|
||||
stateKey,
|
||||
}: EditorProps,
|
||||
@@ -122,6 +133,20 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
wrapLines = settings.editorSoftWrap;
|
||||
}
|
||||
|
||||
if (disabled) {
|
||||
readOnly = true;
|
||||
}
|
||||
|
||||
if (
|
||||
singleLine ||
|
||||
language == null ||
|
||||
language === 'text' ||
|
||||
language === 'url' ||
|
||||
language === 'pairs'
|
||||
) {
|
||||
disableTabIndent = true;
|
||||
}
|
||||
|
||||
const cm = useRef<{ view: EditorView; languageCompartment: Compartment } | null>(null);
|
||||
useImperativeHandle(ref, () => cm.current?.view, []);
|
||||
|
||||
@@ -166,7 +191,7 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
useEffect(
|
||||
function configurePlaceholder() {
|
||||
if (cm.current === null) return;
|
||||
const ext = placeholderExt(placeholderElFromText(placeholder ?? '', type));
|
||||
const ext = placeholderExt(placeholderElFromText(placeholder, type));
|
||||
const effects = placeholderCompartment.current.reconfigure(ext);
|
||||
cm.current?.view.dispatch({ effects });
|
||||
},
|
||||
@@ -209,6 +234,23 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
[wrapLines],
|
||||
);
|
||||
|
||||
// Update tab indent
|
||||
const tabIndentCompartment = useRef(new Compartment());
|
||||
useEffect(
|
||||
function configureTabIndent() {
|
||||
if (cm.current === null) return;
|
||||
const current = tabIndentCompartment.current.get(cm.current.view.state) ?? emptyExtension;
|
||||
// PERF: This is expensive with hundreds of editors on screen, so only do it when necessary
|
||||
if (disableTabIndent && current !== emptyExtension) return; // Nothing to do
|
||||
if (!disableTabIndent && current === emptyExtension) return; // Nothing to do
|
||||
|
||||
const ext = !disableTabIndent ? keymap.of([indentWithTab]) : emptyExtension;
|
||||
const effects = tabIndentCompartment.current.reconfigure(ext);
|
||||
cm.current?.view.dispatch({ effects });
|
||||
},
|
||||
[disableTabIndent],
|
||||
);
|
||||
|
||||
const onClickFunction = useCallback(
|
||||
async (fn: TemplateFunction, tagValue: string, startPos: number) => {
|
||||
const initialTokens = await parseTemplate(tagValue);
|
||||
@@ -342,9 +384,12 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
const extensions = [
|
||||
languageCompartment.of(langExt),
|
||||
placeholderCompartment.current.of(
|
||||
placeholderExt(placeholderElFromText(placeholder ?? '', type)),
|
||||
placeholderExt(placeholderElFromText(placeholder, type)),
|
||||
),
|
||||
wrapLinesCompartment.current.of(wrapLines ? EditorView.lineWrapping : emptyExtension),
|
||||
tabIndentCompartment.current.of(
|
||||
!disableTabIndent ? keymap.of([indentWithTab]) : emptyExtension,
|
||||
),
|
||||
keymapCompartment.current.of(
|
||||
keymapExtensions[settings.editorKeymap] ?? keymapExtensions['default'],
|
||||
),
|
||||
@@ -475,6 +520,7 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
className={classNames(
|
||||
className,
|
||||
'cm-wrapper text-base',
|
||||
disabled && 'opacity-disabled',
|
||||
type === 'password' && 'cm-obscure-text',
|
||||
heightMode === 'auto' ? 'cm-auto-height' : 'cm-full-height',
|
||||
singleLine ? 'cm-singleline' : 'cm-multiline',
|
||||
@@ -557,10 +603,8 @@ function getExtensions({
|
||||
tooltips({ parent }),
|
||||
keymap.of(singleLine ? defaultKeymap.filter((k) => k.key !== 'Enter') : defaultKeymap),
|
||||
...(singleLine ? [singleLineExtensions()] : []),
|
||||
...(!singleLine ? [multiLineExtensions({ hideGutter })] : []),
|
||||
...(readOnly
|
||||
? [EditorState.readOnly.of(true), EditorView.contentAttributes.of({ tabindex: '-1' })]
|
||||
: []),
|
||||
...(!singleLine ? multiLineExtensions({ hideGutter }) : []),
|
||||
...(readOnly ? readonlyExtensions : []),
|
||||
|
||||
// ------------------------ //
|
||||
// Things that must be last //
|
||||
@@ -580,13 +624,15 @@ function getExtensions({
|
||||
];
|
||||
}
|
||||
|
||||
const placeholderElFromText = (text: string, type: EditorProps['type']) => {
|
||||
const placeholderElFromText = (text: string | undefined, type: EditorProps['type']) => {
|
||||
const el = document.createElement('div');
|
||||
if (type === 'password') {
|
||||
// Will be obscured (dots) so just needs to be something to take up space
|
||||
el.innerHTML = 'something-cool';
|
||||
el.setAttribute('aria-hidden', 'true');
|
||||
} else {
|
||||
// Default to <SPACE> because codemirror needs it for sizing. I'm not sure why, but probably something
|
||||
// to do with how Yaak "hacks" it with CSS for single line input.
|
||||
el.innerHTML = text ? text.replaceAll('\n', '<br/>') : ' ';
|
||||
}
|
||||
return el;
|
||||
@@ -596,8 +642,8 @@ function saveCachedEditorState(stateKey: string | null, state: EditorState | nul
|
||||
if (!stateKey || state == null) return;
|
||||
const stateObj = state.toJSON(stateFields);
|
||||
|
||||
// Save state in sessionStorage by removing doc and saving the hash of it instead
|
||||
// This will be checked on restore and put back in if it matches
|
||||
// Save state in sessionStorage by removing doc and saving the hash of it instead.
|
||||
// This will be checked on restore and put back in if it matches.
|
||||
stateObj.docHash = md5(stateObj.doc);
|
||||
delete stateObj.doc;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
closeBracketsKeymap,
|
||||
completionKeymap,
|
||||
} from '@codemirror/autocomplete';
|
||||
import { history, historyKeymap, indentWithTab } from '@codemirror/commands';
|
||||
import { history, historyKeymap } from '@codemirror/commands';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
import { json } from '@codemirror/lang-json';
|
||||
import { markdown } from '@codemirror/lang-markdown';
|
||||
@@ -142,6 +142,11 @@ export const baseExtensions = [
|
||||
keymap.of([...historyKeymap, ...completionKeymap]),
|
||||
];
|
||||
|
||||
export const readonlyExtensions = [
|
||||
EditorState.readOnly.of(true),
|
||||
EditorView.contentAttributes.of({ tabindex: '-1' }),
|
||||
];
|
||||
|
||||
export const multiLineExtensions = ({ hideGutter }: { hideGutter?: boolean }) => [
|
||||
hideGutter
|
||||
? []
|
||||
@@ -208,5 +213,5 @@ export const multiLineExtensions = ({ hideGutter }: { hideGutter?: boolean }) =>
|
||||
rectangularSelection(),
|
||||
crosshairCursor(),
|
||||
highlightActiveLineGutter(),
|
||||
keymap.of([indentWithTab, ...closeBracketsKeymap, ...searchKeymap, ...foldKeymap, ...lintKeymap]),
|
||||
keymap.of([...closeBracketsKeymap, ...searchKeymap, ...foldKeymap, ...lintKeymap]),
|
||||
];
|
||||
|
||||
@@ -1,16 +1,5 @@
|
||||
import type { CompletionContext } from '@codemirror/autocomplete';
|
||||
|
||||
export interface GenericCompletionOption {
|
||||
label: string;
|
||||
type: 'constant' | 'variable';
|
||||
detail?: string;
|
||||
info?: string;
|
||||
/** When given, should be a number from -99 to 99 that adjusts
|
||||
* how this completion is ranked compared to other completions
|
||||
* that match the input as well as this one. A negative number
|
||||
* moves it down the list, a positive number moves it up. */
|
||||
boost?: number;
|
||||
}
|
||||
import type { GenericCompletionOption } from '@yaakapp-internal/plugins';
|
||||
|
||||
export interface GenericCompletionConfig {
|
||||
minMatch?: number;
|
||||
|
||||
Reference in New Issue
Block a user